Преобразование строки, разделенной обратной косой чертой, в ассоциативный массив

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

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

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

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

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


person Pyrite    schedule 13.03.2011    source источник


Ответы (5)


Используя простое регулярное выражение через 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 и сбора массива.

person mario    schedule 13.03.2011
comment
Как будет выглядеть регулярное выражение, если у вас есть строка, содержащая такие переходы табуляции: key1=value1\tkey2=value2\tkey3=value3 ? - person Avatar; 02.11.2012
comment
@EchtEinfachTV Больше похоже на это: stackoverflow .com/questions/168171/ — хотя есть и более простые подходы (замена \t на &, а затем str_parse() или регулярное выражение \t(\w+)=(\w+) или что-то в этом роде). Однако, пожалуйста, задайте новый вопрос, если у вас есть другой источник. - person mario; 03.11.2012
comment
@mario Спасибо, я наткнулся на parse_str() и использую его сейчас: // преобразовать переходы вкладок в &, чтобы иметь возможность использовать функцию запроса $toURL = str_replace("\t","&",$keysValues); // анализировать как URL-адрес parse_str($toURL, $data); // ключи доступа в массиве $postid = $data['postid']; - person Avatar; 05.11.2012

Как насчет такого:

$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', ...
  • and, then, iterate over this list, with a jump of 2, using each time :
    • one element as the key -- the one pointed by $i
    • тот, что сразу после него в качестве значения - тот, на который указывает $i+1
person Pascal MARTIN    schedule 13.03.2011

Я не очень хорошо разбираюсь в RegExp, но как насчет этого однострочного кода

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

Разделите на сегменты и выполните цикл, увеличивая их на два.

$str = 'php\127\typescript\12\jquery\120\angular\50';
$segments = explode('\\', $str);
$data = [];
for ($i = 0, $count = count($segments) - 1; $i < $count; $i += 2) {
    $data[$segments[$i]] = $segments[$i + 1];
}

Выход

array(4) {
  'php'        => string(3) "127"
  'typescript' => string(2) "12"
  'jquery'     => string(3) "120"
  'angular'    => string(2) "50"
}
person Markus Zeller    schedule 23.06.2020

@ Ответ Васима, который

  1. не работает как указано и
  2. из которых @mario перечисляет предостережения по его использованию

можно улучшить / сделать более гибким, используя сочетание классов символов с отрицанием и экранированных буквенных обратных косых черт для создания строки запроса.

Код: (Демо)

parse_str(preg_replace('/([^\\\\]+)\\\\([^\\\\]+)\\\\?/', '$1=$2&', $string), $output);
var_export($output);

Выход:

array (
  'key1' => 'value1',
  'key2' => 'value2',
  'key3' => 'value3',
  'key4' => 'value4',
  'key5' => 'value5',
)
person mickmackusa    schedule 07.12.2020