PHP 不包括 <pre> 标记的正则表达式
PHP Regex expression excluding <pre> tag
我正在使用名为 Acronyms (https://wordpress.org/plugins/acronyms/) 的 WordPress 插件。这个插件用他们的描述替换首字母缩略词。它使用 PHP PREG_REPLACE 函数。
问题是它替换了 <pre>
标签中包含的首字母缩略词,我用它来显示源代码。
您能否修改此表达式,使其不会替换 <pre>
标签中包含的首字母缩略词(不仅直接替换,而且随时替换)?可能吗?
PHP代码是:
$text = preg_replace(
"|(?!<[^<>]*?)(?<![?.&])\b$acronym\b(?!:)(?![^<>]*?>)|msU"
, "<acronym title=\"$fulltext\">$acronym</acronym>"
, $text
);
您可以使用 PCRE SKIP/FAIL regex trick(也适用于 PHP)告诉正则表达式引擎仅匹配不在某些定界符内的内容:
(?s)<pre[^<]*>.*?<\/pre>(*SKIP)(*F)|\b$acronym\b
这意味着:跳过所有以 <pre>
开头并以 </pre>
结尾的子字符串,然后才将 $acronym
作为一个完整的单词进行匹配。
这是一个sample PHP demo:
<?php
$acronym = "ASCII";
$fulltext = "American Standard Code for Information Interchange";
$re = "/(?s)<pre[^<]*>.*?<\/pre>(*SKIP)(*F)|\b$acronym\b/";
$str = "<pre>ASCII\nSometext\nMoretext</pre>More text \nASCII\nMore text<pre>More\nlines\nASCII\nlines</pre>";
$subst = "<acronym title=\"$fulltext\">$acronym</acronym>";
$result = preg_replace($re, $subst, $str);
echo $result;
输出:
<pre>ASCII</pre><acronym title="American Standard Code for Information Interchange">ASCII</acronym><pre>ASCII</pre>
也可以使用preg_split
,将代码块保持为一个组,只替换非代码块部分,然后将其组合成一个完整的字符串:
function replace($s) {
return str_replace('"', '"', $s); // do something with `$s`
}
$text = 'Your text goes here...';
$parts = preg_split('#(<\/?[-:\w]+(?:\s[^<>]+?)?>)#', $text, null, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$text = "";
$x = 0;
foreach ($parts as $v) {
if (trim($v) === "") {
$text .= $v;
continue;
}
if ($v[0] === '<' && substr($v, -1) === '>') {
if (preg_match('#^<(\/)?(?:code|pre)(?:\s[^<>]+?)?>$#', $v, $m)) {
$x = isset($m[1]) && $m[1] === '/' ? 0 : 1;
}
$text .= $v; // this is a HTML tag…
} else {
$text .= !$x ? replace($v) : $v; // process or skip…
}
}
return $text;
摘自 here.
我正在使用名为 Acronyms (https://wordpress.org/plugins/acronyms/) 的 WordPress 插件。这个插件用他们的描述替换首字母缩略词。它使用 PHP PREG_REPLACE 函数。
问题是它替换了 <pre>
标签中包含的首字母缩略词,我用它来显示源代码。
您能否修改此表达式,使其不会替换 <pre>
标签中包含的首字母缩略词(不仅直接替换,而且随时替换)?可能吗?
PHP代码是:
$text = preg_replace(
"|(?!<[^<>]*?)(?<![?.&])\b$acronym\b(?!:)(?![^<>]*?>)|msU"
, "<acronym title=\"$fulltext\">$acronym</acronym>"
, $text
);
您可以使用 PCRE SKIP/FAIL regex trick(也适用于 PHP)告诉正则表达式引擎仅匹配不在某些定界符内的内容:
(?s)<pre[^<]*>.*?<\/pre>(*SKIP)(*F)|\b$acronym\b
这意味着:跳过所有以 <pre>
开头并以 </pre>
结尾的子字符串,然后才将 $acronym
作为一个完整的单词进行匹配。
这是一个sample PHP demo:
<?php
$acronym = "ASCII";
$fulltext = "American Standard Code for Information Interchange";
$re = "/(?s)<pre[^<]*>.*?<\/pre>(*SKIP)(*F)|\b$acronym\b/";
$str = "<pre>ASCII\nSometext\nMoretext</pre>More text \nASCII\nMore text<pre>More\nlines\nASCII\nlines</pre>";
$subst = "<acronym title=\"$fulltext\">$acronym</acronym>";
$result = preg_replace($re, $subst, $str);
echo $result;
输出:
<pre>ASCII</pre><acronym title="American Standard Code for Information Interchange">ASCII</acronym><pre>ASCII</pre>
也可以使用preg_split
,将代码块保持为一个组,只替换非代码块部分,然后将其组合成一个完整的字符串:
function replace($s) {
return str_replace('"', '"', $s); // do something with `$s`
}
$text = 'Your text goes here...';
$parts = preg_split('#(<\/?[-:\w]+(?:\s[^<>]+?)?>)#', $text, null, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$text = "";
$x = 0;
foreach ($parts as $v) {
if (trim($v) === "") {
$text .= $v;
continue;
}
if ($v[0] === '<' && substr($v, -1) === '>') {
if (preg_match('#^<(\/)?(?:code|pre)(?:\s[^<>]+?)?>$#', $v, $m)) {
$x = isset($m[1]) && $m[1] === '/' ? 0 : 1;
}
$text .= $v; // this is a HTML tag…
} else {
$text .= !$x ? replace($v) : $v; // process or skip…
}
}
return $text;
摘自 here.