2010-12-09 26 views
11

Connaissez-vous une implémentation java du stemmer Porter2 (ou un meilleur stemmer écrit en java)? Je sais qu'il ya une version java de Porter (non Porter2) ici:Existe-t-il une implémentation java de Porter2 stemmer

http://tartarus.org/~martin/PorterStemmer/java.txt

mais http://tartarus.org/~martin/PorterStemmer/ l'auteur mentionne que le Porter est un peu obsolète et recommande d'utiliser Porter2, disponible à

http://snowball.tartarus.org/algorithms/english/stemmer.html

Cependant, le problème avec moi est que ce Porter2 est écrit en boule de neige (je n'en ai jamais entendu parler auparavant, donc je ne sais rien à ce sujet). Ce que je cherche exactement, c'est une version Java.

Merci. Votre aide sera grandement appréciée.

Répondre

16

La boule de neige algo est disponible en Java download

Et de snowball.tartarus.org:

février 2002 - Prise en charge Java Richard a modifié le générateur de code de boule de neige pour produire une sortie Java, ainsi que ANSI C sortie. Cela signifie que les systèmes purs Java peuvent maintenant utiliser les stemmers snowball.

C'est ce que vous voulez, non?

Vous pouvez créer une instance de celui-ci comme ceci:

Class stemClass = Class.forName("org.tartarus.snowball.ext." + lang + "Stemmer"); 
    stemmer = (SnowballProgram) stemClass.newInstance(); 
    stemmer.setCurrent("your_word"); 
    stemmer.stem(); 
    String your_stemmed_word = stemmer.getCurrent(); 
+1

Oui c'est exactement ce dont j'avais besoin. Exactement! Merci pour ça. Cependant, cela vous dérangerait-il d'éditer le code? Il devrait être: SnowballStemmer stemmer = (SnowballStemmer) stemClass.newInstance(); dans la deuxième ligne. Merci. – bikashg

+0

Et j'ai une autre requête. Mon entrée dans le stemmer est un tableau contenant l'anglais ainsi que d'autres mots de la langue. Puisque je veux juste passer les mots anglais au stemmer anglais, et empêcher d'autres mots étant passés au Stemmer anglais, comment je peux vérifier cela. Pouvez-vous me référer à un lien? – bikashg

+0

Salut Bikash, en ce qui concerne le filtrage des mots non-anglais ma seule suggestion serait de filtrer en utilisant une carte de dictionnaire. Il y a beaucoup de dictionnaires de fichiers texte que vous pouvez télécharger, par exemple - http://www.curlewcommunications.co.uk/wordlist.html - bien que vous voudrez probablement combiner avec une liste d'anglais américain aussi. Je vous recommande également de consulter le dictionnaire WordNet http://wordnet.princeton.edu/. Je note qu'il est beaucoup plus difficile d'identifier des mots anglais individuels, plutôt que d'identifier un bloc entier de texte, car avec un bloc entier de texte. – Joel

0

Peut-être pas une réponse directe, mais il y a beaucoup de mots dans beaucoup de boîtes à outils PNL - voir http://en.wikipedia.org/wiki/Natural_language_processing_toolkits. Il ya une question connexe ici Tokenizer, Stop Word Removal, Stemming in Java avec plusieurs réponses qui pourraient être utiles.

Nous utilisons OpenNLP qui est écrit en Java et peut fournir la fonctionnalité. Je ne m'attendrais pas à ce que la variation entre les stemmers soit critique si vous travaillez en anglais.

0

On dirait Luceneintegrates, sous une forme ou une autre, certains algorithmes de bourrage. Vous pouvez trouver ce que vous cherchez à partir du paquet org.apache.lucene.analysis. Je crains cependant que le code racine ne soit profondément intégré dans les composants d'analyse, rendant par conséquent très difficile son extraction ...

+1

La dernière fois que j'ai utilisé lucene je pense que c'était porteuse1. La nouvelle version offre-t-elle proter2? – Favonius

2

Il est disponible dans le cadre de MG4J. Voir la documentation pour EnglishStemmer, à savoir Porter2. La méthode d'utilisation processTerm(MutableString ms)

MG4J vous donne également des versions java d'autres stemmers. Voir le snowball package. Tous ces stemmers peuvent être utilisés indépendamment.

+0

@Valentin Quel vote en baisse? – athena

+0

il y avait un downvote à votre réponse quand j'ai vérifié, maintenant il semble avoir disparu :) –

3
/* 

    Porter stemmer in Java. The original paper is in 

     Porter, 1980, An algorithm for suffix stripping, Program, Vol. 14, 
     no. 3, pp 130-137, 

    See also http://www.tartarus.org/~martin/PorterStemmer 

    History: 

    Release 1 

    Bug 1 (reported by Gonzalo Parra 16/10/99) fixed as marked below. 
    The words 'aed', 'eed', 'oed' leave k at 'a' for step 3, and b[k-1] 
    is then out outside the bounds of b. 

    Release 2 

    Similarly, 

    Bug 2 (reported by Steve Dyrdahl 22/2/00) fixed as marked below. 
    'ion' by itself leaves j = -1 in the test for 'ion' in step 5, and 
    b[j] is then outside the bounds of b. 

    Release 3 

    Considerably revised 4/9/00 in the light of many helpful suggestions 
    from Brian Goetz of Quiotix Corporation ([email protected]). 

    Release 4 

*/ 

import java.io.*; 

/** 
    * Stemmer, implementing the Porter Stemming Algorithm 
    * 
    * The Stemmer class transforms a word into its root form. The input 
    * word can be provided a character at time (by calling add()), or at once 
    * by calling one of the various stem(something) methods. 
    */ 

class Stemmer 
{ private char[] b; 
    private int i,  /* offset into b */ 
       i_end, /* offset to end of stemmed word */ 
       j, k; 
    private static final int INC = 50; 
        /* unit of size whereby b is increased */ 
    public Stemmer() 
    { b = new char[INC]; 
     i = 0; 
     i_end = 0; 
    } 

    /** 
    * Add a character to the word being stemmed. When you are finished 
    * adding characters, you can call stem(void) to stem the word. 
    */ 

    public void add(char ch) 
    { if (i == b.length) 
     { char[] new_b = new char[i+INC]; 
     for (int c = 0; c < i; c++) new_b[c] = b[c]; 
     b = new_b; 
     } 
     b[i++] = ch; 
    } 


    /** Adds wLen characters to the word being stemmed contained in a portion 
    * of a char[] array. This is like repeated calls of add(char ch), but 
    * faster. 
    */ 

    public void add(char[] w, int wLen) 
    { if (i+wLen >= b.length) 
     { char[] new_b = new char[i+wLen+INC]; 
     for (int c = 0; c < i; c++) new_b[c] = b[c]; 
     b = new_b; 
     } 
     for (int c = 0; c < wLen; c++) b[i++] = w[c]; 
    } 

    /** 
    * After a word has been stemmed, it can be retrieved by toString(), 
    * or a reference to the internal buffer can be retrieved by getResultBuffer 
    * and getResultLength (which is generally more efficient.) 
    */ 
    public String toString() { return new String(b,0,i_end); } 

    /** 
    * Returns the length of the word resulting from the stemming process. 
    */ 
    public int getResultLength() { return i_end; } 

    /** 
    * Returns a reference to a character buffer containing the results of 
    * the stemming process. You also need to consult getResultLength() 
    * to determine the length of the result. 
    */ 
    public char[] getResultBuffer() { return b; } 

    /* cons(i) is true <=> b[i] is a consonant. */ 

    private final boolean cons(int i) 
    { switch (b[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, 

     <c><v>  gives 0 
     <c>vc<v>  gives 1 
     <c>vcvc<v> gives 2 
     <c>vcvcvc<v> gives 3 
     .... 
    */ 

    private final int m() 
    { int n = 0; 
     int i = 0; 
     while(true) 
     { if (i > j) return n; 
     if (! cons(i)) break; i++; 
     } 
     i++; 
     while(true) 
     { while(true) 
     { if (i > j) return n; 
       if (cons(i)) break; 
       i++; 
     } 
     i++; 
     n++; 
     while(true) 
     { if (i > j) return n; 
      if (! cons(i)) break; 
      i++; 
     } 
     i++; 
     } 
    } 

    /* vowelinstem() is true <=> 0,...j contains a vowel */ 

    private final boolean vowelinstem() 
    { int i; for (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 (b[j] != b[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; 
     { int ch = b[i]; 
     if (ch == 'w' || ch == 'x' || ch == 'y') return false; 
     } 
     return true; 
    } 

    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 (b[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++) b[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 (b[k] == 's') 
     { if (ends("sses")) k -= 2; else 
     if (ends("ies")) setto("i"); else 
     if (b[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--; 
      { int ch = b[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()) b[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 (b[k-1]) 
    { 
     case 'a': if (ends("ational")) { r("ate"); break; } 
       if (ends("tional")) { r("tion"); break; } 
       break; 
     case 'c': if (ends("enci")) { r("ence"); break; } 
       if (ends("anci")) { r("ance"); break; } 
       break; 
     case 'e': if (ends("izer")) { r("ize"); break; } 
       break; 
     case 'l': if (ends("bli")) { r("ble"); break; } 
       if (ends("alli")) { r("al"); break; } 
       if (ends("entli")) { r("ent"); break; } 
       if (ends("eli")) { r("e"); break; } 
       if (ends("ousli")) { r("ous"); break; } 
       break; 
     case 'o': if (ends("ization")) { r("ize"); break; } 
       if (ends("ation")) { r("ate"); break; } 
       if (ends("ator")) { r("ate"); break; } 
       break; 
     case 's': if (ends("alism")) { r("al"); break; } 
       if (ends("iveness")) { r("ive"); break; } 
       if (ends("fulness")) { r("ful"); break; } 
       if (ends("ousness")) { r("ous"); break; } 
       break; 
     case 't': if (ends("aliti")) { r("al"); break; } 
       if (ends("iviti")) { r("ive"); break; } 
       if (ends("biliti")) { r("ble"); break; } 
       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 (b[k]) 
    { 
     case 'e': if (ends("icate")) { r("ic"); break; } 
       if (ends("ative")) { r(""); break; } 
       if (ends("alize")) { r("al"); break; } 
       break; 
     case 'i': if (ends("iciti")) { r("ic"); break; } 
       break; 
     case 'l': if (ends("ical")) { r("ic"); break; } 
       if (ends("ful")) { r(""); break; } 
       break; 
     case 's': if (ends("ness")) { r(""); break; } 
       break; 
    } } 

    /* step5() takes off -ant, -ence etc., in context <c>vcvc<v>. */ 

    private final void step5() 
    { if (k == 0) return; /* for Bug 1 */ switch (b[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 (b[k] == 'e') 
     { int a = m(); 
     if (a > 1 || a == 1 && !cvc(k-1)) k--; 
     } 
     if (b[k] == 'l' && doublec(k) && m() > 1) k--; 
    } 

    /** Stem the word placed into the Stemmer buffer through calls to add(). 
    * Returns true if the stemming process resulted in a word different 
    * from the input. You can retrieve the result with 
    * getResultLength()/getResultBuffer() or toString(). 
    */ 
    public void stem() 
    { k = i - 1; 
     if (k > 1) { step1(); step2(); step3(); step4(); step5(); step6(); } 
     i_end = k+1; i = 0; 
    } 

    /** Test program for demonstrating the Stemmer. It reads text from a 
    * a list of files, stems each word, and writes the result to standard 
    * output. Note that the word stemmed is expected to be in lower case: 
    * forcing lower case must be done outside the Stemmer class. 
    * Usage: Stemmer file-name file-name ... 
    */ 
    public static void main(String[] args) 
    { 
     char[] w = new char[501]; 
     Stemmer s = new Stemmer(); 
     for (int i = 0; i < args.length; i++) 
     try 
     { 
     FileInputStream in = new FileInputStream(args[i]); 

     try 
     { while(true) 

      { int ch = in.read(); 
       if (Character.isLetter((char) ch)) 
       { 
       int j = 0; 
       while(true) 
       { ch = Character.toLowerCase((char) ch); 
        w[j] = (char) ch; 
        if (j < 500) j++; 
        ch = in.read(); 
        if (!Character.isLetter((char) ch)) 
        { 
         /* to test add(char ch) */ 
         for (int c = 0; c < j; c++) s.add(w[c]); 

         /* or, to test add(char[] w, int j) */ 
         /* s.add(w, j); */ 

         s.stem(); 
         { String u; 

          /* and now, to test toString() : */ 
          u = s.toString(); 

          /* to test getResultBuffer(), getResultLength() : */ 
          /* u = new String(s.getResultBuffer(), 0, s.getResultLength()); */ 

          System.out.print(u); 
         } 
         break; 
        } 
       } 
       } 
       if (ch < 0) break; 
       System.out.print((char)ch); 
      } 
     } 
     catch (IOException e) 
     { System.out.println("error reading " + args[i]); 
      break; 
     } 
     } 
     catch (FileNotFoundException e) 
     { System.out.println("file " + args[i] + " not found"); 
     break; 
     } 
    } 
}