Advertisement
Willcode4cash

Porter Stemmer

Aug 7th, 2017
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 12.83 KB | None | 0 0
  1. // https://svn.apache.org/repos/asf/lucene/lucene.net/tags/Lucene.Net_2_9_1/src/Lucene.Net/Analysis/PorterStemmer.cs
  2. public class PorterStemmer
  3. {
  4.     private char[] b;
  5.     private int i, j, k, k0;
  6.     private bool dirty = false;
  7.     private const int INC = 50; /* unit of size whereby b is increased */
  8.     private const int EXTRA = 1;
  9.  
  10.     public PorterStemmer()
  11.     {
  12.         b = new char[INC];
  13.         i = 0;
  14.     }
  15.  
  16.     /// <summary> reset() resets the stemmer so it can stem another word.  If you invoke
  17.     /// the stemmer by calling add(char) and then stem(), you must call reset()
  18.     /// before starting another word.
  19.     /// </summary>
  20.     public virtual void Reset()
  21.     {
  22.         i = 0; dirty = false;
  23.     }
  24.  
  25.     /// <summary> Add a character to the word being stemmed.  When you are finished
  26.     /// adding characters, you can call stem(void) to process the word.
  27.     /// </summary>
  28.     public virtual void Add(char ch)
  29.     {
  30.         if (b.Length <= i + EXTRA)
  31.         {
  32.             char[] new_b = new char[b.Length + INC];
  33.             Array.Copy(b, 0, new_b, 0, b.Length);
  34.             b = new_b;
  35.         }
  36.         b[i++] = ch;
  37.     }
  38.  
  39.     /// <summary> After a word has been stemmed, it can be retrieved by toString(),
  40.     /// or a reference to the internal buffer can be retrieved by getResultBuffer
  41.     /// and getResultLength (which is generally more efficient.)
  42.     /// </summary>
  43.     public override System.String ToString()
  44.     {
  45.         return new System.String(b, 0, i);
  46.     }
  47.  
  48.     /// <summary> Returns the length of the word resulting from the stemming process.</summary>
  49.     public virtual int GetResultLength()
  50.     {
  51.         return i;
  52.     }
  53.  
  54.     /// <summary> Returns a reference to a character buffer containing the results of
  55.     /// the stemming process.  You also need to consult getResultLength()
  56.     /// to determine the length of the result.
  57.     /// </summary>
  58.     public virtual char[] GetResultBuffer()
  59.     {
  60.         return b;
  61.     }
  62.  
  63.     private bool Cons(int i)
  64.     {
  65.         switch (b[i])
  66.         {
  67.  
  68.             case 'a':
  69.             case 'e':
  70.             case 'i':
  71.             case 'o':
  72.             case 'u':
  73.                 return false;
  74.  
  75.             case 'y':
  76.                 return (i == k0) ? true : !Cons(i - 1);
  77.  
  78.             default:
  79.                 return true;
  80.  
  81.         }
  82.     }
  83.  
  84.     /* m() measures the number of consonant sequences between k0 and j. if c is
  85.     a consonant sequence and v a vowel sequence, and <..> indicates arbitrary
  86.     presence,
  87.  
  88.     <c><v>       gives 0
  89.     <c>vc<v>     gives 1
  90.     <c>vcvc<v>   gives 2
  91.     <c>vcvcvc<v> gives 3
  92.     ....
  93.     */
  94.  
  95.     private int M()
  96.     {
  97.         int n = 0;
  98.         int i = k0;
  99.         while (true)
  100.         {
  101.             if (i > j)
  102.                 return n;
  103.             if (!Cons(i))
  104.                 break;
  105.             i++;
  106.         }
  107.         i++;
  108.         while (true)
  109.         {
  110.             while (true)
  111.             {
  112.                 if (i > j)
  113.                     return n;
  114.                 if (Cons(i))
  115.                     break;
  116.                 i++;
  117.             }
  118.             i++;
  119.             n++;
  120.             while (true)
  121.             {
  122.                 if (i > j)
  123.                     return n;
  124.                 if (!Cons(i))
  125.                     break;
  126.                 i++;
  127.             }
  128.             i++;
  129.         }
  130.     }
  131.  
  132.     /* vowelinstem() is true <=> k0,...j contains a vowel */
  133.  
  134.     private bool Vowelinstem()
  135.     {
  136.         int i;
  137.         for (i = k0; i <= j; i++)
  138.             if (!Cons(i))
  139.                 return true;
  140.         return false;
  141.     }
  142.  
  143.     /* doublec(j) is true <=> j,(j-1) contain a double consonant. */
  144.  
  145.     private bool Doublec(int j)
  146.     {
  147.         if (j < k0 + 1)
  148.             return false;
  149.         if (b[j] != b[j - 1])
  150.             return false;
  151.         return Cons(j);
  152.     }
  153.  
  154.     /* cvc(i) is true <=> i-2,i-1,i has the form consonant - vowel - consonant
  155.     and also if the second c is not w,x or y. this is used when trying to
  156.     restore an e at the end of a short word. e.g.
  157.  
  158.     cav(e), lov(e), hop(e), crim(e), but
  159.     snow, box, tray.
  160.  
  161.     */
  162.  
  163.     private bool Cvc(int i)
  164.     {
  165.         if (i < k0 + 2 || !Cons(i) || Cons(i - 1) || !Cons(i - 2))
  166.             return false;
  167.         else
  168.         {
  169.             int ch = b[i];
  170.             if (ch == 'w' || ch == 'x' || ch == 'y')
  171.                 return false;
  172.         }
  173.         return true;
  174.     }
  175.  
  176.     private bool Ends(System.String s)
  177.     {
  178.         int l = s.Length;
  179.         int o = k - l + 1;
  180.         if (o < k0)
  181.             return false;
  182.         for (int i = 0; i < l; i++)
  183.             if (b[o + i] != s[i])
  184.                 return false;
  185.         j = k - l;
  186.         return true;
  187.     }
  188.  
  189.     /* setto(s) sets (j+1),...k to the characters in the string s, readjusting
  190.     k. */
  191.  
  192.     internal virtual void Setto(System.String s)
  193.     {
  194.         int l = s.Length;
  195.         int o = j + 1;
  196.         for (int i = 0; i < l; i++)
  197.             b[o + i] = s[i];
  198.         k = j + l;
  199.         dirty = true;
  200.     }
  201.  
  202.     /* r(s) is used further down. */
  203.  
  204.     internal virtual void R(System.String s)
  205.     {
  206.         if (M() > 0)
  207.             Setto(s);
  208.     }
  209.  
  210.     /* step1() gets rid of plurals and -ed or -ing. e.g.
  211.  
  212.     caresses  ->  caress
  213.     ponies    ->  poni
  214.     ties      ->  ti
  215.     caress    ->  caress
  216.     cats      ->  cat
  217.  
  218.     feed      ->  feed
  219.     agreed    ->  agree
  220.     disabled  ->  disable
  221.  
  222.     matting   ->  mat
  223.     mating    ->  mate
  224.     meeting   ->  meet
  225.     milling   ->  mill
  226.     messing   ->  mess
  227.  
  228.     meetings  ->  meet
  229.  
  230.     */
  231.  
  232.     private void Step1()
  233.     {
  234.         if (b[k] == 's')
  235.         {
  236.             if (Ends("sses"))
  237.                 k -= 2;
  238.             else if (Ends("ies"))
  239.                 Setto("i");
  240.             else if (b[k - 1] != 's')
  241.                 k--;
  242.         }
  243.         if (Ends("eed"))
  244.         {
  245.             if (M() > 0)
  246.                 k--;
  247.         }
  248.         else if ((Ends("ed") || Ends("ing")) && Vowelinstem())
  249.         {
  250.             k = j;
  251.             if (Ends("at"))
  252.                 Setto("ate");
  253.             else if (Ends("bl"))
  254.                 Setto("ble");
  255.             else if (Ends("iz"))
  256.                 Setto("ize");
  257.             else if (Doublec(k))
  258.             {
  259.                 int ch = b[k--];
  260.                 if (ch == 'l' || ch == 's' || ch == 'z')
  261.                     k++;
  262.             }
  263.             else if (M() == 1 && Cvc(k))
  264.                 Setto("e");
  265.         }
  266.     }
  267.  
  268.     /* step2() turns terminal y to i when there is another vowel in the stem. */
  269.  
  270.     private void Step2()
  271.     {
  272.         if (Ends("y") && Vowelinstem())
  273.         {
  274.             b[k] = 'i';
  275.             dirty = true;
  276.         }
  277.     }
  278.  
  279.     /* step3() maps double suffices to single ones. so -ization ( = -ize plus
  280.     -ation) maps to -ize etc. note that the string before the suffix must give
  281.     m() > 0. */
  282.  
  283.     private void Step3()
  284.     {
  285.         if (k == k0)
  286.             return; /* For Bug 1 */
  287.         switch (b[k - 1])
  288.         {
  289.  
  290.             case 'a':
  291.                 if (Ends("ational"))
  292.                 {
  293.                     R("ate"); break;
  294.                 }
  295.                 if (Ends("tional"))
  296.                 {
  297.                     R("tion"); break;
  298.                 }
  299.                 break;
  300.  
  301.             case 'c':
  302.                 if (Ends("enci"))
  303.                 {
  304.                     R("ence"); break;
  305.                 }
  306.                 if (Ends("anci"))
  307.                 {
  308.                     R("ance"); break;
  309.                 }
  310.                 break;
  311.  
  312.             case 'e':
  313.                 if (Ends("izer"))
  314.                 {
  315.                     R("ize"); break;
  316.                 }
  317.                 break;
  318.  
  319.             case 'l':
  320.                 if (Ends("bli"))
  321.                 {
  322.                     R("ble"); break;
  323.                 }
  324.                 if (Ends("alli"))
  325.                 {
  326.                     R("al"); break;
  327.                 }
  328.                 if (Ends("entli"))
  329.                 {
  330.                     R("ent"); break;
  331.                 }
  332.                 if (Ends("eli"))
  333.                 {
  334.                     R("e"); break;
  335.                 }
  336.                 if (Ends("ousli"))
  337.                 {
  338.                     R("ous"); break;
  339.                 }
  340.                 break;
  341.  
  342.             case 'o':
  343.                 if (Ends("ization"))
  344.                 {
  345.                     R("ize"); break;
  346.                 }
  347.                 if (Ends("ation"))
  348.                 {
  349.                     R("ate"); break;
  350.                 }
  351.                 if (Ends("ator"))
  352.                 {
  353.                     R("ate"); break;
  354.                 }
  355.                 break;
  356.  
  357.             case 's':
  358.                 if (Ends("alism"))
  359.                 {
  360.                     R("al"); break;
  361.                 }
  362.                 if (Ends("iveness"))
  363.                 {
  364.                     R("ive"); break;
  365.                 }
  366.                 if (Ends("fulness"))
  367.                 {
  368.                     R("ful"); break;
  369.                 }
  370.                 if (Ends("ousness"))
  371.                 {
  372.                     R("ous"); break;
  373.                 }
  374.                 break;
  375.  
  376.             case 't':
  377.                 if (Ends("aliti"))
  378.                 {
  379.                     R("al"); break;
  380.                 }
  381.                 if (Ends("iviti"))
  382.                 {
  383.                     R("ive"); break;
  384.                 }
  385.                 if (Ends("biliti"))
  386.                 {
  387.                     R("ble"); break;
  388.                 }
  389.                 break;
  390.  
  391.             case 'g':
  392.                 if (Ends("logi"))
  393.                 {
  394.                     R("log"); break;
  395.                 }
  396.                 break;
  397.         }
  398.     }
  399.  
  400.     /* step4() deals with -ic-, -full, -ness etc. similar strategy to step3. */
  401.  
  402.     private void Step4()
  403.     {
  404.         switch (b[k])
  405.         {
  406.  
  407.             case 'e':
  408.                 if (Ends("icate"))
  409.                 {
  410.                     R("ic"); break;
  411.                 }
  412.                 if (Ends("ative"))
  413.                 {
  414.                     R(""); break;
  415.                 }
  416.                 if (Ends("alize"))
  417.                 {
  418.                     R("al"); break;
  419.                 }
  420.                 break;
  421.  
  422.             case 'i':
  423.                 if (Ends("iciti"))
  424.                 {
  425.                     R("ic"); break;
  426.                 }
  427.                 break;
  428.  
  429.             case 'l':
  430.                 if (Ends("ical"))
  431.                 {
  432.                     R("ic"); break;
  433.                 }
  434.                 if (Ends("ful"))
  435.                 {
  436.                     R(""); break;
  437.                 }
  438.                 break;
  439.  
  440.             case 's':
  441.                 if (Ends("ness"))
  442.                 {
  443.                     R(""); break;
  444.                 }
  445.                 break;
  446.         }
  447.     }
  448.  
  449.     /* step5() takes off -ant, -ence etc., in context <c>vcvc<v>. */
  450.  
  451.     private void Step5()
  452.     {
  453.         if (k == k0)
  454.             return; /* for Bug 1 */
  455.         switch (b[k - 1])
  456.         {
  457.  
  458.             case 'a':
  459.                 if (Ends("al"))
  460.                     break;
  461.                 return;
  462.  
  463.             case 'c':
  464.                 if (Ends("ance"))
  465.                     break;
  466.                 if (Ends("ence"))
  467.                     break;
  468.                 return;
  469.  
  470.             case 'e':
  471.                 if (Ends("er"))
  472.                     break; return;
  473.  
  474.             case 'i':
  475.                 if (Ends("ic"))
  476.                     break; return;
  477.  
  478.             case 'l':
  479.                 if (Ends("able"))
  480.                     break;
  481.                 if (Ends("ible"))
  482.                     break; return;
  483.  
  484.             case 'n':
  485.                 if (Ends("ant"))
  486.                     break;
  487.                 if (Ends("ement"))
  488.                     break;
  489.                 if (Ends("ment"))
  490.                     break;
  491.                 /* element etc. not stripped before the m */
  492.                 if (Ends("ent"))
  493.                     break;
  494.                 return;
  495.  
  496.             case 'o':
  497.                 if (Ends("ion") && j >= 0 && (b[j] == 's' || b[j] == 't'))
  498.                     break;
  499.                 /* j >= 0 fixes Bug 2 */
  500.                 if (Ends("ou"))
  501.                     break;
  502.                 return;
  503.             /* takes care of -ous */
  504.  
  505.             case 's':
  506.                 if (Ends("ism"))
  507.                     break;
  508.                 return;
  509.  
  510.             case 't':
  511.                 if (Ends("ate"))
  512.                     break;
  513.                 if (Ends("iti"))
  514.                     break;
  515.                 return;
  516.  
  517.             case 'u':
  518.                 if (Ends("ous"))
  519.                     break;
  520.                 return;
  521.  
  522.             case 'v':
  523.                 if (Ends("ive"))
  524.                     break;
  525.                 return;
  526.  
  527.             case 'z':
  528.                 if (Ends("ize"))
  529.                     break;
  530.                 return;
  531.  
  532.             default:
  533.                 return;
  534.  
  535.         }
  536.         if (M() > 1)
  537.             k = j;
  538.     }
  539.  
  540.     /* step6() removes a final -e if m() > 1. */
  541.  
  542.     private void Step6()
  543.     {
  544.         j = k;
  545.         if (b[k] == 'e')
  546.         {
  547.             int a = M();
  548.             if (a > 1 || a == 1 && !Cvc(k - 1))
  549.                 k--;
  550.         }
  551.         if (b[k] == 'l' && Doublec(k) && M() > 1)
  552.             k--;
  553.     }
  554.  
  555.  
  556.     /// <summary> Stem a word provided as a String.  Returns the result as a String.</summary>
  557.     public virtual System.String Stem(System.String s)
  558.     {
  559.         if (Stem(s.ToCharArray(), s.Length))
  560.         {
  561.             return ToString();
  562.         }
  563.         else
  564.             return s;
  565.     }
  566.  
  567.     /// <summary>Stem a word contained in a char[].  Returns true if the stemming process
  568.     /// resulted in a word different from the input.  You can retrieve the
  569.     /// result with getResultLength()/getResultBuffer() or toString().
  570.     /// </summary>
  571.     public virtual bool Stem(char[] word)
  572.     {
  573.         return Stem(word, word.Length);
  574.     }
  575.  
  576.     /// <summary>Stem a word contained in a portion of a char[] array.  Returns
  577.     /// true if the stemming process resulted in a word different from
  578.     /// the input.  You can retrieve the result with
  579.     /// getResultLength()/getResultBuffer() or toString().
  580.     /// </summary>
  581.     public virtual bool Stem(char[] wordBuffer, int offset, int wordLen)
  582.     {
  583.         Reset();
  584.         if (b.Length < wordLen)
  585.         {
  586.             char[] new_b = new char[wordLen + EXTRA];
  587.             b = new_b;
  588.         }
  589.         Array.Copy(wordBuffer, offset, b, 0, wordLen);
  590.         i = wordLen;
  591.         return Stem(0);
  592.     }
  593.  
  594.     /// <summary>Stem a word contained in a leading portion of a char[] array.
  595.     /// Returns true if the stemming process resulted in a word different
  596.     /// from the input.  You can retrieve the result with
  597.     /// getResultLength()/getResultBuffer() or toString().
  598.     /// </summary>
  599.     public virtual bool Stem(char[] word, int wordLen)
  600.     {
  601.         return Stem(word, 0, wordLen);
  602.     }
  603.  
  604.     /// <summary>Stem the word placed into the Stemmer buffer through calls to add().
  605.     /// Returns true if the stemming process resulted in a word different
  606.     /// from the input.  You can retrieve the result with
  607.     /// getResultLength()/getResultBuffer() or toString().
  608.     /// </summary>
  609.     public virtual bool Stem()
  610.     {
  611.         return Stem(0);
  612.     }
  613.  
  614.     public virtual bool Stem(int i0)
  615.     {
  616.         k = i - 1;
  617.         k0 = i0;
  618.         if (k > k0 + 1)
  619.         {
  620.             Step1(); Step2(); Step3(); Step4(); Step5(); Step6();
  621.         }
  622.         // Also, a word is considered dirty if we lopped off letters
  623.         // Thanks to Ifigenia Vairelles for pointing this out.
  624.         if (i != k + 1)
  625.             dirty = true;
  626.         i = k + 1;
  627.         return dirty;
  628.     }
  629.  
  630.     /// <summary>Test program for demonstrating the Stemmer.  It reads a file and
  631.     /// stems each word, writing the result to standard out.
  632.     /// Usage: Stemmer file-name
  633.     /// </summary>
  634.     [STAThread]
  635.     public static void Main(System.String[] args)
  636.     {
  637.         PorterStemmer s = new PorterStemmer();
  638.  
  639.         for (int i = 0; i < args.Length; i++)
  640.         {
  641.             try
  642.             {
  643.                 System.IO.Stream in_Renamed = new System.IO.FileStream(args[i], System.IO.FileMode.Open, System.IO.FileAccess.Read);
  644.                 byte[] buffer = new byte[1024];
  645.                 int bufferLen, offset, ch;
  646.  
  647.                 bufferLen = in_Renamed.Read(buffer, 0, buffer.Length);
  648.                 offset = 0;
  649.                 s.Reset();
  650.  
  651.                 while (true)
  652.                 {
  653.                     if (offset < bufferLen)
  654.                         ch = buffer[offset++];
  655.                     else
  656.                     {
  657.                         bufferLen = in_Renamed.Read(buffer, 0, buffer.Length);
  658.                         offset = 0;
  659.                         if (bufferLen < 0)
  660.                             ch = -1;
  661.                         else
  662.                             ch = buffer[offset++];
  663.                     }
  664.  
  665.                     if (System.Char.IsLetter((char)ch))
  666.                     {
  667.                         s.Add(System.Char.ToLower((char)ch));
  668.                     }
  669.                     else
  670.                     {
  671.                         s.Stem();
  672.                         System.Console.Out.Write(s.ToString());
  673.                         s.Reset();
  674.                         if (ch < 0)
  675.                             break;
  676.                         else
  677.                         {
  678.                             System.Console.Out.Write((char)ch);
  679.                         }
  680.                     }
  681.                 }
  682.  
  683.                 in_Renamed.Close();
  684.             }
  685.             catch (System.IO.IOException e)
  686.             {
  687.                 System.Console.Out.WriteLine("error reading " + args[i]);
  688.             }
  689.         }
  690.     }
  691. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement