PHP разбивает строку с разделителями на пары ключ / значение (ассоциативный массив) - PullRequest
9 голосов
/ 13 марта 2011

У меня есть такая строка:

key1\value1\key2\value2\key3\value3\key4\value4\key5\value5

И я бы хотел, чтобы это был ассоциативный массив, чтобы я мог сделать:

echo $myArray['key1']; // prints value1
echo $myArray['key3']; // prints value3
//etc...

Я знаю, что могу взорватьсяна обратную косую черту, но не уверен, как идти оттуда.

Ответы [ 3 ]

17 голосов
/ 13 марта 2011

Использование простого регулярного выражения через preg_match_all и array_combine часто является самым коротким и быстрым вариантом:

 preg_match_all("/([^\\\\]+)\\\\([^\\\\]+)/", $string, $p);
 $array = array_combine($p[1], $p[2]);

Теперь это, конечно, особый случай. Обе клавиши и значения разделены обратной косой чертой \ , как и все их пары. Регулярное выражение также немного длиннее из-за необходимости двойного экранирования.

Однако эту схему можно обобщить для других строк key:value,.

Отдельные key:value, разделители

Обычные варианты включают : и = в качестве разделителей ключа / значения, а также , или & и другие в качестве разделителей пар. В таких случаях регулярное выражение становится достаточно очевидным (с флагом /x для удобства чтения):

 #                    ↓    ↓    ↓
 preg_match_all("/ ([^:]+) : ([^,]+) /x", $string, $p);
 $array = array_combine($p[1], $p[2]);

Что делает супер легким обмен : и , на другие разделители.

  • Знаки равенства = вместо : двоеточия.
  • Например, \\t как разделитель пар (разделенный табуляцией ключ: списки значений)
  • Классический & или ; в качестве разделителя между парами ключ = значение.
  • Или просто \\s пробелы или \\n переводы строк даже.

Разрешить различные разделители

Вы можете сделать его более гибким / прощающим, разрешив разные разделители между ключами / значениями / парами:

 #                    ↓      ↓       ↓
 preg_match_all("/ ([^:=]+) [:=]+ ([^,+&]+) /x", $string, $p);

Где оба key=value,key2:value2++key3==value3 будут работать. Что может иметь смысл для большего количества людей, дружественных (АКА нетехнических пользователей).

Ограничить буквенно-цифровые клавиши

Часто вы можете запретить что-либо, кроме классических key идентификаторов. Просто используйте шаблон строки слова \w+, чтобы регулярное выражение пропускало нежелательные случаи:

 #                   ↓   ↓    ↓
 preg_match_all("/ (\w+) = ([^,]+) /x", $string, $p);

Это самый тривиальный подход к созданию белого списка. Если OTOH вы хотите заявить / заранее ограничить всю строку ключ / значение, то создайте отдельную preg_match("/^(\w+=[^,]+(,|$))+/", …

Пробелы в скобках или цитирование

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

 preg_match_all("/ \s*([^=]+) \s*=\s* ([^,]+) (?<!\s) /x", $string, $p);

Или, например, необязательные кавычки:

 preg_match_all("/ \s*([^=]+) \s*=\s* '? ([^,]+) (?<![\s']) /x", $string, $p);

INI-стиль извлечения

И вы можете создать базовый метод извлечения INI-файла:

 preg_match_all("/^ \s*(\w+) \s*=\s* ['\"]?(.+?)['\"]? \s* $/xm", $string, $p);

Обратите внимание, что это всего лишь сырое подмножество распространенных схем INI.

Альтернатива: parse_str()

Если у вас уже есть строка key=value&key2=value2, то parse_str работает как брелок. Но, комбинируя его с strtr, можно даже обрабатывать различные другие разделители:

 #                         ↓↓    ↑↑
 parse_str(strtr($string, ":,", "=&"), $pairs);

У которого есть пара плюсов и минусов собственных:

  • Даже короче, чем метод регулярных выражений с двумя строками.
  • Предопределяет хорошо известный механизм экранирования, такой как %2F для специальных символов).
  • Не допускает использование разделителей или неэкранированных разделителей внутри.
  • Автоматически преобразует keys[]= в массивы, которые вы можете или не можете хотеть.

Альтернатива: explode + foreach

Вы найдете много примеров ручного расширения ключа / строки значения . Хотя это часто больше кода. explode несколько чрезмерно используется в PHP из-за предположений об оптимизации. Однако после профилирования оно оказывается медленнее из-за руководства foreach и коллекции массивов.

6 голосов
/ 13 марта 2011

Что-то вроде этого:

$str = 'key1\value1\key2\value2\key3\value3\key4\value4\key5\value5';
$list = explode('\\', $str);

$result = array();
for ($i=0 ; $i<count($list) ; $i+=2) {
    $result[ $list[$i] ] = $list[$i+1];
}

var_dump($result);

Что бы вы получили:

array
  'key1' => string 'value1' (length=6)
  'key2' => string 'value2' (length=6)
  'key3' => string 'value3' (length=6)
  'key4' => string 'value4' (length=6)
  'key5' => string 'value5' (length=6)


По сути, здесь идея состоит в том, чтобы:

  • разбить строку
  • , который даст вам массив, такой как 'key1', 'value1', 'key2', 'value2', ...
  • и затем выполнить итерацию по этому списку с скачком 2, используя каждый раз:
    • один элемент в качестве ключа - тот, на который указывает $i
    • значение, указанное сразу после него, - значение, указанное $i+1
0 голосов
/ 03 декабря 2016

Я не так хорош с RegExp, но как насчет этого однострочного кода

parse_str(preg_replace("/key(.*?)\\value(.*?)(\\|$)/", "key$1=value$2&", $input_lines), $output);
...