2009-07-05 18 views
7

Hace dos días comencé a trabajar en un analizador de código y estoy atascado.PHP y RegEx: divide una cadena por comas que no están entre paréntesis (y también paréntesis anidados)

¿Cómo puedo dividir una cadena por comas que no están entre paréntesis dentro, te voy a mostrar lo que significa:

que tienen esta cadena para analizar:

one, two, three, (four, (five, six), (ten)), seven 

me gustaría conseguir este resultado:

array(
"one"; 
"two"; 
"three"; 
"(four, (five, six), (ten))"; 
"seven" 
) 

pero en cambio me sale:

array(
    "one"; 
    "two"; 
    "three"; 
    "(four"; 
    "(five"; 
    "six)"; 
    "(ten))"; 
    "seven" 
) 

¿Cómo puedo hacer esto en PHP RegEx.

¡Gracias de antemano!

Respuesta

10

Puede hacerlo más fácil:

preg_match_all('/[^(,\s]+|\([^)]+\)/', $str, $matches) 

Pero sería mejor si utiliza un analizador real. Tal vez algo como esto:

$str = 'one, two, three, (four, (five, six), (ten)), seven'; 
$buffer = ''; 
$stack = array(); 
$depth = 0; 
$len = strlen($str); 
for ($i=0; $i<$len; $i++) { 
    $char = $str[$i]; 
    switch ($char) { 
    case '(': 
     $depth++; 
     break; 
    case ',': 
     if (!$depth) { 
      if ($buffer !== '') { 
       $stack[] = $buffer; 
       $buffer = ''; 
      } 
      continue 2; 
     } 
     break; 
    case ' ': 
     if (!$depth) { 
      continue 2; 
     } 
     break; 
    case ')': 
     if ($depth) { 
      $depth--; 
     } else { 
      $stack[] = $buffer.$char; 
      $buffer = ''; 
      continue 2; 
     } 
     break; 
    } 
    $buffer .= $char; 
} 
if ($buffer !== '') { 
    $stack[] = $buffer; 
} 
var_dump($stack); 
+0

Sí, es más fácil, pero no funciona en el caso de los paréntesis anidados, así: uno, dos, tres, (cuatro, (cinco, seis), (diez)), siete –

+0

Ese es el punto en el que debe usar un analizador real Las expresiones regulares no pueden contar ni manejar estados. – Gumbo

+0

Tengo que usar expresiones regulares. Las expresiones regulares son recursivas y codiciosas, puedes lograrlo usándolas. –

1

me temo que podría ser muy difícil de analizar corchetes anidados como one, two, (three, (four, five)) sólo con la expresión regular.

5

No se puede, directamente. Necesitarías, como mínimo, una apariencia de ancho variable y, por último, sabía que la PCRE de PHP solo tenía una apariencia de ancho fijo.

Mi primera recomendación sería primero extraer expresiones entre paréntesis de la cadena. Sin embargo, no sé nada acerca de su problema real, así que no sé si eso será factible.

+0

Sí, ese fue el truco que estaba planeando usar. Reemplace los soportes con $ 1, $ 2 o algo similar, divida la cadena y luego restaure los corchetes en el resultado. Gracias ! –

+0

El punto es que lo que describes no es un idioma normal, por lo que las expresiones regulares son una mala opción. Por lo tanto, analizar primero todas las partes anidadas no es un "truco", sino lo más sensato. – Svante

2

No puedo pensar en una manera de hacerlo utilizando una sola expresión regular, pero es muy fácil de hackear juntos algo que funciona:

function process($data) 
{ 
     $entries = array(); 
     $filteredData = $data; 
     if (preg_match_all("/\(([^)]*)\)/", $data, $matches)) { 
       $entries = $matches[0]; 
       $filteredData = preg_replace("/\(([^)]*)\)/", "-placeholder-", $data); 
     } 

     $arr = array_map("trim", explode(",", $filteredData)); 

     if (!$entries) { 
       return $arr; 
     } 

     $j = 0; 
     foreach ($arr as $i => $entry) { 
       if ($entry != "-placeholder-") { 
         continue; 
       } 

       $arr[$i] = $entries[$j]; 
       $j++; 
     } 

     return $arr; 
} 

Si se invoca así:

$data = "one, two, three, (four, five, six), seven, (eight, nine)"; 
print_r(process($data)); 

genera:

Array 
(
    [0] => one 
    [1] => two 
    [2] => three 
    [3] => (four, five, six) 
    [4] => seven 
    [5] => (eight, nine) 
) 
+0

Muchas gracias, esto debería funcionar. Así fue como planeé hacerlo primero, pero pensé que existe una manera más fácil. –

+0

Tu método no puede analizar "uno, dos, tres, ((cinco), (cuatro (seis))), siete, ocho, nueve". Creo que el RegEx correcto sería recursivo:/\ (([^()] + | (? R)) * \) /. –

+0

No mencionaste que tenía que ser capaz de analizar expresiones recursivas cuando primero escribí esta respuesta. Sin embargo, otros definitivamente sugirieron mejores soluciones después de que escribí esto. –

2

torpe, pero hace el trabajo ...

<?php 

function split_by_commas($string) { 
    preg_match_all("/\(.+?\)/", $string, $result); 
    $problem_children = $result[0]; 
    $i = 0; 
    $temp = array(); 
    foreach ($problem_children as $submatch) { 
    $marker = '__'.$i++.'__'; 
    $temp[$marker] = $submatch; 
    $string = str_replace($submatch, $marker, $string); 
    } 
    $result = explode(",", $string); 
    foreach ($result as $key => $item) { 
    $item = trim($item); 
    $result[$key] = isset($temp[$item])?$temp[$item]:$item; 
    } 
    return $result; 
} 


$test = "one, two, three, (four, five, six), seven, (eight, nine), ten"; 

print_r(split_by_commas($test)); 

?> 
7

Hm ... OK ya marcadas como respondida, pero desde que solicitó una solución fácil, sin embargo, voy a tratar:

<?php 
    $test = "one, two, three, , , ,(four, five, six), seven, (eight, nine)"; 
    $split = "/([(].*?[)])|(\w)+/"; 
    preg_match_all($split, $test, $out); 
    print_r($out[0]);    
    die(); 
?> 

salida

Array 
(
    [0] => one 
    [1] => two 
    [2] => three 
    [3] => (four, five, six) 
    [4] => seven 
    [5] => (eight, nine) 
) 
+0

Muchas gracias, su ayuda es muy apreciada. Pero ahora me doy cuenta de que también encontraré corchetes anidados y su solución no se aplica. –

1

Creo que vale la pena señalar, que siempre debe evitar las expresiones regulares cuando sea posible. Para ello, debe saber que para PHP 5.3+ puede usar str_getcsv(). Sin embargo, si está trabajando con archivos (o secuencias de archivos), como archivos CSV, la función fgetcsv() puede ser lo que necesita, y está disponible desde PHP4.

Por último, estoy sorprendido de que nadie haya usado preg_split(), ¿o no funcionó según era necesario?

+0

Sí ken, quiero usar preg_split(), pero ¿cuál sería el RegEx que ignora las comas entre paréntesis? –

+0

Ah, sí, buen punto, después de intentar durante un minuto o 2 puedo ver que es un desafío con las condiciones establecidas. – ken

+0

Sí, tienes razón, también probé tu solución y no funciona. Gracias todavía –

Cuestiones relacionadas