All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.ucla.sspace.text.PorterStemmer Maven / Gradle / Ivy

Go to download

The S-Space Package is a collection of algorithms for building Semantic Spaces as well as a highly-scalable library for designing new distributional semantics algorithms. Distributional algorithms process text corpora and represent the semantic for words as high dimensional feature vectors. This package also includes matrices, vectors, and numerous clustering algorithms. These approaches are known by many names, such as word spaces, semantic spaces, or distributed semantics and rest upon the Distributional Hypothesis: words that appear in similar contexts have similar meanings.

The newest version!
/*
 * Copyright 2009 Keith Stevens 
 *
 * This file is part of the S-Space package and is covered under the terms and
 * conditions therein.
 *
 * The S-Space package is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation and distributed hereunder to you.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND NO REPRESENTATIONS OR WARRANTIES,
 * EXPRESS OR IMPLIED ARE MADE.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, WE MAKE
 * NO REPRESENTATIONS OR WARRANTIES OF MERCHANT- ABILITY OR FITNESS FOR ANY
 * PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE OR DOCUMENTATION
 * WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER
 * RIGHTS.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see .
 */


package edu.ucla.sspace.text;


/**
 * This is an implementation of the Porter stemmer in Java. The original paper
 * is in  Porter, 1980, An algorithm for suffix stripping, Program, Vol. 14, no.
 * 3, pp 130-137,
 * 
 * 

This code has been ported and heavily modified based on the original * implementation found here * * @author Keith Stevens */ public class PorterStemmer implements Stemmer { private StringBuilder sb; private char[] b; private int j, k; /** * Creates a new {@link PorterStemmer} */ public PorterStemmer() { } /** * {@inheritDoc} */ public synchronized String stem(String token) { // NOTE: due to the fact that this implemenation saves state per // stemming operation, the method needs to be synchronized in order to // prevent race conditions by multiple threads. sb = new StringBuilder(token); k = sb.length() - 1; if (k > 1) { step1(); step2(); step3(); step4(); step5(); step6(); } sb.delete(k+1, sb.length()); return sb.toString(); } /** * cons(i) is true <=> b[i] is a consonant. */ private final boolean cons(int i) { switch (sb.charAt(i)) { case 'a': case 'e': case 'i': case 'o': case 'u': return false; case 'y': return (i==0) ? true : !cons(i-1); default: return true; } } /** * m() measures the number of consonant sequences between 0 and j. if c is * a consonant sequence and v a vowel sequence, and <..> indicates * arbitrary presence, * * gives 0 * vc gives 1 * vcvc gives 2 * vcvcvc gives 3 * .... */ private final int m() { int n = 0; int i = 0; for (; cons(i); ++i) { if (i > j) return n; } i++; while(i <= j) { for (; !cons(i); ++i) { if (i > j) return n; } i++; n++; if (i > j) return n; for (; cons(i); ++i) { if (i > j) return n; } i++; } return n; } /* vowelinstem() is true <=> 0,...j contains a vowel */ private final boolean vowelinstem() { for (int i = 0; i <= j; i++) if (!cons(i)) return true; return false; } /* doublec(j) is true <=> j,(j-1) contain a double consonant. */ private final boolean doublec(int j) { if (j < 1) return false; if (sb.charAt(j) != sb.charAt(j-1)) return false; return cons(j); } /* cvc(i) is true <=> i-2,i-1,i has the form consonant - vowel - consonant and also if the second c is not w,x or y. this is used when trying to restore an e at the end of a short word. e.g. cav(e), lov(e), hop(e), crim(e), but snow, box, tray. */ private final boolean cvc(int i) { if (i < 2 || !cons(i) || cons(i-1) || !cons(i-2)) return false; char ch = sb.charAt(i); return !(ch == 'w' || ch == 'x' || ch == 'y'); } private final boolean ends(String s) { int l = s.length(); int o = k-l+1; if (o < 0) return false; for (int i = 0; i < l; i++) if (sb.charAt(o+i) != s.charAt(i)) return false; j = k-l; return true; } /* setto(s) sets (j+1),...k to the characters in the string s, readjusting k. */ private final void setto(String s) { int l = s.length(); int o = j+1; for (int i = 0; i < l; i++) sb.setCharAt(o+i, s.charAt(i)); k = j+l; } /* r(s) is used further down. */ private final void r(String s) { if (m() > 0) setto(s); } /* step1() gets rid of plurals and -ed or -ing. e.g. caresses -> caress ponies -> poni ties -> ti caress -> caress cats -> cat feed -> feed agreed -> agree disabled -> disable matting -> mat mating -> mate meeting -> meet milling -> mill messing -> mess meetings -> meet */ private final void step1() { if (sb.charAt(k) == 's') { if (ends("sses")) k -= 2; else if (ends("ies")) setto("i"); else if (sb.charAt(k-1) != 's') k--; } if (ends("eed")) { if (m() > 0) k--; } else if ((ends("ed") || ends("ing")) && vowelinstem()) { k = j; if (ends("at")) setto("ate"); else if (ends("bl")) setto("ble"); else if (ends("iz")) setto("ize"); else if (doublec(k)) { k--; char ch = sb.charAt(k); if (ch == 'l' || ch == 's' || ch == 'z') k++; } else if (m() == 1 && cvc(k)) setto("e"); } } /* step2() turns terminal y to i when there is another vowel in the stem. */ private final void step2() { if (ends("y") && vowelinstem()) sb.setCharAt(k, 'i'); } /* step3() maps double suffices to single ones. so -ization ( = -ize plus -ation) maps to -ize etc. note that the string before the suffix must give m() > 0. */ private final void step3() { if (k == 0) return; /* For Bug 1 */ switch (sb.charAt(k-1)) { case 'a': if (ends("ational")) r("ate"); else if (ends("tional")) r("tion"); break; case 'c': if (ends("enci")) r("ence"); else if (ends("anci")) r("ance"); break; case 'e': if (ends("izer")) r("ize"); break; case 'l': if (ends("bli")) r("ble"); else if (ends("alli")) r("al"); else if (ends("entli")) r("ent"); else if (ends("eli")) r("e"); else if (ends("ousli")) r("ous"); break; case 'o': if (ends("ization")) r("ize"); else if (ends("ation")) r("ate"); else if (ends("ator")) r("ate"); break; case 's': if (ends("alism")) r("al"); else if (ends("iveness")) r("ive"); else if (ends("fulness")) r("ful"); else if (ends("ousness")) r("ous"); break; case 't': if (ends("aliti")) r("al"); else if (ends("iviti")) r("ive"); else if (ends("biliti")) r("ble"); break; case 'g': if (ends("logi")) r("log"); break; } } /* step4() deals with -ic-, -full, -ness etc. similar strategy to step3. */ private final void step4() { switch (sb.charAt(k)) { case 'e': if (ends("icate")) r("ic"); else if (ends("ative")) r(""); else if (ends("alize")) r("al"); break; case 'i': if (ends("iciti")) r("ic"); break; case 'l': if (ends("ical")) r("ic"); else if (ends("ful")) r(""); break; case 's': if (ends("ness")) r(""); break; } } /* step5() takes off -ant, -ence etc., in context vcvc. */ private final void step5() { if (k == 0) return; /* for Bug 1 */ switch (sb.charAt(k-1)) { case 'a': if (ends("al")) break; return; case 'c': if (ends("ance")) break; if (ends("ence")) break; return; case 'e': if (ends("er")) break; return; case 'i': if (ends("ic")) break; return; case 'l': if (ends("able")) break; if (ends("ible")) break; return; case 'n': if (ends("ant")) break; if (ends("ement")) break; if (ends("ment")) break; /* element etc. not stripped before the m */ if (ends("ent")) break; return; case 'o': if (ends("ion") && j >= 0 && (b[j] == 's' || b[j] == 't')) break; /* j >= 0 fixes Bug 2 */ if (ends("ou")) break; return; /* takes care of -ous */ case 's': if (ends("ism")) break; return; case 't': if (ends("ate")) break; if (ends("iti")) break; return; case 'u': if (ends("ous")) break; return; case 'v': if (ends("ive")) break; return; case 'z': if (ends("ize")) break; return; default: return; } if (m() > 1) k = j; } /* step6() removes a final -e if m() > 1. */ private final void step6() { j = k; if (sb.charAt(k) == 'e') { int a = m(); if (a > 1 || a == 1 && !cvc(k-1)) k--; } if (sb.charAt(k) == 'l' && doublec(k) && m() > 1) k--; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy