Как разбить строку в 2D массив с помощью Regex? - PullRequest
6 голосов
/ 15 ноября 2011

У меня есть проблема, которая кажется простой на первый взгляд, но победила мои скудные навыки регулярных выражений.У меня есть строка, которую мне нужно преобразовать в массив и затем обработать значения соответствующим образом, что достаточно просто, но формат строки не может быть изменен (он генерируется в другом месте), и логика этого сбила меня с толку.

Строка:

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

Это в основном набор идентификаторов и десятичных значений (в данном случае id 3 == 12.00).Количество идентификаторов может измениться в любой момент, а десятичные значения могут быть в любом или во всех идентификаторах.

В идеальном мире у меня был бы следующий массив:

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

Делать любыеиз вас волшебники регулярных выражений знают, как это можно сделать с меньшими ругательствами, чем я смог достичь?

До сих пор я мог извлечь идентификаторы, используя:

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

идесятичные дроби, использующие:

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

, но теряют корреляцию между идентификаторами и значениями.

Ответы [ 5 ]

3 голосов
/ 15 ноября 2011

Пример:

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

Выход:

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 голос
/ 15 ноября 2011

Как то так? Мои навыки php довольно слабые, поэтому вам придется проверить, как получить доступ к именованным группам захвата 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];
    } 
}

Как это работает:

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

Он также учитывает отрицательные значения.

1 голос
/ 15 ноября 2011

Если вас устраивает список идентификаторов или NUM, то вы можете просто объединить два рабочих регулярных выражения в один вызов:

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

Это даст вам список ассоциативных массивов сid или num, если вы также используете флаг PREG_SET_ORDER.

0 голосов
/ 15 ноября 2011

Это не подход регулярного выражения, но, возможно, он работает для вас: (конечно, он может быть улучшен)

$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++;       
    }
}
0 голосов
/ 15 ноября 2011

Посмотрите на команду php explode - http://php.net/manual/en/function.explode.php

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...