Pregunta

Estoy tratando de dividir el texto en palabras:

$delimiterList = array(" ", ".", "-", ",", ";", "_", ":",
           "!", "?", "/", "(", ")", "[", "]", "{", "}", "<", ">", "\r", "\n",
           '"');
$words = mb_split($delimiterList, $string);

que funciona bastante bien con cadenas pero estoy atascado en algunos casos en los que tengo que ver con números.

Por ejemplo. Si tengo el texto "Mire esto. Mi puntaje es 3.14, y estoy feliz por eso". Ahora la matriz es

[0]=>Look,
[1]=>at,
[2]=>this,
[3]=>My,
[4]=>score,
[5]=>is,
[6]=>3,
[7]=>14,
[8]=>and, ....

Entonces también el 3.14 se divide en 3 y 14, lo que no debería suceder en mi caso. Quiero decir que el punto debe dividir dos cadenas pero no dos números. Debería ser como:

[0]=>Look,
[1]=>at,
[2]=>this,
[3]=>My,
[4]=>score,
[5]=>is,
[6]=>3.14,
[7]=>and, ....

¡Pero no tengo idea de cómo evitar estos casos!

¿Alguien tiene alguna idea de cómo resolver este problema?

Gracias, Granit

¿Fue útil?

Solución

O usa regex :)

<?php
$str = "Look at this.My score is 3.14, and I am happy about it.";

// alternative to handle Marko's example (updated)
// /([\s_;?!\/\(\)\[\]{}<>\r\n"]|\.$|(?<=\D)[:,.\-]|[:,.\-](?=\D))/

var_dump(preg_split('/([\s\-_,:;?!\/\(\)\[\]{}<>\r\n"]|(?<!\d)\.(?!\d))/',
                    $str, null, PREG_SPLIT_NO_EMPTY));

array(13) {
  [0]=>
  string(4) "Look"
  [1]=>
  string(2) "at"
  [2]=>
  string(4) "this"
  [3]=>
  string(2) "My"
  [4]=>
  string(5) "score"
  [5]=>
  string(2) "is"
  [6]=>
  string(4) "3.14"
  [7]=>
  string(3) "and"
  [8]=>
  string(1) "I"
  [9]=>
  string(2) "am"
  [10]=>
  string(5) "happy"
  [11]=>
  string(5) "about"
  [12]=>
  string(2) "it"
}

Otros consejos

Eche un vistazo a strtok . Le permite cambiar los tokens de análisis dinámicamente, por lo que puede separar la cadena manualmente en un bucle while, empujando cada palabra dividida en una matriz.

Mi primera idea fue preg_match_all ('/ \ w + /', $ string, $ coincidencias); pero eso da un resultado similar al que tienes. El problema es que los números separados por un punto son muy ambiguos. Puede significar tanto el punto decimal como el final de la oración, por lo que necesitamos una forma de cambiar la cadena para eliminar el doble significado.

Por ejemplo, en esta oración tenemos varias partes que nos gustaría mantener como una sola palabra: " Mira esto. Mi puntaje es 3.14, y estoy feliz por eso. No es 334,3 y hoy no es 2009-12-12 11: 12: 13. " .

Comenzamos construyendo un diccionario de búsqueda y reemplazo para codificar las excepciones en algo que no se va a dividir:

$encode = array(
    '/(\d+?)\.(\d+?)/' => '\\1DOT\\2',
    '/(\d+?),(\d+?)/' => '\\1COMMA\\2',
    '/(\d+?)-(\d+?)-(\d+?) (\d+?):(\d+?):(\d+?)/' => '\\1DASH\\2DASH\\3SPACE\\4COLON\\5COLON\\6'
);

A continuación, codificamos las excepciones:

foreach ($encode as $regex => $repl) {
    $string = preg_replace($regex, $repl, $string);
}

Dividir la cadena:

preg_match_all('/\w+/', $string, $matches);

Y convierta la palabra codificada de nuevo:

$decode = array(
    'search' =>  array('DOT', 'COMMA', 'DASH', 'SPACE', 'COLON'),
    'replace' => array('.',   ',',     '-',    ' ',     ':'    )
);
foreach ($matches as $k => $v) {
    $matches[$k] = str_replace($decode['search'], $decode['replace'], $v);
}

$ coincide ahora contiene la oración original dividida en palabras con las excepciones correctas.

Puede hacer que la expresión regular utilizada en las excepciones sea tan simple o tan compleja como desee, pero siempre habrá alguna ambigüedad, por ejemplo, dos sesiones con la primera terminando y la siguiente comenzando con un número: El número de conteo será solo 3.3 y nada más que el 3.5 está listo.

Use " ;. " ;, en lugar de ". " ;, en $ delimiterList .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top