Регулярное выражение PHP для сопоставления пар ключ-значение из заданной строки

#php #arrays #regex

#php #массивы #регулярное выражение

Вопрос:

я надеюсь, что кто-нибудь может помочь.

У меня есть строка следующего вида

 $string = 'latitude=46.6781471,longitude=13.9709534,options=[units=auto,lang=de,exclude=[hourly,minutely]]';
  

Теперь я пытаюсь создать массив из каждой пары ключ-значение, но у меня плохо получается с регулярным выражением для preg_match_all()

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

 Array (
[0] => Array
    (
        [0] => latitude=46.6781471,
        [1] => longitude=13.9709534,
        [2] => options=[units=si,
        [3] => lang=de,
    )

[1] => Array
    (
        [0] => latitude
        [1] => longitude
        [2] => options=[units
        [3] => lang
    )

.. and so on
  

Где в конце я хотел бы достичь следующих результатов.

 Array (
[latitude] => 46.6781471
[longitude] => 13.9709534
[options] => Array
    (
        [units] => auto
        [exclude] => hourly,minutely
    )
)
  

Я был бы признателен за любую помощь или пример, как я могу добиться этого из заданной строки.

Комментарии:

1. Просто любопытно, но зачем вам создавать и пытаться анализировать совершенно новый формат сериализации? Почему бы просто не использовать JSON или аналогичный стандарт, в котором уже есть полная библиотека для синтаксического анализа и получения правильных значений. Единственный способ заставить это работать с регулярным выражением — это сделать его рекурсивным регулярным выражением, которое может соответствовать квадратным скобкам, что не совсем просто. Вам было бы почти лучше перебирать каждый символ, пока вы не нажмете специальный символ (запятая, равно или квадратная скобка) и работать с этим.

Ответ №1:

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

 function parseOptionsString($string) {

    $length        = strlen($string);
    $key           = null;
    $contextStack  = array();
    $options       = array();

    $specialTokens = array('[', ']', '=', ',');
    $buffer     = '';

    $currentOptions = $options;

    for ($i = 0; $i < $length; $i  ) {
        $currentChar = $string[$i];

        if (!in_array($currentChar, $specialTokens)) {
            $buffer .= $currentChar;
            continue;
        }

        if ($currentChar == '[') {
            array_push($contextStack, [$key, $currentOptions]);
            $currentOptions[$key] = array();
            $currentOptions       = $currentOptions[$key];
            $key                  = '';
            $buffer               = '';
            continue;
        }

        if ($currentChar == ']') {
            if (!empty($buffer)) {
                if (!empty($key)) {
                    $currentOptions[$key] = $buffer;    
                } else {
                    $currentOptions[] = $buffer;
                }
            }


            $contextInfo     = array_pop($contextStack);
            $previousContext = $contextInfo[1];
            $thisKey         = $contextInfo[0];

            $previousContext[$thisKey] = $currentOptions;

            $currentOptions        = $previousContext;
            $buffer                = '';
            $key                   = '';
            continue;
        }

        if ($currentChar == '=') {
            $key    = $buffer;
            $buffer = '';
            continue;
        }

        if ($currentChar == ',') {

            if (!empty($key)) {
                $currentOptions[$key] = $buffer; 
            } else if (!empty($buffer)) {
                $currentOptions[] = $buffer;
            }
            $buffer        = '';
            $key           = '';
            continue;
        }

    }

    if (!empty($key)) {
        $currentOptions[$key] = $buffer;
    }

    return $currentOptions;
} 
  

это дает следующий результат:

 print_r(parseOptionsString($string));

Array
(
    [latitude] => 46.6781471
    [longitude] => 13.9709534
    [options] => Array
        (
            [units] => auto
            [lang] => de
            [exclude] => Array
                (
                    [0] => hourly
                    [1] => minutely
                )

        )

)
  

Обратите также внимание, что вам нужен специальный синтаксис для массивов, содержащих значения, разделенные только запятыми (exclude=[ежечасно, поминутно] становится exclude => ежечасно, поминутно, а не exclude => array(ежечасно, поминутно)). Я думаю, что это несоответствие в вашем формате, и я написал синтаксический анализатор с учетом «правильной» версии.

Комментарии:

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

2. Да, вам определенно следует выбрать другой подход. Упрощать — это правило выше всех нас.

Ответ №2:

Если вам не нужен синтаксический анализатор, вы также можете попробовать этот код. Оно преобразует вашу строку в JSON и декодирует в массив. Но, как говорили другие, я думаю, вам следует попробовать подход с использованием JSON. Если вы отправляете эту строку с помощью XMLHttpRequest в JavaScript, создать правильный JSON-код для отправки не составит труда.

 $string = 'latitude=46.6781471,longitude=13.9709534,options=[units=auto,lang=de,exclude=[hourly,minutely]]';

$string = preg_replace('/([^=,[]s] )/', '"$1"', $string);

$string = '{' . $string . '}';
$string = str_replace('=', ':', $string);
$string = str_replace('[', '{', $string);
$string = str_replace(']', '}', $string);

$string = preg_replace('/({[^:}]*})/', '|$1|', $string);
$string = str_replace('|{', '[', $string);
$string = str_replace('}|', ']', $string);

$result = json_decode($string, true);

print_r($result);