Заменить шаблон в PHP - PullRequest
       37

Заменить шаблон в PHP

2 голосов
/ 29 ноября 2009

У меня нет опыта использования регулярных выражений в PHP, поэтому я обычно пишу некоторую запутанную функцию, используя серию str_replace (), substr (), strpos (), strstr () и т. Д. (Вы понимаете).

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

ПРИМЕЧАНИЕ. Я НЕ анализирую HTML или XML, и иногда я буду использовать разделители, отличные от (например, | или ~ или [tag] или: :). Я ищу общий способ замены подстановочного знака между двумя известными разделителями с помощью регулярных выражений, я не создаю синтаксический анализатор HTML или XML.

Мне нужно регулярное выражение, которое заменяет это:

<sometag>everything in here</sometag>

с этим:

<sometag>new contents</sometag>

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

$new_text = swapText ( "<sometag>", $the_new_text_to_go_into_the_dag );

function swapText ( $in_tag_with_brackets_to_update, $in_new_text ) {
 // define tags
 $starting_tag  = $in_tag_with_brackets_to_update;
 $ending_tag    = str_replace( "<", "</", $in_tag_with_brackets_to_update) );

 // not sure if this is the proper regex match string or not
 // and/or if any escaping needs to be done on the tags
 $find_string         = "{$starting_tag}.*{$ending_tag}";
 $replace_with_string = "{$starting_tag}{$in_new_text}{$ending_tag}";

 // after some regex, this function should return new version of <tag>data</tag>
}

Спасибо.

Ответы [ 4 ]

10 голосов
/ 29 ноября 2009

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

Теперь причина, по которой вы не можете использовать регулярные выражения для разбора xml, заключается в том, что они не являются контекстными. Поэтому существует целый класс проблем, для которых нельзя использовать регулярные выражения. Это включает в себя вложенные теги (будь то XML или нет), так что имейте это в виду.

Это, кстати, вы должны использовать preg - не ereg. ereg - это наименее используемый, более медленный и теперь устаревший тип регулярных выражений. Просто забудь об этом.

В pcre (регулярные выражения, совместимые с Perl), который использует язык preg, . (точка) - это подстановочный знак, который соответствует любому отдельному символу (кроме новой строки). Вы можете поставить квантификатор после матча. Квантификатор может быть явным диапазоном чисел, таким как {1,3} (означающий, по крайней мере, один, но не более 3), или вы можете использовать один из коротких символов, например + (Сокращенное от {1,}, что означает хотя бы один) или * (имеется в виду любое число, включая ноль). С этим знанием вы можете сопоставить что угодно с .*.

По умолчанию выражения будут соответствовать максимально возможному шаблону (известен как жадный). Вы можете изменить это с помощью модификатора ?. Таким образом, .*? будет соответствовать чему угодно, но принимать кратчайший возможный образец. Затем его можно использовать для сопоставления любому значению с разделителями, например:

~<foo>.*?</foo>~

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

/<foo>.*?<\/foo>/

В целом, вышесказанное является плохой практикой, поскольку гораздо лучше сопоставлять отрицательный класс символов, чем точку, но для простоты просто игнорируйте это до тех пор, пока не получите основы под своей оболочкой. Это будет работать в большинстве случаев. В частности, поскольку . не соответствует символу новой строки, это не будет работать, если содержимое содержит символ новой строки. Если вам это нужно, вы можете сделать одно из двух: либо добавьте к выражению модификатор , либо замените . классом символов, включающим символы новой строки. Например, [\s\S] (имеется в виду символ пробела или - символ без пробела, который совпадает с чем-либо). Вот так выглядело бы выражение:

~<foo>.*?</foo>~s

Или:

~<foo>[\s\S]*?</foo>~

Чтобы все это заработало, давайте передадим это функции preg_replace:

echo preg_replace('~<foo>.*?</foo>~s', '<foo>Lorem Ipsum</foo>', $input);

Если ваши имена тегов являются переменными, вы можете создать выражение, как если бы вы использовали SQL-запрос. Как и в SQL, вам нужно экранировать определенные символы. Для этого используйте preg_quote:

function swapText($tagname, $replacement_text, $input) {
  $tagname_escaped = preg_quote($tagname, '~');
  return preg_replace(
    '~<' . $tagname_escaped . '>.*?</' . $tagname_escaped . '>~s',
    '<' . $tagname . '>' . $replacement_text . '</' . $tagname . '>',
    $input);
}
3 голосов
/ 30 ноября 2009

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

$mystr='<sometag>everything in here</sometag>';
$start=strpos($mystr,"<sometag>");
$end=strpos($mystr,"</sometag>");
print substr($mystr,0,$start+strlen("<sometag>") ) . "new value" . substr($mystr,$end);
1 голос
/ 29 ноября 2009

Во-первых, если вы заменяете html, используйте что-то вроде simple html dom . Если формат в точности соответствует тому, что вы говорите (например, <sometag> не может быть <sometag >), тогда можно использовать регулярное выражение.

Не используйте функции на основе ereg, так как они устарели, используйте функции preg .

preg_replace('%(<sometag>)[^<]*(</sometag>)%i', '$1something else$2', $str);

EDIT
Немного лучшая версия выше, теперь поддерживает < в тексте

preg_replace('%(<sometag>).*?(</sometag>)%i', '$1something else$2', $str);

$ 1 и $ 2 - это согласованный текст в скобках. Поскольку они постоянны, их можно заменить на постоянную

preg_replace('%<sometag>.*?</sometag>%i', '<sometag>something else</sometag>', $str);
0 голосов
/ 09 июня 2011

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

function wildcardReplace($String,$Search,$Filler,$Wildcard = '???'){

        list($startStr,$endStr) = explode($Wildcard,$Search);

        $start = strpos($String,$startStr);

        // Make sure the end point is the first closest match after the start string.   

        $endofstarter = strpos($String,$startStr) + strlen($startStr);

        $startofender = strpos(
                    substr($String,$endofstarter),
                    $endStr
                ) + $endofstarter;


        $Result = substr($String,0,$start+strlen($startStr) ) . $Filler. substr($String,$startofender);

        // Replace any remaining stuff

        $RemainingString = substr($String,$startofender);

        // If theres any matches left, replace them

        if(strpos($RemainingString,$startStr)>-1) $Result = str_replace($RemainingString,wildcardReplace($RemainingString,$Search,$Filler),$Result);

        return $Result;
}

Пример использования: $Output = wildcardReplace('<a href="http://www.youtube.com/watch?v=dQw4w9WgXcQ"><img src="rickroll.png" width="500"></a>','width="???"',350,'???')

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