Регулярное выражение для соответствия чему угодно, кроме двух последовательных фигурных скобок - PullRequest
3 голосов
/ 23 декабря 2010

Какое регулярное выражение может соответствовать чему-либо, кроме двух последовательных фигурных скобок ({)?
Пример строки:
{{some text}} string I want {{another set {{and inner}} }}
Я хочу получить только string i want.

Использование стека для этого пришло мне в голову, но я хотел знать, можно ли это сделать с помощью регулярных выражений.
Я использую PHP PCRE

Заранее спасибо

Ответы [ 2 ]

6 голосов
/ 23 декабря 2010

Используйте опережающее утверждение (?!{{|}}), чтобы убедиться, что у вас нет вложенного набора скобок внутри внешнего набора.

{{((?!{{|}}).)*}}

Тестовая программа

<?php
$string = '{{lot {{of}} characters}}';

for (;;)
{
    var_dump($string);
    $replacement = preg_replace('/{{((?!{{|}}).)*}}/', '', $string);

    if ($string == $replacement)
        break;

    $string = $replacement;
}

выход

string(25) "{{lot {{of}} characters}}"
string(19) "{{lot  characters}}"
string(0) ""

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

# Unbalanced braces.
string(23) "{{lot {{of}} characters"
string(17) "{{lot  characters"

string(23) "lot {{of}} characters}}"
string(17) "lot  characters}}"

# Multiple sets of braces.
string(25) "{{lot }}of{{ characters}}"
string(2) "of"

# Lone curlies.
string(41) "{{lot {{of {single curly} }} characters}}"
string(19) "{{lot  characters}}"
string(0) ""
2 голосов
/ 23 декабря 2010

Если вам нужно сделать что-то более сложное с содержимым, например, обработать содержимое или переменные, то вы можете использовать рекурсивное регулярное выражение, используя оператор (? R).

$data = "{{abcde{{fg{{hi}}jk}}lm}}";
$regexp = "#\{\{((?:[^(\{\{)(\}\})]+|(?R))+)\}\}#";
$count = 0;

function revMatch($matches) {
  global $regexp, $count;

  if (is_array($matches)) {
    // Match detected, process for nested components
    $subData = preg_replace_callback($regexp, 'revMatch', $matches[1]);
  } else {
    // No match, leave text alone
    $subData = $matches;
  }

  // This numbers each match, to demonstrate call order
  return "(" . $count++ . ":<" . $subData . ">)";
}

echo preg_replace_callback($regexp, 'revMatch', $data);

Это преобразует: {{abcde{{fg{{hi}}jk}}lm}} в (2:<abcde(1:<fg(0:<hi>)jk>)lm>)


Немного пояснений по регулярному выражению: #\{\{((?:[^(\{\{)(\}\})]+|(?R))+)\}\}#

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

  1. строка без двойных скобок [^(\{\{)(\}\})]+

  2. все регулярное выражение повторяется. Скобка (?:) - это группа без захвата.

NB. #s - это разделители шаблонов, я думал, что дополнительные косые черты еще больше уменьшат читабельность.

...