Регулярные выражения: как сделать замену "опция разделения" - PullRequest
1 голос
/ 16 июля 2009

эти регулярные выражения сводят меня с ума. Я застрял с этим:

test1:[[link]] test2:[[gold|silver]] test3:[[out1[[inside]]out2]] test4:this|not

Задача:
Удалите все [[и]] и, если есть опция split, выберите более позднюю, поэтому вывод должен быть:

test1:link test2:silver test3:out1insideout2 test4:this|not

Я придумал (PHP)

$text = preg_replace("/\\[\\[|\\]\\]/",'',$text); // remove [[ or ]]

это работает для части 1 задачи. но до этого я думаю, что я должен сделать опцию разделения, мое лучшее решение:

$text = preg_replace("/\\[\\[(.*\|)(.*?)\\]\\]/",'$2',$text);

Результат:

test1:silver test3:[[out1[[inside]]out2]] this|not

Я застрял. может кто-нибудь с некоторыми свободными минутами помочь мне? Спасибо!

Ответы [ 7 ]

1 голос
/ 16 июля 2009

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

\[\[(?:[^\[\]]*\|)?([^\[\]]+)\]\]

Это заменит строки параметров, чтобы дать вам последний вариант из группы. Если вы будете запускать его несколько раз, пока он больше не будет совпадать, вы должны получить правильный результат (первый проход заменит [[out1 [[inside]] out2]] на [[out1insideout2]], а второй отбросит скобки.

Редактировать 1 : В качестве объяснения,

\[\[        # Opening [[
(?:         # A non-matching group (we don't want this bit)
    [^\[\]] # Non-bracket characters
    *       # Zero or more of anything but [
    \|      # A literal '|' character representing the end of the discarded options
)?          # This group is optional: if there is only one option, it won't be present
(           # The group we're actually interested in ($1)
    [^\[\]] # All the non-bracket characters
    +       # Must be at least one
)           # End of $1
\]\]        # End of the grouping.

Редактировать 2 : Изменено выражение, чтобы игнорировать ']', а также '[' (работает немного лучше).

Редактировать 3 : Нет необходимости знать количество вложенных скобок, так как вы можете сделать что-то вроде:

$oldtext = "";
$newtext = $text;
while ($newtext != $oldtext)
{
    $oldtext = $newtext;
    $newtext = preg_replace(regexp,replace,$oldtext);
}
$text = $newtext;

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

Обратите внимание, что я не знаю PHP, поэтому, возможно, в приведенном выше тексте есть ошибки синтаксиса.

0 голосов
/ 16 июля 2009
$str = 'test1:[[link]] test2:[[gold|silver]] test3:[[out1[[inside]]out2]] test4:this|not';
$s = preg_split("/\s+/",$str);
foreach ($s as $k=>$v){
    $v = preg_replace("/\[\[|\]\]/","",$v);        
    $j = explode(":",$v);
    $j[1]=preg_replace("/.*\|/","",$j[1]);
    print implode(":",$j)."\n"; 
}
0 голосов
/ 16 июля 2009

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

String input = "test1:[[link]] " +
               "test2:[[gold|silver]] " +
               "test3:[[out1[[inside]]out2]] " +
               "test4:this|not";

String step1 = Regex.Replace(input, @"\[\[([^|]+)\|([^\]]+)\]\]", @"[[$2]]");
String step2 = Regex.Replace(step1, @"\[\[|\]\]", String.Empty);

// Prints "test1:silver test3:out1insideout2 test4:this|not"
Console.WriteLine(step2);
0 голосов
/ 16 июля 2009

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

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

Вам понадобится экранировать все обратные слеши при помещении в строку (\ становится \\.)

\[\[((?:[^][|]+|(?!\[\[|]])[^|])++\|?)*]]

Как уже объясняли другие, вы используете это с несколькими проходами. Продолжайте цикл пока есть совпадения, выполняя замену (сохраняя только группу совпадений 1).

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

test1:[[link]] test2:[[gold|si[lv]er]]
test3:[[out1[[in[si]de]]out2]] test4:this|not

становится

test1:[[link]] test2:si[lv]er
test3:out1in[si]deout2 test4:this|not
0 голосов
/ 16 июля 2009

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

$str = 'test1:[[link]] test2:[[gold|silver]] test3:[[out1[[inside]]out2]] test4:this|not'; $reg = '/(.*?):(.*?)( |$)/'; 
preg_match_all($reg, $str, $m);
foreach($m[2] as $pos => $match) {
  if (strpos($match, '|') !== FALSE && strpos($match, '[[') !== FALSE ) {
    $opt = explode('|', $match); $match = $opt[count($opt)-1]; 
  }
  $m[2][$pos] = str_replace(array('[', ']'),'', $match );
}

foreach($m[1] as $k=>$v) $result[$k] = $v.':'.$m[2][$k]; 
0 голосов
/ 16 июля 2009

Почему бы просто не убрать оставшиеся скобки?

$str = 'test1:[[link]] test2:[[gold|silver]] test3:[[out1[[inside]]out2]] test4:this|not';
$str = preg_replace('/\\[\\[(?:[^|\\]]+\\|)+([^\\]]+)\\]\\]/', '$1', $str);
$str = str_replace(array('[', ']'), '', $str);
0 голосов
/ 16 июля 2009

Зачем пытаться сделать все это за один раз. Сначала удалите [[]], а затем разберитесь с опциями, сделайте это в двух строках кода.

Пытаясь что-то сделать, отдайте предпочтение ясности и простоте.

Похоже, у вас есть все кусочки.

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