我正在尝试用文本块中的 HTML 标签替换特殊符号/占位符。到目前为止,它运行良好,但如果可替换实体位于
<pre>
或 <code>
标签内,我现在需要避免替换。
我当前的代码:
function parse($text) {
$search = array(
'/\*\*(.*?)\*\*/is', // bold
'/\/\/(.*?)\/\//is', // italic
'/__(.*?)__/is', // underline
); #search
$replace = array(
'<b>$1</b>',
'<i>$1</i>',
'<u>$1</u>',
); #replace
return preg_replace($search, $replace, $text);
} #parse
输入示例:
<pre>
** Bold Text **
// Italic Text //
__ Underline Text __
</pre>
<code>
** Bold Text **
// Italic Text //
__ Underline Text __
</code>
如何在解析时排除这些特定标签内的文本?
首先,这不是 BBCode。 BBCode 使用
[
和 ]
作为分隔符来模仿常见的 HTML 标记。你所拥有的类似于 Markdown 或 reStructuredText。
其次,你的替换算法极其简单,很可能在将来给你带来很多麻烦。如果您这样做不仅仅是为了学习如何用 PHP 进行编码,我建议您使用已经可以完成您想做的事情的现有解析器,例如 PHP Markdown、PHP reStrucuredText 或 PHP BBCode Parser。
现在,至于您的实际问题:这并不容易,但您可以从更改正则表达式开始,这样它们仅在不在
<pre>
标签内时才适用,如下所示:(未经测试)
'/(?<!<pre>).*?\*\*(.*?)\*\*.*?(?!</pre>)/is', // bold
我的解决方案不仅会排除
<pre>
和 <code>
标签之间的文本,我还会整合您预先存在的模式。
首先,以不贪婪的方式将前导
<pre>
和 <code>
标签与其同名的结束标签进行匹配。用 (*SKIP)(*FAIL)
丢弃此类匹配的子字符串。
接下来,以非贪婪的方式将应替换的两个字符子字符串匹配到其相同样式的末尾。捕获组 #2 将是比赛开始时配对符号中的第一个,捕获组 #3 将是要包装在新 HTML 标签中的文本。
在回调函数中,
$m
代表比赛的负载。 [0]
是全字符串匹配,[1]
仅与丢弃的文本块相关。 [2]
的 $m
元素将包含第一个匹配的符号 - 使用 strtr()
将这些符号转换为各自的 HTML 标签。 [3]
的 $m
元素将包含内部文本 - 这不需要更改,而只需插入新的开始和结束 HTML 标记之间。
作为附加功能(未请求),我建议在
do-while()
循环内执行替换过程,以便嵌套 BBtag 全部转换。
关于
s
、i
和 x
模式修饰符:
s
告诉正则表达式引擎模式中的.
(任何字符)也应该匹配换行符(默认情况下不匹配)。i
告诉正则表达式引擎不区分大小写地匹配 - 这将影响文字字符串 pre
和 code
,因为模式的所有其他方面都已经是不区分大小写的匹配。x
告诉正则表达式引擎忽略模式中的文字空格——这样做是为了使模式能够以缩进形式写在多行上,以便于人类阅读。代码:(演示)
do {
$text = preg_replace_callback(
'#
<(pre|code)>.*?</\1>(*SKIP)(*FAIL)
|([*_/])\2(.*?)\2\2
#six',
fn($m) => sprintf(
'<%1$s>%2$s</%1$s>',
strtr($m[2], '*/_', 'biu'),
$m[3]
),
$text,
-1,
$count
);
} while ($count);
var_export($text);
更具挑战性的输入:
$text = <<<TEXT
<pre>
** Bold Text **
// Italic Text //
__ Underline Text __
</pre>
** Bold Text **
// Italic Text //
__ Underline Text __
__** Bold Text **
Nested Text __
<code>
** Bold Text **
// Italic Text //
__ Underline Text __
</code>
TEXT;
输出:
'<pre>
<b> Bold Text </b>
<i> Italic Text </i>
<u> Underline Text </u>
</pre>
<b> Bold Text </b>
<i> Italic Text </i>
<u> Underline Text </u>
<u><b> Bold Text </b>
Nested Text </u>
<code>
<b> Bold Text </b>
<i> Italic Text </i>
<u> Underline Text </u>
</code>'