2011-11-15 8 views
6

J'ai un problème qui semble simple à première vue mais qui a vaincu mes maigres compétences de regex. J'ai une chaîne que j'ai besoin de convertir en un tableau puis de traiter les valeurs en conséquence, ce qui est assez simple, mais le format de la chaîne ne peut pas être changé (il est généré ailleurs) et sa logique me déroute.Comment diviser une chaîne en tableau 2D avec Regex?

La chaîne est:

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

Il est essentiellement un ensemble de valeurs décimales et ids (dans ce cas id == 3 12.00). La quantité d'identifiants peut changer à tout moment et les valeurs décimales peuvent figurer dans tout ou partie des identifiants.

Dans un monde idéal, je devrais le tableau suivant:

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

Est-ce que l'un des assistants vous regex savent comment cela peut être accompli avec moins de jurons que je suis en mesure de réaliser?

J'ai été jusqu'ici en mesure d'extraire les id en utilisant:

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

et les décimaux à l'aide:

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

mais perdre la corrélation entre les id et les valeurs.

+1

vous pouvez résoudre ce problème avec exploser et strstr son meilleur que regex en terme de performance. – shox

Répondre

3

Exemple:

<?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); 

Sortie:

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: Fait et fait. Merci pour la suggestion. :) –

+0

C'est pourquoi j'aime Stack Overflow - qui fonctionne absolument parfait! Merci beaucoup à vous tous! –

+1

@Matthew Chambers: De rien. :) –

1

Si vous êtes satisfait de la liste des deux IDs ou NUMs, alors vous pouvez simplement combiner vos deux rege de travail xes en un seul appel:

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

Cela vous donnera une liste de tableaux associatifs, soit avec id ou num ensemble, si vous utilisez également le drapeau PREG_SET_ORDER.

1

Quelque chose comme ça? Mes compétences PHP sont plutôt faibles, vous devrez donc vérifier comment accéder aux groupes de capture nommés 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]; 
    } 
} 

Comment ça marche:

" 
\[    # 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) 
" 

Il prend également en charge des valeurs négatives.

0

Son pas la __gVirt_NP_NN_NNPS<__ approche regex, mais peut-être cela fonctionne pour vous: (bien sûr, il pourrait être amélioré)

$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++;  
    } 
}