2010-06-16 9 views
21

Dada una cadena arbitraria, por ejemplo ("I'm going to play croquet next Friday" o "Gadzooks, is it 17th June already?"), ¿cómo harías para extraer las fechas desde allí?Analizando una cadena para las fechas en PHP

Si esto se ve como un buen candidato para la cesta demasiado dura, quizás podría sugerir una alternativa. Quiero ser capaz de analizar los mensajes de Twitter para las fechas. Los tweets que estaría viendo serían los que los usuarios dirigen en este servicio, por lo que podrían ser entrenados para usar un formato más fácil, sin embargo me gustaría que sea lo más transparente posible. ¿Hay un buen término medio que se te ocurra?

+1

Sólo para personas de habla inglesa? –

+0

@Serty, sí. __ – nickf

Respuesta

11

Si tiene caballos de fuerza, puede probar con el siguiente algoritmo. Estoy mostrando un ejemplo, y dejando el trabajo tedioso hasta que :)

//Attempt to perform strtotime() on each contiguous subset of words... 

//1st iteration 
strtotime("Gadzooks, is it 17th June already") 
strtotime("is it 17th June already") 
strtotime("it 17th June already") 
strtotime("17th June already") 
strtotime("June already") 
strtotime("already") 

//2nd iteration 
strtotime("Gadzooks, is it 17th June") 
strtotime("is it 17th June") 
strtotime("17th June") //date! 
strtotime("June") //date! 

//3rd iteration 
strtotime("Gadzooks, is it 17th") 
strtotime("is it 17th") 
strtotime("it 17th") 
strtotime("17th") //date! 

//4th iteration 
strtotime("Gadzooks, is it") 
//etc 

y podemos asumir que strtotime("17th June") es más preciso que strtotime("17th") simplemente porque contiene más palabras ... es decir, "el próximo viernes" voluntad siempre será más preciso que "viernes".

2

Utilice la función strtotime php.

Por supuesto, necesitaría configurar algunas reglas para analizarlas, ya que debe deshacerse de todo el contenido adicional de la cadena, pero aparte de eso, es una función muy flexible que probablemente lo ayude. aquí.

Por ejemplo, puede tomar cadenas como "el próximo viernes" y "15 de junio" y devolver la indicación de fecha y hora de UNIX apropiada para la fecha en la cadena. Supongo que si consideras algunas reglas básicas como buscar "next X" y los nombres de semana y mes, podrás hacer esto.

Si se pudiera localizar el "próximo viernes " de la "Voy a jugar al croquet próximo viernes" se podía extraer de la fecha. ¡Parece un proyecto divertido de hacer! Pero tenga en cuenta que strtotime solo toma frases en inglés y no funcionará con ningún otro idioma.

Por ejemplo, una regla que localizar todos los casos "Siguiente semana" sería tan simple como:

$datestring = "I'm going to play croquet next Friday"; 

$weekdays = array('monday','tuesday','wednesday', 
        'thursday','friday','saturday','sunday'); 

foreach($weekdays as $weekday){ 
    if(strpos(strtolower($datestring),"next ".$weekday) !== false){ 
     echo date("F j, Y, g:i a",strtotime("next ".$weekday)); 
    } 
} 

Esto devolverá la fecha del siguiente día hábil mencionado en la cuerda, siempre y cuando se sigue ¡la regla! En este caso particular, la salida fue June 18, 2010, 12:00 am. Con algunas (¡quizás más que algunas!) De esas reglas, es muy probable que extraigas la fecha correcta en un alto porcentaje de los casos, considerando que los usuarios usan la ortografía correcta.

Como se ha señalado, con expresiones regulares y un poco de paciencia puede hacerlo. La parte más difícil de la codificación es decidir de qué manera abordará su problema, sin codificarlo una vez que sepa qué.

+1

No estoy seguro de cuán factible es eso. Ver http://de3.php.net/manual/en/datetime.formats.php para los formatos de entrada permitidos. Deshacerse de las partes incompatibles de la cadena parece no trivial. – Gordon

+0

Nunca dije que fuera trivial, pero es un enfoque. Establecer algunas reglas que extraerán la fecha válida no es imposible. Tomaría algún tiempo con seguridad, pero es factible. Sería mucho más fácil si las fechas siguieran un cierto patrón, pero dado que es arbitrario, no puedo pensar en un mejor enfoque. ¡Estaría más que satisfecho de ver otra solución! –

+0

@Gordon, exactamente. Me pregunto acerca de cualquier enfoque interesante para aislar la parte de fecha que luego podría analizar con strtotime. – nickf

1

algo como lo siguiente podría hacerlo:

$months = array(
        "01" => "January", 
        "02" => "Feberuary", 
        "03" => "March", 
        "04" => "April", 
        "05" => "May", 
        "06" => "June", 
        "07" => "July", 
        "08" => "August", 
        "09" => "September", 
        "10" => "October", 
        "11" => "November", 
        "12" => "December" 
       ); 

$weekDays = array(
        "01" => "Monday", 
        "02" => "Tuesday", 
        "03" => "Wednesday", 
        "04" => "Thursday", 
        "05" => "Friday", 
        "06" => "Saturday", 
        "07" => "Sunday" 
       ); 

foreach($months as $value){ 
    if(strpos(strtolower($string),strtolower($value))){ 
     \\ extract and assign as you like... 
    } 
} 

probable que un bucle tro para comprobar si hay otros días de la semana o en otros formatos, o simplemente nido.

6

lo haría de esta manera:

En primer lugar comprobar si toda la cadena es una fecha válida con strtotime(). Si es así, has terminado.

De no ser así, determine cuántas palabras hay en su cadena (dividida en espacios en blanco, por ejemplo). Deje que este número sea n.

Pasa el cursor sobre cada combinación de palabras n-1 y usa strtotime() para ver si la frase es una fecha válida. Si es así, has encontrado la cadena de fecha válida más larga dentro de tu cadena original.

Si no, repita cada combinación de palabras n-2 y use strtotime() para ver si la frase es una fecha válida. Si es así, has encontrado la cadena de fecha válida más larga dentro de tu cadena original.

... y así sucesivamente hasta que encuentre una cadena de fecha válida o busque cada palabra individual o individual. Al encontrar las coincidencias más largas, obtendrá las fechas más informadas (si tiene sentido). Como se trata de tweets, tus cadenas nunca serán enormes.

+0

Esta es definitivamente una manera fácil de empezar. La complejidad del tiempo es bastante atroz, así que ten cuidado. Después de unos pocos miles de caracteres, la complejidad se reduce a O (n^3). En la marca de 140 caracteres, los ahorros del n-1 tienen un efecto más significativo, pero aún superan O (n^2). – erisco

+1

@erisco: De acuerdo. No procesaría un libro de esta manera. Sin embargo, un tweet nunca debe tener más de 70 palabras, y generalmente no más de 25, por lo que n seguirá siendo bastante pequeño. Para optimizar aún más, puede decidir que ninguna fecha estará compuesta por más de siete palabras, por ejemplo: 'Jueves, 17 de junio de 2010 a las 9:00 a.m.' Entonces, en lugar de comenzar con n-1, puedes contar hacia abajo desde siete. –

1

La mayoría de los algoritmos sugeridos son de hecho bastante cojos. Sugiero usar algunas buenas expresiones regulares para las fechas y probar la oración con ellas. Use esto como un ejemplo:

(\d{1,2})? 
((mon|tue|wed|thu|fri|sat|sun)|(monday|tuesday|wednesday|thursday|friday|saturday|sunday))? 
(\d{1,2})? (\d{2,4})? 

Me salté meses, ya que no estoy seguro de recordarlos en el orden correcto.

Esta es la solución más fácil, pero haré el trabajo mejor que otras soluciones basadas en la potencia de cómputo. (Y sí, difícilmente es una expresión regular a prueba de fallos, pero entiendes el punto). A continuación, aplique la función de tiempo de recorrido en la secuencia coincidente. Esta es la solución más simple y rápida.

1

Lo que estás buscando es un analizador de expresiones temporales. Puede consultar the Wikipedia article para comenzar. Tenga en cuenta que los analizadores pueden ser bastante complicados, porque esto realmente es un problema de reconocimiento de lenguaje. Ese es un problema comúnmente abordado por el campo de inteligencia artificial/lingüística computacional.

2

Siguiendo Dolph Mathews idea y básicamente ignorando mi respuesta anterior, construí una función bastante buena que hace exactamente eso. Devuelve la cadena que cree que coincide con una fecha, el marcador de fecha de Unix y la fecha misma con el formato especificado por el usuario o el predefinido (F j, Y). Escribí una pequeña publicación al respecto en Extracting a date from a string with PHP. Como un adelanto, aquí está la salida de las dos cadenas Ejemplo:

entrada: “Voy a jugar al croquet próximo viernes”

Output: Array ( 
      [string] => "next friday", 
      [unix] => 1276844400, 
      [date] => "June 18, 2010" 
     ) 

entrada: “Gadzooks, es que ya 17a junio?”

Output: Array ( 
      [string] => "17th june", 
      [unix] => 1276758000, 
      [date] => "June 17, 2010" 
     ) 

espero que ayude a alguien.

+2

Parece que su enlace está muerto. ¿Todavía tienes ese ejemplo? –

2

Basado en sugerencia de Dolph, escribí una función que creo sirve al propósito.

public function parse_date($text, $offset, $length){ 

    $parseArray = preg_split("/[\s,.]/", $text); 
    $dateTest = implode(" ", array_slice($parseArray, $offset, $length == 0 ? null : $length)); 

    $date = strtotime($dateTest); 

    if ($date){ 
    return $date; 
    } 

    //make the string one word shorter in the front 
    $offset++; 

    //have we reached the end of the array? 
    if($offset > count($parseArray)){ 

    //reset the start of the string 
    $offset = 0; 

    //trim the end by one 
    $length--; 

    //reached the very bottom with no date found 
    if(abs($length) >= count($parseArray)){ 
     return false; 
    } 
    } 

    //try to find the date with the new substring 
    return $this->parse_date($text, $offset, $length); 
} 

Se podría llamar así:

parse_date ('Ajuste de la fecha de vencimiento en enero del 2017 5 ª empresa', 0, 0)