2011-11-15 21 views
6

Tengo un problema que parece simple a primera vista, pero que ha derrotado mis escasas habilidades en expresiones regulares. Tengo una cadena que necesito convertir a una matriz y luego procesar los valores en consecuencia, lo cual es bastante simple, pero el formato de la cadena no se puede cambiar (se genera en otro lugar) y su lógica me tiene desconcertado.Cómo dividir una cadena en una matriz 2D con Regex?

La cadena es:

[6] [2] [3] 12.00; [5] [4] 

Se trata básicamente de un conjunto de identificadores y valores decimales (en este caso 3 Identificación del == 12.00). La cantidad de identificadores podría cambiar en cualquier momento y los valores decimales podrían estar en alguno o todos los ID.

En un mundo ideal tendría la siguiente matriz:

Array (
    [0] => Array (
      [id] => 6 
      [num] => 
     ) 
    [1] => Array (
      [id] => 2 
      [num] => 
     ) 
    [2] => Array (
      [id] => 3 
      [num] => 12.00 
     ) 
    Etc... 

¿Alguno de ustedes asistentes de expresiones regulares saben cómo esto se puede lograr con menos de juramento lo que he sido capaz de lograr?

hasta ahora he sido capaz de extraer los identificadores utilizando:

preg_match_all('@\[(.*?)\]@s', $string, $array); 

y los decimales usando:

preg_match_all('/([0-9]+[,\.]{1}[0-9]{2})/', $string, $array); 

pero pierden la correlación entre los valores de y de identificación.

+1

puede resolver este problema y explotar strstr es mejor que la expresión regular en términos de rendimiento. – shox

Respuesta

3

Ejemplo:

<?php 

$string = '[6] [2] [3] 12.00; [5] [4]'; 

preg_match_all('/\[(?P<id>\d+)\](?: (?P<num>[\d\.]+);)?/', $string, $matches, PREG_SET_ORDER); 

var_dump($matches); 

Salida:

array(5) { 
    [0]=> 
    array(3) { 
    [0]=> 
    string(3) "[6]" 
    ["id"]=> 
    string(1) "6" 
    [1]=> 
    string(1) "6" 
    } 
    [1]=> 
    array(3) { 
    [0]=> 
    string(3) "[2]" 
    ["id"]=> 
    string(1) "2" 
    [1]=> 
    string(1) "2" 
    } 
    [2]=> 
    array(5) { 
    [0]=> 
    string(10) "[3] 12.00;" 
    ["id"]=> 
    string(1) "3" 
    [1]=> 
    string(1) "3" 
    ["num"]=> 
    string(5) "12.00" 
    [2]=> 
    string(5) "12.00" 
    } 
    [3]=> 
    array(3) { 
    [0]=> 
    string(3) "[5]" 
    ["id"]=> 
    string(1) "5" 
    [1]=> 
    string(1) "5" 
    } 
    [4]=> 
    array(3) { 
    [0]=> 
    string(3) "[4]" 
    ["id"]=> 
    string(1) "4" 
    [1]=> 
    string(1) "4" 
    } 
} 
+1

@Gordon: Hecho y hecho. Gracias por la sugerencia. :) –

+0

Es por eso que amo Stack Overflow, ¡eso funciona absolutamente perfecto! ¡Muchas gracias a todos! –

+1

@Matthew Chambers: De nada. :) –

1

Si está satisfecho con una lista de cualquiera IDs o NUMs, entonces podrías combinar tus dos respuestas de trabajo jos en una sola llamada:

preg_match_all('@ \[(?P<id> \d+)] | (?P<num> [\d,.]+) @xs', 
     $string, $array, PREG_SET_ORDER); 

Esto le dará una lista de matrices asociativas, ya sea con o idnum conjunto, si también está usando la bandera PREG_SET_ORDER.

1

¿Algo como esto? Mis habilidades de php son bastante débiles, así que tendrás que comprobar cómo acceder a los grupos de captura nombrados id/num.

preg_match_all('/\[(?P<id>\d+)\]\s*(?P<num>[-+]?\b[0-9]+(?:\.[0-9]+)?\b)?/', $subject, $result, PREG_SET_ORDER); 
for ($matchi = 0; $matchi < count($result); $matchi++) { 
    for ($backrefi = 0; $backrefi < count($result[$matchi]); $backrefi++) { 
     # Matched text = $result[$matchi][$backrefi]; 
    } 
} 

Cómo funciona:

" 
\[    # Match the character “[” literally 
(?<id>   # Match the regular expression below and capture its match into backreference with name “id” 
    \d    # Match a single digit 0..9 
     +    # Between one and unlimited times, as many times as possible, giving back as needed (greedy) 
) 
]    # Match the character “]” literally 
\s    # Match a single character that is a “whitespace character” (spaces, tabs, line breaks, etc.) 
    *    # Between zero and unlimited times, as many times as possible, giving back as needed (greedy) 
(?<num>  # Match the regular expression below and capture its match into backreference with name “num” 
    [-+]   # Match a single character present in the list “-+” 
     ?    # Between zero and one times, as many times as possible, giving back as needed (greedy) 
    \b    # Assert position at a word boundary 
    [0-9]   # Match a single character in the range between “0” and “9” 
     +    # Between one and unlimited times, as many times as possible, giving back as needed (greedy) 
    (?:   # Match the regular expression below 
     \.    # Match the character “.” literally 
     [0-9]   # Match a single character in the range between “0” and “9” 
     +    # Between one and unlimited times, as many times as possible, giving back as needed (greedy) 
    )?    # Between zero and one times, as many times as possible, giving back as needed (greedy) 
    \b    # Assert position at a word boundary 
)?    # Between zero and one times, as many times as possible, giving back as needed (greedy) 
" 

También se encarga de valores negativos.

0

no su enfoque de la expresión regular, pero puede que funciona para usted: (por supuesto que se podría mejorar)

$str = "[6] [2] [3] 12.00; [5] [4]"; 
$str = str_replace(array('[',']'), '', $str); 

$arr = explode(' ', $str); 
$array = array(); 
for($i=0 ; $i < count($arr) ; $i++) 
{ 
    $isValue = strpos($arr[$i], '.'); 
    if($isValue !== false){ 
     continue; 
    } 

    $key = $arr[$i]; 
    $ret = array('id' => $key , 'num' => ''); 

    $nextIsFloat = strstr($arr[$i+1], ';', TRUE); 
    if(!$nextIsFloat){ 
     $array[] = $ret;   
     continue; 
    }else{ 
     $ret['num'] = $nextIsFloat; 
     $array[] = $ret; 
     $i++;  
    } 
}