PorterStemmer.cs
上传用户:huiyue
上传日期:2022-04-08
资源大小:1429k
文件大小:17k
源码类别:

搜索引擎

开发平台:

ASP/ASPX

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. namespace Searcharoo.Common
  5. {
  6.     #region Porter Stemmer comments
  7.     /*
  8. Porter stemmer in CSharp, based on the Java port. The original paper is in
  9. Porter, 1980, An algorithm for suffix stripping, Program, Vol. 14,
  10. no. 3, pp 130-137,
  11. See also http://www.tartarus.org/~martin/PorterStemmer
  12. History:
  13. Release 1
  14. Bug 1 (reported by Gonzalo Parra 16/10/99) fixed as marked below.
  15. The words 'aed', 'eed', 'oed' leave k at 'a' for step 3, and b[k-1]
  16. is then out outside the bounds of b.
  17. Release 2
  18. Similarly,
  19. Bug 2 (reported by Steve Dyrdahl 22/2/00) fixed as marked below.
  20. 'ion' by itself leaves j = -1 in the test for 'ion' in step 5, and
  21. b[j] is then outside the bounds of b.
  22. Release 3
  23. Considerably revised 4/9/00 in the light of many helpful suggestions
  24. from Brian Goetz of Quiotix Corporation (brian@quiotix.com).
  25. Release 4
  26. This revision allows the Porter Stemmer Algorithm to be exported via the
  27. .NET Framework. To facilate its use via .NET, the following commands need to be
  28. issued to the operating system to register the component so that it can be
  29. imported into .Net compatible languages, such as Delphi.NET, Visual Basic.NET,
  30. Visual C++.NET, etc. 
  31. 1. Create a stong name: 
  32. sn -k Keyfile.snk  
  33. 2. Compile the C# class, which creates an assembly PorterStemmerAlgorithm.dll
  34. csc /t:library PorterStemmerAlgorithm.cs
  35. 3. Register the dll with the Windows Registry 
  36. and so expose the interface to COM Clients via the type library 
  37. ( PorterStemmerAlgorithm.tlb will be created)
  38. regasm /tlb PorterStemmerAlgorithm.dll
  39. 4. Load the component in the Global Assembly Cache
  40. gacutil -i PorterStemmerAlgorithm.dll
  41. Note: You must have the .Net Studio installed.
  42. Once this process is performed you should be able to import the class 
  43. via the appropiate mechanism in the language that you are using.
  44. i.e in Delphi 7 .NET this is simply a matter of selecting: 
  45. Project | Import Type Libary
  46. And then selecting Porter stemmer in CSharp Version 1.4"!
  47. Cheers Leif
  48. */
  49.     #endregion
  50.     /// <summary>
  51.     /// By the original author:
  52.     /// Stemmer, implementing the Porter Stemming Algorithm
  53.     /// 
  54.     /// The Stemmer class transforms a word into its root form.  The input
  55.     /// word can be provided a character at time (by calling add()), or at once
  56.     /// by calling one of the various stem(something) methods.
  57.     /// </summary>
  58.     /// <remarks>
  59.     /// source, licence, etc for this class available here:
  60.     /// http://www.tartarus.org/martin/PorterStemmer/
  61.     /// http://www.tartarus.org/martin/PorterStemmer/csharp2.txt
  62.     /// </remarks>
  63.     public class PorterStemmer : IStemming
  64.     {
  65.         private char[] b;
  66.         private int i,     /* offset into b */
  67.             i_end, /* offset to end of stemmed word */
  68.             j, k;
  69.         private static int INC = 200;
  70.         /* unit of size whereby b is increased */
  71.         public PorterStemmer()
  72.         {
  73.             b = new char[INC];
  74.             i = 0;
  75.             i_end = 0;
  76.         }
  77.         /* Implementation of the .NET interface - added as part of realease 4 (Leif) */
  78.         public string StemWord(string s)
  79.         {
  80.             setTerm(s);
  81.             stem();
  82.             return getTerm();
  83.         }
  84.         /*
  85.             SetTerm and GetTerm have been simply added to ease the 
  86.             interface with other lanaguages. They replace the add functions 
  87.             and toString function. This was done because the original functions stored
  88.             all stemmed words (and each time a new woprd was added, the buffer would be
  89.             re-copied each time, making it quite slow). Now, The class interface 
  90.             that is provided simply accepts a term and returns its stem, 
  91.             instead of storing all stemmed words.
  92.             (Leif)
  93.         */
  94.         void setTerm(string s)
  95.         {
  96.             i = s.Length;
  97.             char[] new_b = new char[i];
  98.             for (int c = 0; c < i; c++)
  99.                 new_b[c] = s[c];
  100.             b = new_b;
  101.         }
  102.         public string getTerm()
  103.         {
  104.             return new String(b, 0, i_end);
  105.         }
  106.         /* Old interface to the class - left for posterity. However, it is not
  107.             * used when accessing the class via .NET (Leif)*/
  108.         /*
  109.             * Add a character to the word being stemmed.  When you are finished
  110.             * adding characters, you can call stem(void) to stem the word.
  111.             */
  112.         public void add(char ch)
  113.         {
  114.             if (i == b.Length)
  115.             {
  116.                 char[] new_b = new char[i + INC];
  117.                 for (int c = 0; c < i; c++)
  118.                     new_b[c] = b[c];
  119.                 b = new_b;
  120.             }
  121.             b[i++] = ch;
  122.         }
  123.         /* Adds wLen characters to the word being stemmed contained in a portion
  124.             * of a char[] array. This is like repeated calls of add(char ch), but
  125.             * faster.
  126.             */
  127.         public void add(char[] w, int wLen)
  128.         {
  129.             if (i + wLen >= b.Length)
  130.             {
  131.                 char[] new_b = new char[i + wLen + INC];
  132.                 for (int c = 0; c < i; c++)
  133.                     new_b[c] = b[c];
  134.                 b = new_b;
  135.             }
  136.             for (int c = 0; c < wLen; c++)
  137.                 b[i++] = w[c];
  138.         }
  139.         /*
  140.             * After a word has been stemmed, it can be retrieved by toString(),
  141.             * or a reference to the internal buffer can be retrieved by getResultBuffer
  142.             * and getResultLength (which is generally more efficient.)
  143.             */
  144.         public override string ToString()
  145.         {
  146.             return new String(b, 0, i_end);
  147.         }
  148.         /*
  149.             * Returns the length of the word resulting from the stemming process.
  150.             */
  151.         public int getResultLength()
  152.         {
  153.             return i_end;
  154.         }
  155.         /*
  156.             * Returns a reference to a character buffer containing the results of
  157.             * the stemming process.  You also need to consult getResultLength()
  158.             * to determine the length of the result.
  159.             */
  160.         public char[] getResultBuffer()
  161.         {
  162.             return b;
  163.         }
  164.         /* cons(i) is true <=> b[i] is a consonant. */
  165.         private bool cons(int i)
  166.         {
  167.             switch (b[i])
  168.             {
  169.                 case 'a':
  170.                 case 'e':
  171.                 case 'i':
  172.                 case 'o':
  173.                 case 'u': return false;
  174.                 case 'y': return (i == 0) ? true : !cons(i - 1);
  175.                 default: return true;
  176.             }
  177.         }
  178.         /* m() measures the number of consonant sequences between 0 and j. if c is
  179.             a consonant sequence and v a vowel sequence, and <..> indicates arbitrary
  180.             presence,
  181.                 <c><v>       gives 0
  182.                 <c>vc<v>     gives 1
  183.                 <c>vcvc<v>   gives 2
  184.                 <c>vcvcvc<v> gives 3
  185.                 ....
  186.         */
  187.         private int m()
  188.         {
  189.             int n = 0;
  190.             int i = 0;
  191.             while (true)
  192.             {
  193.                 if (i > j) return n;
  194.                 if (!cons(i)) break; i++;
  195.             }
  196.             i++;
  197.             while (true)
  198.             {
  199.                 while (true)
  200.                 {
  201.                     if (i > j) return n;
  202.                     if (cons(i)) break;
  203.                     i++;
  204.                 }
  205.                 i++;
  206.                 n++;
  207.                 while (true)
  208.                 {
  209.                     if (i > j) return n;
  210.                     if (!cons(i)) break;
  211.                     i++;
  212.                 }
  213.                 i++;
  214.             }
  215.         }
  216.         /* vowelinstem() is true <=> 0,...j contains a vowel */
  217.         private bool vowelinstem()
  218.         {
  219.             int i;
  220.             for (i = 0; i <= j; i++)
  221.                 if (!cons(i))
  222.                     return true;
  223.             return false;
  224.         }
  225.         /* doublec(j) is true <=> j,(j-1) contain a double consonant. */
  226.         private bool doublec(int j)
  227.         {
  228.             if (j < 1)
  229.                 return false;
  230.             if (b[j] != b[j - 1])
  231.                 return false;
  232.             return cons(j);
  233.         }
  234.         /* cvc(i) is true <=> i-2,i-1,i has the form consonant - vowel - consonant
  235.             and also if the second c is not w,x or y. this is used when trying to
  236.             restore an e at the end of a short word. e.g.
  237.                 cav(e), lov(e), hop(e), crim(e), but
  238.                 snow, box, tray.
  239.         */
  240.         private bool cvc(int i)
  241.         {
  242.             if (i < 2 || !cons(i) || cons(i - 1) || !cons(i - 2))
  243.                 return false;
  244.             int ch = b[i];
  245.             if (ch == 'w' || ch == 'x' || ch == 'y')
  246.                 return false;
  247.             return true;
  248.         }
  249.         private bool ends(String s)
  250.         {
  251.             int l = s.Length;
  252.             int o = k - l + 1;
  253.             if (o < 0)
  254.                 return false;
  255.             char[] sc = s.ToCharArray();
  256.             for (int i = 0; i < l; i++)
  257.                 if (b[o + i] != sc[i])
  258.                     return false;
  259.             j = k - l;
  260.             return true;
  261.         }
  262.         /* setto(s) sets (j+1),...k to the characters in the string s, readjusting
  263.             k. */
  264.         private void setto(String s)
  265.         {
  266.             int l = s.Length;
  267.             int o = j + 1;
  268.             char[] sc = s.ToCharArray();
  269.             for (int i = 0; i < l; i++)
  270.                 b[o + i] = sc[i];
  271.             k = j + l;
  272.         }
  273.         /* r(s) is used further down. */
  274.         private void r(String s)
  275.         {
  276.             if (m() > 0)
  277.                 setto(s);
  278.         }
  279.         /* step1() gets rid of plurals and -ed or -ing. e.g.
  280.                 caresses  ->  caress
  281.                 ponies    ->  poni
  282.                 ties      ->  ti
  283.                 caress    ->  caress
  284.                 cats      ->  cat
  285.                 feed      ->  feed
  286.                 agreed    ->  agree
  287.                 disabled  ->  disable
  288.                 matting   ->  mat
  289.                 mating    ->  mate
  290.                 meeting   ->  meet
  291.                 milling   ->  mill
  292.                 messing   ->  mess
  293.                 meetings  ->  meet
  294.         */
  295.         private void step1()
  296.         {
  297.             if (b[k] == 's')
  298.             {
  299.                 if (ends("sses"))
  300.                     k -= 2;
  301.                 else if (ends("ies"))
  302.                     setto("i");
  303.                 else if (b[k - 1] != 's')
  304.                     k--;
  305.             }
  306.             if (ends("eed"))
  307.             {
  308.                 if (m() > 0)
  309.                     k--;
  310.             }
  311.             else if ((ends("ed") || ends("ing")) && vowelinstem())
  312.             {
  313.                 k = j;
  314.                 if (ends("at"))
  315.                     setto("ate");
  316.                 else if (ends("bl"))
  317.                     setto("ble");
  318.                 else if (ends("iz"))
  319.                     setto("ize");
  320.                 else if (doublec(k))
  321.                 {
  322.                     k--;
  323.                     int ch = b[k];
  324.                     if (ch == 'l' || ch == 's' || ch == 'z')
  325.                         k++;
  326.                 }
  327.                 else if (m() == 1 && cvc(k)) setto("e");
  328.             }
  329.         }
  330.         /* step2() turns terminal y to i when there is another vowel in the stem. */
  331.         private void step2()
  332.         {
  333.             if (ends("y") && vowelinstem())
  334.                 b[k] = 'i';
  335.         }
  336.         /* step3() maps double suffices to single ones. so -ization ( = -ize plus
  337.             -ation) maps to -ize etc. note that the string before the suffix must give
  338.             m() > 0. */
  339.         private void step3()
  340.         {
  341.             if (k == 0)
  342.                 return;
  343.             /* For Bug 1 */
  344.             switch (b[k - 1])
  345.             {
  346.                 case 'a':
  347.                     if (ends("ational")) { r("ate"); break; }
  348.                     if (ends("tional")) { r("tion"); break; }
  349.                     break;
  350.                 case 'c':
  351.                     if (ends("enci")) { r("ence"); break; }
  352.                     if (ends("anci")) { r("ance"); break; }
  353.                     break;
  354.                 case 'e':
  355.                     if (ends("izer")) { r("ize"); break; }
  356.                     break;
  357.                 case 'l':
  358.                     if (ends("bli")) { r("ble"); break; }
  359.                     if (ends("alli")) { r("al"); break; }
  360.                     if (ends("entli")) { r("ent"); break; }
  361.                     if (ends("eli")) { r("e"); break; }
  362.                     if (ends("ousli")) { r("ous"); break; }
  363.                     break;
  364.                 case 'o':
  365.                     if (ends("ization")) { r("ize"); break; }
  366.                     if (ends("ation")) { r("ate"); break; }
  367.                     if (ends("ator")) { r("ate"); break; }
  368.                     break;
  369.                 case 's':
  370.                     if (ends("alism")) { r("al"); break; }
  371.                     if (ends("iveness")) { r("ive"); break; }
  372.                     if (ends("fulness")) { r("ful"); break; }
  373.                     if (ends("ousness")) { r("ous"); break; }
  374.                     break;
  375.                 case 't':
  376.                     if (ends("aliti")) { r("al"); break; }
  377.                     if (ends("iviti")) { r("ive"); break; }
  378.                     if (ends("biliti")) { r("ble"); break; }
  379.                     break;
  380.                 case 'g':
  381.                     if (ends("logi")) { r("log"); break; }
  382.                     break;
  383.                 default:
  384.                     break;
  385.             }
  386.         }
  387.         /* step4() deals with -ic-, -full, -ness etc. similar strategy to step3. */
  388.         private void step4()
  389.         {
  390.             switch (b[k])
  391.             {
  392.                 case 'e':
  393.                     if (ends("icate")) { r("ic"); break; }
  394.                     if (ends("ative")) { r(""); break; }
  395.                     if (ends("alize")) { r("al"); break; }
  396.                     break;
  397.                 case 'i':
  398.                     if (ends("iciti")) { r("ic"); break; }
  399.                     break;
  400.                 case 'l':
  401.                     if (ends("ical")) { r("ic"); break; }
  402.                     if (ends("ful")) { r(""); break; }
  403.                     break;
  404.                 case 's':
  405.                     if (ends("ness")) { r(""); break; }
  406.                     break;
  407.             }
  408.         }
  409.         /* step5() takes off -ant, -ence etc., in context <c>vcvc<v>. */
  410.         private void step5()
  411.         {
  412.             if (k == 0)
  413.                 return;
  414.             /* for Bug 1 */
  415.             switch (b[k - 1])
  416.             {
  417.                 case 'a':
  418.                     if (ends("al")) break; return;
  419.                 case 'c':
  420.                     if (ends("ance")) break;
  421.                     if (ends("ence")) break; return;
  422.                 case 'e':
  423.                     if (ends("er")) break; return;
  424.                 case 'i':
  425.                     if (ends("ic")) break; return;
  426.                 case 'l':
  427.                     if (ends("able")) break;
  428.                     if (ends("ible")) break; return;
  429.                 case 'n':
  430.                     if (ends("ant")) break;
  431.                     if (ends("ement")) break;
  432.                     if (ends("ment")) break;
  433.                     /* element etc. not stripped before the m */
  434.                     if (ends("ent")) break; return;
  435.                 case 'o':
  436.                     if (ends("ion") && j >= 0 && (b[j] == 's' || b[j] == 't')) break;
  437.                     /* j >= 0 fixes Bug 2 */
  438.                     if (ends("ou")) break; return;
  439.                 /* takes care of -ous */
  440.                 case 's':
  441.                     if (ends("ism")) break; return;
  442.                 case 't':
  443.                     if (ends("ate")) break;
  444.                     if (ends("iti")) break; return;
  445.                 case 'u':
  446.                     if (ends("ous")) break; return;
  447.                 case 'v':
  448.                     if (ends("ive")) break; return;
  449.                 case 'z':
  450.                     if (ends("ize")) break; return;
  451.                 default:
  452.                     return;
  453.             }
  454.             if (m() > 1)
  455.                 k = j;
  456.         }
  457.         /* step6() removes a final -e if m() > 1. */
  458.         private void step6()
  459.         {
  460.             j = k;
  461.             if (b[k] == 'e')
  462.             {
  463.                 int a = m();
  464.                 if (a > 1 || a == 1 && !cvc(k - 1))
  465.                     k--;
  466.             }
  467.             if (b[k] == 'l' && doublec(k) && m() > 1)
  468.                 k--;
  469.         }
  470.         /* Stem the word placed into the Stemmer buffer through calls to add().
  471.             * Returns true if the stemming process resulted in a word different
  472.             * from the input.  You can retrieve the result with
  473.             * getResultLength()/getResultBuffer() or toString().
  474.             */
  475.         public void stem()
  476.         {
  477.             k = i - 1;
  478.             if (k > 1)
  479.             {
  480.                 step1();
  481.                 step2();
  482.                 step3();
  483.                 step4();
  484.                 step5();
  485.                 step6();
  486.             }
  487.             i_end = k + 1;
  488.             i = 0;
  489.         }
  490.     }
  491. }