2010-10-03 10 views
22

Necesito poder reconocer las cadenas de fecha. No importa si no puedo distinguir entre el mes y la fecha (por ejemplo, 12/12/10), solo necesito clasificar la cadena como una fecha, en lugar de convertirla en un objeto Date. Entonces, esto es realmente una clasificación en lugar de un problema de análisis.Reconocer una cadena de fecha arbitraria

tendré fragmentos de texto como:

"bla bla bla bla 12 Ene 09 bla bla bla 01/04/10 bla bla bla"

y necesito poder reconocer el límite de inicio y final para cada cadena de fecha dentro.

Me preguntaba si alguien sabía de alguna biblioteca de Java que pueda hacer esto. Mi google-fu no ha encontrado nada hasta ahora.

ACTUALIZACIÓN: Necesito poder reconocer la mayor cantidad posible de maneras de representar una fecha. Por supuesto, la solución ingenua podría ser escribir una instrucción if para cada formato concebible, pero un enfoque de reconocimiento de patrones , con un modelo entrenado, es idealmente lo que busco.

+0

He eliminado mi respuesta después (* suspiro *) en realidad la lectura de la documentación DateFormat :) – Dave

+0

¡Oh! ¿Y echaron un vistazo a Calendar y SimpleDateFormat y a los métodos de fecha en desuso y .... :-) –

+2

Si está buscando reconocer fechas de todas las configuraciones regionales, no se olvide de tener en cuenta diferentes caracteres de separación, como en 30.12 .2010 y 2010 年 12 月 30 日 – oksayt

Respuesta

5

Uso JChronic

Es posible que desee utilizar DateParser2 del paquete edu.mit.broad.genome.utils.

+0

¿Hay una descarga para DateParser2? – Joel

+0

Parece ser parte de una base de código de análisis genómico completo. Hay un enlace de descarga disponible en la página de inicio (http://www.broadinstitute.org/gsea/index.jsp) pero requiere una inscripción gratuita primero. – corriganjc

+0

@Puspendu: Intentó JChronic. Se ve bastante bien. – Joel

0

Por lo general, las fechas son caracteres separados por una barra invertida/anterior o una raya. ¿Consideraste una expresión regular?

estoy asumiendo que usted no está buscando para clasificar fechas del domingo Tipo, 3 ª octubre de 2010 y así sucesivamente

+0

Sí, yo a.m. CUALQUIER formato de fecha. – Joel

+0

Usted está inusualmente equivocado. Hay todo un mundo afuera y me temo que la mayoría de los países no usa barras como separador de fechas. –

0

No sé de cualquier biblioteca que puede hacer esto, pero la escritura de su propia no estaría muy difícil. Asumiendo que todas las fechas estén formateadas con barras como 12/12/12, entonces puede verificar que tiene tres '\'. Puede obtener aún más técnico y hacer que verifique los valores entre las barras. Por ejemplo, si usted tiene:

30/12/10

entonces usted sabe que 30 es el día y 12 es el mes. Sin embargo, si obtiene el 30/30/10, sabrá que aunque tiene el formato correcto, no puede ser una fecha porque no hay '30' meses.

1

Quizás debería usar expresiones regulares?

Esperamos que esto se podría trabajar para el formato dd-mm-aaaa:

^(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.](19|20)\d\d$

Aquí (0[1-9]|1[012]) coincide con el mes 00..12, (0[1-9]|[12][0-9]|3[01]) coincide con una fecha de 00..31 y (19|20)\d\d partidos al año.

Los campos pueden ser eliminados por guiones, barras o un punto.

Saludos, Serge

+0

Hay muchas maneras de representar una fecha. Aunque podría usar heurísticas simples, un clasificador podría ser más robusto. Necesito reconocer CUALQUIER formato de fecha. – Joel

+0

@Joel, entonces tal vez puedas dividir la cadena usando [- /.] regex y luego asegúrese de que tenga 3 campos y cada uno de ellos calcula una de las expresiones para la fecha (de 0 a 30), mes (de 0 a 12) y año (19xx/20xx o solo xx)? – zserge

+0

Sí, parece ser un buen enfoque: dividir en cualquier carácter no alfanumérico y luego probar cada campo de forma independiente y asegurarse de que tiene al menos un candidato para cada mes, día y año. – Joel

0

no sé de cualquier biblioteca que hace esto tampoco. Sugeriría una combinación de funciones recursivas anidadas y expresiones regulares (mucho) para hacer coincidir cadenas y tratar de llegar a una mejor estimación para ver si puede ser una fecha.Las fechas se pueden escribir de muchas maneras diferentes, algunas personas pueden escribirlas como "Domingo, 3 de octubre de 2010" o "Domingo, 3 de octubre de 2010" o "10/03/2010" o "10/3/2010" y un montón de maneras diferentes (incluso más si está considerando fechas en otros idiomas/culturas).

0

Siempre puede verificar si hay dos caracteres '/' en una cadena.

public static boolean isDate(){ 
    String date = "12/25/2010"; 
    int counter = 0; 
    for(int i=0; i<date.length(); i++){ 
      if ("\/-.".indexOf(date.charAt(i)) != -1) //Any symbol can be used. 
       counter++; 
    } 
    if(counter == 2) //If there are two symbols in the string, 
      return true; //Return true. 
    else 
      return false; 
} 

Puede hacer algo similar para comprobar si todo lo demás es un número entero.

+1

Los europeos usan puntos en lugar de barras, por lo general. –

1

Es virtualmente imposible reconocer todos los formatos de fecha posibles como fechas usando algoritmos "estándar". Eso es solo porque hay muchos de ellos.

Nosotros, los humanos, somos capaces de hacer eso solo porque aprendimos que algo así como 2010-03-31 se asemeja a la fecha. En otras palabras, sugeriría usar algoritmos de Machine Learning y enseñarle a su programa a reconocer secuencias de fechas válidas. Con Google Prediction API que debería ser factible.

O puede usar expresiones regulares como se sugiere arriba, para detectar algunos, pero no todos, los formatos de fecha.

+0

¡También creo que los falsos positivos serán un gran problema! p.ej. con un puntaje del 10 de enero rankeado el 2 de mayo 8 y el 3 de junio 7. –

2

Estoy seguro de que los investigadores en information extraction han examinado este problema, pero no he podido encontrar un documento.

Una cosa que puedes probar es hacerlo como un proceso de dos pasos. (1) después de recopilar tantos datos como sea posible, extraiga las características, algunas características que le vienen a la mente: cantidad de números que aparecen en la cadena, cantidad de números del 1 al 31 que aparecen en la cadena, cantidad de números del 1 12 que aparecen en la cadena, la cantidad de meses que aparecen en la cadena, etc. (2) aprenda de las características usando algún tipo de método de clasificación binario (SVM, por ejemplo) y finalmente (3) cuando aparezca una nueva cadena, extraiga las características y consulte el SVM para una predicción.

+0

+1, una SVM podría ser una herramienta de aprendizaje razonable. – Joel

5

puede recorrer todos los formatos de fecha disponibles en Java:

for (Locale locale : DateFormat.getAvailableLocales()) { 
    for (int style = DateFormat.FULL; style <= DateFormat.SHORT; style ++) { 
     DateFormat df = DateFormat.getDateInstance(style, locale); 
     try { 
       df.parse(dateString); 
       // either return "true", or return the Date obtained Date object 
     } catch (ParseException ex) { 
      continue; // unperasable, try the next one 
     } 
    } 
} 

Sin embargo, esto no va a dar cuenta de cualquier formato de fecha personalizado.

+0

Sí, había considerado esto, pero en última instancia es una lista finita. – Joel

4

lo hice con una enorme expresión regular (auto creado):

public static final String DATE_REGEX = "\b([0-9]{1,2} ?([\\-/\\\\] ?[0-9]{1,2} ?| (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ?)([\\-/\\\\]? ?('?[0-9]{2}|[0-9]{4}))?)\b"; 
public static final Pattern DATE_PATTERN = Pattern.compile(DATE_REGEX, Pattern.CASE_INSENSITIVE); // Case insensitive is to match also "mar" and not only "Mar" for March 

public static boolean containsDate(String str) 
{ 
    Matcher matcher = pattern.matcher(str); 
    return matcher.matches(); 
} 

Esto coincide con las fechas siguientes:

06 Sep 2010 
12-5-2005 
07 Mar 95 
30 DEC '99 
11\9\2001 

Y no esto:

444/11/11 
bla11/11/11 
11/11/11blah 

También coincide con las fechas entre símbolos como [], (), ,:

Yesterday (6 nov 2010) 

Coincide con fechas sin año:

Yesterday, 6 nov, was a rainy day... 

Pero coincide:

86-44/1234 
00-00-0000 
11\11/11 

Y esto no se ve ya no más como una cita. Pero esto es algo que puede resolver comprobando si los números son valores posibles para un mes, día, año.

1

Lo que haría es buscar las características de fecha, en lugar de las fechas. Por ejemplo, puede buscar barras inclinadas, (para obtener fechas del formulario 1/1/1001), guiones (1 - 1 - 1001), nombres de los meses y abreviaturas (1 de enero de 1001 o 1 de enero de 1001). Cuando obtenga un golpe para estos, recopile las palabras cercanas (2 de cada lado deberían estar bien) y almacénelas en una serie de cadenas. Una vez que haya escaneado todas las entradas, consulte esta matriz de cadenas con una función que profundizará un poco más y extraiga las cadenas de fechas reales, utilizando los métodos que se encuentran aquí. Lo importante es simplemente bajar las fechas generales a un nivel manejable.

5

reglas que pueden ayudarle en su búsqueda:

  1. hacer o encontrar algún tipo de base de datos con palabras conocidas que responden meses. Nombres abreviados y completos, como Jan o January. Durante la búsqueda, debe ser insensible a las mayúsculas y minúsculas, porque fEBruaRy también es un mes, aunque la persona que lo escribió debe haber estado borracha. Si planeas buscar meses que no sean en inglés, también se necesita una base de datos, porque ninguna heurística descubrirá que "Wrzesień" es polaco para septiembre.
  2. Solo para inglés, consulte ordinal numbers y también haga una base de datos para los números del 1 al 31. Estos serán útiles por días y meses. Si desea utilizar este enfoque para otros idiomas, tendrá que hacer su propia investigación.
  3. Una vez más, solo en inglés, busca "Anno Domini" y "Before Christ", es decir, AD y BC respectivamente. También pueden estar en la forma A.D. y B.C.
  4. Con respecto a los números mismos que representarán días, meses y años, debe saber dónde está su límite. ¿Es 0-9999 o más? Es decir, ¿desea buscar fechas que representen años posteriores al año 9999? Si no, entonces las cadenas que tienen 1-4 dígitos consecutivos son buenas conjeturas para un día, mes o año válido.
  5. Los días y meses tienen uno o dos dígitos. Los ceros iniciales son aceptables, por lo que se aceptan cadenas con un formato de 0*, donde * puede ser 1-9.
  6. Los separadores pueden ser complicados, pero si no permite el formato incoherente como 10/20 \ 1999, entonces se ahorrará un montón de dolor. Esto se debe a que 10 * 20 * 1999 puede ser una fecha válida, siendo * normalmente un elemento del conjunto {-,_, ,:,/,\,.,','}, pero es posible que * sea una combinación de 2 o 3 elementos del conjunto mencionado. Una vez más, debes elegir separadores aceptables. 10-20? 1999 puede ser una fecha válida para alguien con un extraño sentido de la elegancia. 20/10/1999 también puede ser una fecha válida, pero 10_/20_/1999 sería muy extraño.
  7. Hay casos sin separador. Por ejemplo: 10 de enero de 1988. Estos casos usan palabras de 1.
  8. Existen casos especiales, como el 28 o el 29 de febrero, dependiendo del año bisiesto. Además, meses con 30 o 31 días.

Creo que estos son suficientes para una clasificación "ingenua", un experto lingüista podría ayudarlo más.

Ahora, una idea para su algoritmo. La velocidad no importa. Puede haber múltiples pases sobre la misma cadena. Optimiza cuando comienza a importar. Cuando dude que ha encontrado una cadena de fecha, guárdela en algún lugar "seguro" en un ListOfPossibleDates y haga un examen una vez más, con reglas más rígidas usando combinaciones de 1. a 8. Cuando cree que una cadena de fecha es válida, aliméntela a la clase Date para ver si es realmente válido.El 32 de marzo de 1999 no es válido, cuando lo convierta a un formato que Date comprenderá.

Un patrón recurrente importante es lookbehind y lookaround. Cuando crea que se encuentra una entidad válida (día, mes, año), tendrá que ver qué hay detrás y después. Un mecanismo basado en pila o recursión puede ayudar aquí.

Pasos:

  1. buscar su cadena de palabras de la regla 1. Si encuentra alguno de ellos, tenga en cuenta que la ubicación. Tenga en cuenta el mes. Ahora, ve algunos personajes detrás y algunos adelante para ver lo que te espera. Si no hay espacios antes y después de su mes, y hay números, como en la regla 7., verifique su validez. Si uno de ellos representa un día (debe ser 0-31) y otro al año (debe ser 0-9999, posiblemente con AD o BC), tiene un candidato. Si hay los mismos separadores antes y después, busque las reglas de 6. Recuerde siempre que debe estar seguro de que existe una combinación válida. entonces, 32 de enero de 1999 no funcionará.
  2. Busque en su cadena otras palabras en inglés, de las reglas 2. y 3. Repita de manera similar al paso 1.
  3. Busque separadores. El espacio vacío será el más complicado. Intenta encontrarlos en pares. Entonces, si tiene una "/" en su cadena, busque otra y vea lo que tienen entremedio. Si encuentra una combinación de separadores, haga lo mismo. Además, use el algoritmo del paso 2.
  4. Busque los dígitos. Los válidos son 0-9999 con ceros a la izquierda permitidos. Si encuentra uno, busque separadores como en el paso 3.

Dado que hay literalmente un sinnúmero de posibilidades, no podrá verlas todas. Una vez que haya encontrado un patrón que cree que podría ocurrir nuevamente, guárdelo en algún lugar y puede usarlo como expresión regular para pasar otras cadenas.

Tomemos el ejemplo, "bla bla bla bla 12 Jan 09 bla bla bla 01/04/10 bla bla bla". Después de extraer la primera fecha, 12 Jan 09, utilice el resto de esa cadena ("bla bla bla 01/04/10 bla bla bla") y aplique todos los pasos anteriores una vez más. De esta manera, estarás seguro de que no te perdiste nada.

Espero que estas sugerencias sean al menos de alguna ayuda. Si no existe una biblioteca para hacer todos estos pasos sucios (y más), entonces tiene un camino difícil por delante. ¡Buena suerte!

3

Muy buena fecha analizador en Java es Natty, puede probarlo here

2

Aquí está un ejemplo sencillo Natty:

import com.joestelmach.natty.*; 

List<Date> dates =new Parser().parse("Start date 11/30/2013 , end date Friday, Sept. 7, 2013").get(0).getDates(); 
     System.out.println(dates.get(0)); 
     System.out.println(dates.get(1)); 

//output: 
     //Sat Nov 30 11:14:30 BDT 2013 
     //Sat Sep 07 11:14:30 BDT 2013 
Cuestiones relacionadas