2009-08-25 19 views
16

Estoy buscando una biblioteca similar en funcionalidad al módulo Perl Lingua::EN::NameParse. Básicamente, me gustaría analizar cadenas como 'Mr. Bob R. Smith en los componentes de prefijo, nombre, apellido y sufijo de nombre. Google no ha sido de mucha ayuda para encontrar algo como esto y preferiría no rodar el mío si es posible. ¿Alguien sabe de una biblioteca OSS Java que puede hacer esto de una manera sofisticada?¿Qué es la biblioteca de análisis de nombre de Java?

Respuesta

0

Personalmente, optaría por regular expressions. Aquí hay un buen intro. Son rápidos, concisos y siempre hagan lo que quieran.

Si quiere mantenerse dentro de los límites de java SDK, use String tokenizers.

Un poco más bajo nivel es JavaCC, un generador de analizador basado en Java. Aquí hay un link to a tutorial.

Una alternativa a javaCC es ANTLR, con la que personalmente he tenido buenas experiencias.

5

¿Quizás podría probar el componente de extracción de entidad con nombre GATE? Tiene una compilación en jape listas de gramática y nomenclátor para extraer los nombres, apellidos, etc., entre otras cosas. Ver la página this.

+1

+1 ¿Me puede mostrar un ejemplo de cómo analizar los nombres utilizando GATE –

0

Este código simple puede ayudar:

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; 
    } 
} 

Por ejemplo esta entrada:

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); 

tiene esta salida:

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

No puedo creer que alguien no ha compartido una biblioteca para esto - bueno miré GitHub y hay un nombre analizador javascript que podría traducirse fácilmente a java: https://github.com/joshfraser/JavaScript-Name-Parser

También he modificado el código en una de las respuestas que trabajar un poco mejor y han incluido un caso de prueba:

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; 
    } 

} 

prueba 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

El método isOneOf deben reescribirse como: isOneOf private boolean (cadena checkStr, títulos String []) { para (título de la cadena: títulos) if (checkStr.equalsIgnoreCase (title)) return true; devuelve falso; } Porque actualmente, reconocerá todos los nombres comenzando con una V como sufijo. – KimvdLinde

3

Apache Commons tiene la clase 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

Puede encontrarlo en [github] (https://github.com/apache/commons-text/blob/master/src/main/java/org/apache/commons/text/names/HumanNameParser.java).La versión actual todavía es SNAPSHOT sin embargo. –

Cuestiones relacionadas