PorterStemmer.cs
上传用户:zhangkuixh
上传日期:2013-09-30
资源大小:5473k
文件大小:15k
源码类别:

搜索引擎

开发平台:

C#

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