Подтвердить что ты не робот

Параметры строковой строки PHP для массива

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

Строка

all ("hi there \(option\)", (this, that), other) another

Требуемый результат (массив)

[0] => all,
[1] => Array(
    [0] => "hi there \(option\)",
    [1] => Array(
        [0] => this,
        [1] => that
    ),
    [2] => other
),
[2] => another

Это используется для консоли, которую я создаю на PHP. Я пытался использовать preg_match_all, но я не знаю, как найти круглые скобки в скобках, чтобы "создавать массивы внутри массивов".

ИЗМЕНИТЬ

Все остальные символы, которые не указаны в примере, должны рассматриваться как String.

РЕДАКТИРОВАТЬ 2

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

4b9b3361

Ответ 1

Нет никаких сомнений в том, что вы должны писать парсер, если вы строите дерево синтаксиса. Но если вам просто нужно проанализировать этот образец ввода regex, все еще может быть инструментом:

<?php
$str = 'all, ("hi there", (these, that) , other), another';

$str = preg_replace('/\, /', ',', $str); //get rid off extra spaces
/*
 * get rid off undefined constants with surrounding them with quotes
*/
$str = preg_replace('/(\w+),/', '\'$1\',', $str);
$str = preg_replace('/(\w+)\)/', '\'$1\')', $str);
$str = preg_replace('/,(\w+)/', ',\'$1\'', $str);

$str = str_replace('(', 'array(', $str);

$str = 'array('.$str.');';

echo '<pre>';
eval('$res = '.$str); //eval is evil.
print_r($res); //print the result

Демо.

Примечание: Если ввод будет неправильным, регулярное выражение, безусловно, завершится ошибкой. Я пишу это решение только в том случае, если вам нужно быстро script. Написание лексера и парсера - это трудоемкая работа, которая потребует много исследований.

Ответ 2

Обзор 10 000 футов

Вам нужно сделать это с помощью небольшого настраиваемого парсера: код принимает ввод этой формы и преобразует ее в желаемую форму.

На практике мне удобно группировать такие проблемы в одной из трех категорий, исходя из их сложности:

  • Trivial: Проблемы, которые могут быть решены с помощью нескольких циклов и гуманных регулярных выражений. Эта категория соблазнительна: если вы даже немного не уверены в том, что проблема может быть решена таким образом, хорошим правилом является решение о том, что она не может.
  • Легко: Проблемы, требующие создания небольшого парсера самостоятельно, но все еще достаточно просты, что не имеет смысла выводить большие пушки. Если вам нужно написать более ~ 100 строк кода, рассмотрите вопрос о переходе на следующую категорию.
  • Принятые: Проблемы, для которых имеет смысл идти формально и использовать уже существующий проверенный генератор парсера¹.

Я классифицирую эту конкретную проблему как принадлежность ко второй категории, что означает, что вы можете приблизиться к ней следующим образом:

Написание небольшого парсера

Определение грамматики

Чтобы сделать это, вы должны сначала определить - по крайней мере неофициально, несколько быстрых заметок - грамматику, которую вы хотите проанализировать. Имейте в виду, что большинство грамматик определяются в какой-то момент рекурсивно. Итак, скажем, наша грамматика:

  • Вход представляет собой последовательность
  • Последовательность представляет собой серию рядов из нуля или более токенов
  • токеном является либо слово, строка или массив
  • Токены разделяются одним или несколькими пробельными символами
  • Слово представляет собой последовательность буквенных символов (a-z)
  • Строка представляет собой произвольную последовательность символов, заключенных в двойные кавычки
  • Массив - это серия из одного или нескольких токенов, разделенных запятыми

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

Рассмотрение вопроса неофициально, как описано выше, проще, чем введение, но рассуждение о грамматиках проще, если вы сделаете это формально.

Построение лексера

С грамматикой в ​​руке вы знаете, что нужно сломать вход в токены, чтобы он мог обрабатываться. Компонент, который принимает вход пользователя и преобразует его в отдельные фрагменты, определенные грамматикой, называется lexer. Лексеры тупые; они касаются только "внешнего внешнего вида" ввода и не пытаются проверить, что это действительно имеет смысл.

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

$input = 'all ("hi there", (this, that) , other) another';

$tokens = array();
$input = trim($input);
while($input) {
    switch (substr($input, 0, 1)) {
        case '"':
            if (!preg_match('/^"([^"]*)"(.*)$/', $input, $matches)) {
                die; // TODO: error: unterminated string
            }

            $tokens[] = array('string', $matches[1]);
            $input = $matches[2];
            break;
        case '(':
            $tokens[] = array('open', null);
            $input = substr($input, 1);
            break;
        case ')':
            $tokens[] = array('close', null);
            $input = substr($input, 1);
            break;
        case ',':
            $tokens[] = array('comma', null);
            $input = substr($input, 1);
            break;
        default:
            list($word, $input) = array_pad(
                preg_split('/(?=[^a-zA-Z])/', $input, 2),
                2,
                null);
            $tokens[] = array('word', $word);
            break;
    }
    $input = trim($input);
}

print_r($tokens);

Построение анализатора

Сделав это, следующим шагом будет создание parser: компонент, который проверяет лексический ввод и преобразует его в желаемый формат. Парсер умный; в процессе преобразования ввода он также гарантирует, что вход хорошо сформирован правилами грамматики.

Парсеры обычно реализуются как состояния машин (также называемые конечными автоматами или конечными автоматами) и работают следующим образом:

  • У анализатора есть состояние; это обычно число в соответствующем диапазоне, но каждое состояние также описывается с более удобным для человека именем.
  • Существует цикл, который читает чтение лексических токенов по одному за раз. Основываясь на текущем состоянии и значении токена, анализатор может решить сделать одно или несколько из следующего:
    • предпринять некоторые действия, которые влияют на его вывод
    • измените его состояние на другое значение
    • решите, что вход плохо сформирован и выдает ошибку.

¹ Генераторы Parser - это программы, входные данные которых являются формальной грамматикой, а выход - лексер и синтаксический анализатор, вы можете "просто добавить воду": просто расширьте код, чтобы выполнить "предпринять какое-то действие" в зависимости от типа токена; все остальное уже позаботится. Быстрый поиск по этой теме дает led PHP Lexer и Parser Generator?

Ответ 3

Насколько мне известно, проблема <круглой скобки - это класс 2 языка Хомского, а регулярные выражения эквивалентны классу класса Хомского 3, поэтому не должно быть регулярного выражения, которое решает эту проблему.

Но я недавно что-то прочитал:

Этот шаблон PCRE решает проблему круглых скобок (предположим, что параметр PCRE_EXTENDED установлен так, что пустое пространство игнорируется): \( ( (?>[^()]+) | (?R) )* \)

С разделителями и без пробелов: /\(((?>[^()]+)|(?R))*\)/.

Это из Рекурсивные шаблоны (PCRE) - руководство по PHP.

В этом руководстве приведен пример, который решает почти ту же проблему, которую вы указали! Вы или другие можете найти его и продолжить эту идею.

Я думаю, что лучшим решением является написать больной рекурсивный шаблон с помощью preg_match_all. К сожалению, я не в силах сделать такое безумие!

Ответ 4

Во-первых, я хочу поблагодарить всех, кто помог мне в этом.

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

В моем случае мне просто нужно было что-то простое и грязное, и, следуя ответам @palindrom и @PLB, у меня есть следующее для меня:

$str=transformEnd(transformStart($string));
$str = preg_replace('/([^\\\])\(/', '$1array(', $str);
$str = 'array('.$str.');';
eval('$res = '.$str);
print_r($res); //print the result

function transformStart($str){
    $match=preg_match('/(^\(|[^\\\]\()/', $str, $positions, PREG_OFFSET_CAPTURE);
    if (count($positions[0]))
        $first=($positions[0][1]+1);
    if ($first>1){
        $start=substr($str, 0,$first);
        preg_match_all("/(?:(?:\"(?:\\\\\"|[^\"])+\")|(?:'(?:\\\'|[^'])+')|(?:(?:[^\s^\,^\"^\']+)))/is",$start,$results);
        if (count($results[0])){
            $start=implode(",", $results[0]).",";
        } else {
            $start="";
        }
        $temp=substr($str, $first);
        $str=$start.$temp;
    }
    return $str;
}

function transformEnd($str){
    $match=preg_match('/(^\)|[^\\\]\))/', $str, $positions, PREG_OFFSET_CAPTURE);
    if (($total=count($positions)) && count($positions[$total-1]))
        $last=($positions[$total-1][1]+1);
    if ($last==null)
        $last=-1;
    if ($last<strlen($str)-1){
        $end=substr($str,$last+1);
        preg_match_all("/(?:(?:\"(?:\\\\\"|[^\"])+\")|(?:'(?:\\\'|[^'])+')|(?:(?:[^\s^\,^\"^\']+)))/is",$end,$results);
        if (count($results[0])){
            $end=",".implode(",", $results[0]);
        } else {
            $end="";
        }
        $temp=substr($str, 0,$last+1);
        $str=$temp.$end;
    }
    if ($last==-1){
        $str=substr($str, 1);
    }
    return $str;
}

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

Еще раз спасибо вам = D.

Ответ 5

Я хочу знать, работает ли это:

  • замените ( на Array(
  • Используйте regex для запятой после слов или круглых скобок без запятой

    preg_replace( '/[^,]\s+/', ',', $string )

  • eval( "\$result = Array( $string )" )

Ответ 6

Я поставлю алгоритм или псевдокод для его реализации. Надеюсь, вы сможете решить, как реализовать его в PHP:

function Parser([receives] input:string) returns Array

define Array returnValue;

for each integer i from 0 to length of input string do
    charachter = ith character from input string.

    if character is '('
        returnValue.Add(Parser(substring of input after i)); // recursive call

    else if character is '"'
        returnValue.Add(substring of input from i to the next '"')

    else if character is whitespace
        continue

    else
        returnValue.Add(substring of input from i to the next space or end of input)

   increment i to the index actually consumed


return returnValue

Ответ 7

если строковые значения являются фиксированными, это можно сделать следующим образом:

$ar = explode('("', $st);

$ar[1] = explode('",', $ar[1]);

$ar[1][1] = explode(',', $ar[1][1]);

$ar[1][2] = explode(')',$ar[1][1][2]);

unset($ar[1][1][2]);

$ar[2] =$ar[1][2][1];

unset($ar[1][2][1]);