2009-08-25 14 views
16

Sto cercando una libreria simile nella funzionalità al modulo Perl Lingua::EN::NameParse. In sostanza, mi piacerebbe analizzare stringhe come "Mr. Bob R. Smith 'in prefisso, nome, cognome e componenti del suffisso del nome. Google non è stato di grande aiuto nel trovare qualcosa di simile e preferirei non lanciare il mio, se possibile. Qualcuno sa di una libreria Java OSS che può farlo in un modo sofisticato?Libreria di analisi nome Java?

risposta

0

Personalmente, opterei per regular expressions. Ecco un buon intro. Sono veloci, concisi e sempre fai quello che vuoi.

Se si desidera rimanere entro i limiti del sdk java, utilizzare String tokenizers.

Un po 'più di basso livello è JavaCC, un generatore di parser basato su Java. Ecco uno link to a tutorial.

Un'alternativa a javaCC è ANTLR, con cui ho avuto personalmente esperienze positive.

5

Forse potresti provare il componente di estrazione dell'entità denominato GATE? Ha compilato le liste di grammatica e di dizionari geografici jape per estrarre il nome, il cognome ecc. Tra le altre cose. Vedi la pagina this.

+1

+1 Puoi mostrarmi un esempio di come analizzare i nomi che utilizzano GATE –

0

Questo semplice codice può aiutare:

import java.util.ArrayList; 
import java.util.List; 

public class NamesConverter { 

    private List<String> titlesBefore = new ArrayList<>(); 
    private List<String> titlesAfter = new ArrayList<>(); 
    private String firstName = ""; 
    private String lastName = ""; 
    private List<String> middleNames = new ArrayList<>(); 

    public NamesConverter(String name) { 
     String[] words = name.split(" "); 
     boolean isTitleAfter = false; 
     boolean isFirstName = false; 

     int length = words.length; 
     for (String word : words) { 
      if (word.charAt(word.length() - 1) == '.') { 
       if (isTitleAfter) { 
        titlesAfter.add(word); 
       } else { 
        titlesBefore.add(word); 
       } 
      } else { 
       isTitleAfter = true; 
       if (isFirstName == false) { 
        firstName = word; 
        isFirstName = true; 
       } else { 
        middleNames.add(word); 
       } 
      } 
     } 
     if (middleNames.size() > 0) { 
      lastName = middleNames.get(middleNames.size() - 1); 
      middleNames.remove(lastName); 
     } 
    } 

    public List<String> getTitlesBefore() { 
     return titlesBefore; 
    } 

    public List<String> getTitlesAfter() { 
     return titlesAfter; 
    } 

    public String getFirstName() { 
     return firstName; 
    } 

    public String getLastName() { 
     return lastName; 
    } 

    public List<String> getMiddleNames() { 
     return middleNames; 
    } 

    @Override 
    public String toString() { 
     String text = "Titles before :" + titlesBefore.toString() + "\n" 
       + "First name :" + firstName + "\n" 
       + "Middle names :" + middleNames.toString() + "\n" 
       + "Last name :" + lastName + "\n" 
       + "Titles after :" + titlesAfter.toString() + "\n"; 

     return text; 
    } 
} 

Per esempio questo ingresso:

NamesConverter ns = new NamesConverter("Mr. Dr. Tom Jones"); 
    NamesConverter ns1 = new NamesConverter("Ing. Tom Ridley Bridley Furthly Murthly Jones CsC."); 
    System.out.println(ns); 
    System.out.println(ns1); 

ha questa uscita:

Titles before :[Mr., Dr.] 
First name :Tom 
Middle names :[] 
Last name :Jones 
Titles after :[] 

Titles before :[Ing.] 
First name :Tom 
Middle names :[Ridley, Bridley, Furthly, Murthly] 
Last name :Jones 
Titles after :[CsC.] 
8

Non riesco a credere che qualcuno non ha condiviso una libreria per questo - beh, ho guardato dentro github e c'è un nome parser javascript che potrebbe facilmente essere tradotto a java: https://github.com/joshfraser/JavaScript-Name-Parser

ho anche modificato il codice in una delle risposte a lavorare un po 'meglio e hanno incluso un banco di prova:

import java.util.ArrayList; 
import java.util.List; 

import org.apache.commons.lang.StringUtils; 

public class NameParser { 
    private String firstName = ""; 
    private String lastName = ""; 
    private String middleName = ""; 
    private List<String> middleNames = new ArrayList<String>(); 
    private List<String> titlesBefore = new ArrayList<String>(); 
    private List<String> titlesAfter = new ArrayList<String>(); 
    private String[] prefixes = { "dr", "mr", "ms", "atty", "prof", "miss", "mrs" }; 
    private String[] suffixes = { "jr", "sr", "ii", "iii", "iv", "v", "vi", "esq", "2nd", "3rd", "jd", "phd", 
      "md", "cpa" }; 

    public NameParser() { 
    } 

    public NameParser(String name) { 
     parse(name); 
    } 

    private void reset() { 
     firstName = lastName = middleName = ""; 
     middleNames = new ArrayList<String>(); 
     titlesBefore = new ArrayList<String>(); 
     titlesAfter = new ArrayList<String>(); 
    } 

    private boolean isOneOf(String checkStr, String[] titles) { 
     for (String title : titles) { 
      if (checkStr.toLowerCase().startsWith(title)) 
       return true; 
     } 
     return false; 
    } 

    public void parse(String name) { 
     if (StringUtils.isBlank(name)) 
      return; 
     this.reset(); 
     String[] words = name.split(" "); 
     boolean isFirstName = false; 

     for (String word : words) { 
      if (StringUtils.isBlank(word)) 
       continue; 
      if (word.charAt(word.length() - 1) == '.') { 
       if (!isFirstName && !this.isOneOf(word, prefixes)) { 
        firstName = word; 
        isFirstName = true; 
       } else if (isFirstName) { 
        middleNames.add(word); 
       } else { 
        titlesBefore.add(word); 
       } 
      } else { 
       if (word.endsWith(",")) 
        word = StringUtils.chop(word); 
       if (isFirstName == false) { 
        firstName = word; 
        isFirstName = true; 
       } else { 
        middleNames.add(word); 
       } 
      } 
     } 
     if (middleNames.size() > 0) { 
      boolean stop = false; 
      List<String> toRemove = new ArrayList<String>(); 
      for (int i = middleNames.size() - 1; i >= 0 && !stop; i--) { 
       String str = middleNames.get(i); 
       if (this.isOneOf(str, suffixes)) { 
        titlesAfter.add(str); 
       } else { 
        lastName = str; 
        stop = true; 
       } 
       toRemove.add(str); 
      } 
      if (StringUtils.isBlank(lastName) && titlesAfter.size() > 0) { 
       lastName = titlesAfter.get(titlesAfter.size() - 1); 
       titlesAfter.remove(titlesAfter.size() - 1); 
      } 
      for (String s : toRemove) { 
       middleNames.remove(s); 
      } 
     } 
    } 

    public String getFirstName() { 
     return firstName; 
    } 

    public String getLastName() { 
     return lastName; 
    } 

    public String getMiddleName() { 
     if (StringUtils.isBlank(this.middleName)) { 
      for (String name : middleNames) { 
       middleName += (name + " "); 
      } 
      middleName = StringUtils.chop(middleName); 
     } 
     return middleName; 
    } 

    public List<String> getTitlesBefore() { 
     return titlesBefore; 
    } 

    public List<String> getTitlesAfter() { 
     return titlesAfter; 
    } 

} 

test case:

import junit.framework.Assert; 

import org.junit.Test; 

public class NameParserTest { 

    private class TestData { 
     String name; 

     String firstName; 
     String lastName; 
     String middleName; 

     public TestData(String name, String firstName, String middleName, String lastName) { 
      super(); 
      this.name = name; 
      this.firstName = firstName; 
      this.lastName = lastName; 
      this.middleName = middleName; 
     } 

    } 

    @Test 
    public void test() { 

     TestData td[] = { new TestData("Henry \"Hank\" J. Fasthoff IV", "Henry", "\"Hank\" J.", "Fasthoff"), 
       new TestData("April A. (Caminez) Bentley", "April", "A. (Caminez)", "Bentley"), 
       new TestData("fff lll", "fff", "", "lll"), 
       new TestData("fff mmmmm lll", "fff", "mmmmm", "lll"), 
       new TestData("fff mmm1  mm2 lll", "fff", "mmm1 mm2", "lll"), 
       new TestData("Mr. Dr. Tom Jones", "Tom", "", "Jones"), 
       new TestData("Robert P. Bethea Jr.", "Robert", "P.", "Bethea"), 
       new TestData("Charles P. Adams, Jr.", "Charles", "P.", "Adams"), 
       new TestData("B. Herbert Boatner, Jr.", "B.", "Herbert", "Boatner"), 
       new TestData("Bernard H. Booth IV", "Bernard", "H.", "Booth"), 
       new TestData("F. Laurens \"Larry\" Brock", "F.", "Laurens \"Larry\"", "Brock"), 
       new TestData("Chris A. D'Amour", "Chris", "A.", "D'Amour") }; 

     NameParser bp = new NameParser(); 
     for (int i = 0; i < td.length; i++) { 
      bp.parse(td[i].name); 
      Assert.assertEquals(td[i].firstName, bp.getFirstName()); 
      Assert.assertEquals(td[i].lastName, bp.getLastName()); 
      Assert.assertEquals(td[i].middleName, bp.getMiddleName()); 
     } 
    } 

} 
+0

Il metodo isOneOf dovrebbe essere riscritto come: isOneOf privato booleani (String checkStr, titoli String []) { per (Titolo stringa: titoli) if (checkStr.equalsIgnoreCase (title)) return true; return false; } Perché attualmente riconoscerà tutti i nomi che iniziano con una V come suffisso. – KimvdLinde

3

Apache Commons ha la classe HumanNameParser.

https://commons.apache.org/sandbox/commons-text/jacoco/org.apache.commons.text.names/HumanNameParser.java.html

Name nextName = parser.parse("James C. ('Jimmy') O'Dell, Jr.") 
String firstName = nextName.getFirstName(); 
String nickname = nextName.getNickName(); 
+0

Puoi trovarlo su [github] (https://github.com/apache/commons-text/blob/master/src/main/java/org/apache/commons/text/names/HumanNameParser.java).La versione corrente è comunque SNAPSHOT. –