Ответ 1
Предпосылка вашего вопроса заключается в том, что вы хотите сопоставить определенный шаблон, а затем заменить его после выполнения дополнительной обработки в соответствующем тексте.
Кажется идеальным кандидатом для preg_replace_callback
Регулярные выражения для захвата совпадающих скобок, кавычек, фигурных скобок и т.д. могут стать довольно сложными, и сделать все это с помощью регулярного выражения на самом деле довольно неэффективно. На самом деле вам нужно написать правильный парсер, если это вам нужно.
По этому вопросу я собираюсь взять на себя ограниченный уровень сложности и решить его с помощью двухэтапного анализа с использованием регулярного выражения.
Прежде всего, самое простое регулярное выражение, которое я могу придумать для захвата жетонов между фигурными фигурными скобками.
/{([^}]+)}/
Давайте сломаем это.
{ # A literal opening brace
( # Begin capture
[^}]+ # Everything that not a closing brace (one or more times)
) # End capture
} # Literal closing brace
При применении к строке с preg_match_all
результаты выглядят примерно так:
array (
0 => array (
0 => 'A string {TOK_ONE}',
1 => ' with {TOK_TWO|0=>"no", 1=>"one", 2=>"two"}',
),
1 => array (
0 => 'TOK_ONE',
1 => 'TOK_TWO|0=>"no", 1=>"one", 2=>"two"',
),
)
Выглядит хорошо.
Обратите внимание, что если в ваших строках есть вложенные фигурные скобки, т.е. {TOK_TWO|0=>"hi {x} y"}
, это регулярное выражение не будет работать. Если это не проблема, перейдите к следующему разделу.
Можно выполнить сопоставление верхнего уровня, но единственный способ, которым я когда-либо мог это сделать, - это рекурсия. Большинство ветеранов регулярных выражений скажут вам, что как только вы добавите рекурсию в регулярное выражение, оно перестает быть регулярным выражением.
Здесь сложна дополнительная сложность обработки, и с длинными сложными строками очень легко вырваться из пространства стека и сбой вашей программы. Используйте его тщательно, если вам нужно использовать его вообще.
Рекурсивное регулярное выражение взято из одного из моих других ответов и немного изменилось.
`/{((?:[^{}]*|(?R))*)}/`
Сломанный.
{ # literal brace
( # begin capture
(?: # don't create another capture set
[^{}]* # everything not a brace
|(?R) # OR recurse
)* # none or more times
) # end capture
} # literal brace
И на этот раз вывод соответствует только скобкам верхнего уровня
array (
0 => array (
0 => '{TOK_ONE|0=>"a {nested} brace"}',
),
1 => array (
0 => 'TOK_ONE|0=>"a {nested} brace"',
),
)
Опять же, не используйте рекурсивное регулярное выражение, если вам не нужно. (Ваша система может даже не поддерживать их, если у нее есть старая библиотека PCRE)
С этим нам нужно работать, если у токена есть связанные с ним параметры. Вместо того, чтобы сопоставлять два фрагмента в соответствии с вашим вопросом, я бы рекомендовал сохранить варианты с токеном в соответствии с моими примерами. {TOKEN|0=>"option"}
Предположим, что $match
содержит совпадающий токен, если мы проверяем трубку |
, а после этого подстрока всего оставим с вашим списком параметров, снова мы можем использовать регулярное выражение для их анализа вне. (Не волнуйтесь, я приведу все вместе в конце)
/(\d)+\s*=>\s*"([^"]*)",?/
Сломанный.
(\d)+ # Capture one or more decimal digits
\s* # Any amount of whitespace (allows you to do 0 => "")
=> # Literal pointy arrow
\s* # Any amount of whitespace
" # Literal quote
([^"]*) # Capture anything that isn't a quote
" # Literal quote
,? # Maybe followed by a comma
И пример соответствует
array (
0 => array (
0 => '0=>"no",',
1 => '1 => "one",',
2 => '2=>"two"',
),
1 => array (
0 => '0',
1 => '1',
2 => '2',
),
2 => array (
0 => 'no',
1 => 'one',
2 => 'two',
),
)
Если вы хотите использовать кавычки внутри своих котировок, вам нужно будет создать собственное рекурсивное регулярное выражение.
Завершение, здесь рабочий пример.
Некорректный код инициализации.
$options = array(
'WERE' => 1,
'TYPE' => 'cat',
'PLURAL' => 1,
'NAME' => 2
);
$string = 'There {WERE|0=>"was a",1=>"were"} ' .
'{TYPE}{PLURAL|1=>"s"} named bob' .
'{NAME|1=>" and bib",2=>" and alice"}';
И все вместе.
$string = preg_replace_callback('/{([^}]+)}/', function($match) use ($options) {
$match = $match[1];
if (false !== $pipe = strpos($match, '|')) {
$tokens = substr($match, $pipe + 1);
$match = substr($match, 0, $pipe);
} else {
$tokens = array();
}
if (isset($options[$match])) {
if ($tokens) {
preg_match_all('/(\d)+\s*=>\s*"([^"]*)",?/', $tokens, $tokens);
$tokens = array_combine($tokens[1], $tokens[2]);
return $tokens[$options[$match]];
}
return $options[$match];
}
return '';
}, $string);
Обратите внимание, что проверка ошибок минимальна, при выборе опций, которые не существуют, появятся неожиданные результаты.
Вероятно, есть намного более простой способ сделать все это, но я просто взял идею и побежал с ней.