PHP 正则表达式负后视可变长度替代问题
PHP Regex negative lookbehind variable length alternatives issue
我正在 PHP 中开发一个电报机器人,我必须处理其中只有一些 basic HTML tag are allowed 和所有 <
、>
和 [=14] 的字符串=] 不属于标签或 HTML 实体的符号必须替换为相应的 HTML 实体(<
为 <
,>
为>
和 &
与 &
)
示例字符串
<b>bold</b>, <strong>bold</strong>
<i>italic</i>, <em>italic</em>
<a href="http://www.example.com/" >inline URL</a>
<code>inline fixed-width code</code>
<pre>pre-formatted fixed-width code block</pre>
yes<b bad<>b> <bad& hi>;<strong >b<a<
我设法使用 Regex 替换了 &
和 <
。例如,我在此模式 <(?!(?:(?:\/?)(?:(?:b>)|(?:strong>)|(?:i>)|(?:em>)|(?:code>)|(?:pre>)|(?:a(?:[^>]+?)?>))))
中使用负前瞻来摆脱 <
符号。
但是我无法构建一个模式来替换不属于任何标签的 >
符号。 PCRE 不支持后视中的不定量词。虽然它允许 lookbehinds 中的备选方案具有不同的长度,但要求每个备选方案具有固定长度。
所以,我尝试使用这种模式(仍然不完整)(?<!(?:(?:<b)|(?:<strong)|(?:<i)|(?:<em)|(?:<code)|(?:<pre>)|(?:<a)))>
,其中所有替代项都有固定长度,但它仍然显示 Compilation failed: lookbehind assertion is not fixed length
正确的答案是使用 DOM 解析器。不过,对于一种快速而肮脏(有时更快)的方法,您可以使用 PCRE
实现的 (*SKIP)(*FAIL)
机制:
<[^<>&]+>(*SKIP)(*FAIL)|[<>&]+
完整的 PHP
演练将是:
<?php
$string = <<<DATA
<b>bold</b>, <strong>bold</strong>
<i>italic</i>, <em>italic</em>
<a href="http://www.example.com/" >inline URL</a>
<code>inline fixed-width code</code>
<pre>pre-formatted fixed-width code block</pre>
yes<b bad<>b> <bad& hi>;<strong >b<a<
DATA;
$regex = '~<[^<>&]+>(*SKIP)(*FAIL)|[<>&]+~';
$string = preg_replace_callback($regex,
function($match) {
return htmlentities($match[0]);
},
$string);
echo $string;
?>
产生:
<b>bold</b>, <strong>bold</strong>
<i>italic</i>, <em>italic</em>
<a href="http://www.example.com/" >inline URL</a>
<code>inline fixed-width code</code>
<pre>pre-formatted fixed-width code block</pre>
yes<b bad<>b> <bad& hi>;<strong >b<a<
但是,正如之前在 Whosebug 上多次声明的那样,请考虑改用解析器,毕竟这就是它们的用途。
解析器方式可以是:
$dom = new DOMDocument();
$dom->loadHTML($string, LIBXML_HTML_NOIMPLIED | LIBXML_NOERROR);
echo $dom->saveHTML();
但是,您提供的代码段已损坏,因此正则表达式可能是处理它的唯一方法。
您可以找到合法的特殊符号来转换为这样的实体。
重要的是正确解析标签。
免责声明 - 如果您不按照下面的方式进行操作,则没有理由使用正则表达式,它不会起作用。
在每场比赛中,第 0 组将包含 <、> 或 &
您可以添加更多,请参阅底部的正则表达式
正则表达式
(?:(?><(?:(?:(?:(script|style|object|embed|applet|noframes|noscript|noembed)(?:\s+(?>"[\S\s]*?"|'[\S\s]*?'|(?:(?!/>)[^>])?)+)?\s*>)[\S\s]*?</\s*(?=>))|(?:/?[\w:]+\s*/?)|(?:[\w:]+\s+(?:"[\S\s]*?"|'[\S\s]*?'|[^>]?)+\s*/?)|\?[\S\s]*?\?|(?:!(?:(?:DOCTYPE[\S\s]*?)|(?:\[CDATA\[[\S\s]*?\]\])|(?:--[\S\s]*?--)|(?:ATTLIST[\S\s]*?)|(?:ENTITY[\S\s]*?)|(?:ELEMENT[\S\s]*?))))>)(*SKIP)(*FAIL)|[<>]|[&](?!(?i:[a-z]+|(?:\#(?:[0-9]+|x[0-9a-f]+)));))
已解释
(?:
(?> # Atomic group
< # Match tag forms and fail them with skip / fail verbs ( see below )
(?:
(?:
(?:
# Invisible content; end tag req'd
( # (1 start)
script
| style
#| head
| object
| embed
| applet
| noframes
| noscript
| noembed
) # (1 end)
(?:
\s+
(?>
" [\S\s]*? "
| ' [\S\s]*? '
| (?:
(?! /> )
[^>]
)?
)+
)?
\s* >
)
[\S\s]*? </ \s*
(?= > )
)
| (?: /? [\w:]+ \s* /? )
| (?:
[\w:]+
\s+
(?:
" [\S\s]*? "
| ' [\S\s]*? '
| [^>]?
)+
\s* /?
)
| \? [\S\s]*? \?
| (?:
!
(?:
(?: DOCTYPE [\S\s]*? )
| (?: \[CDATA\[ [\S\s]*? \]\] )
| (?: -- [\S\s]*? -- )
| (?: ATTLIST [\S\s]*? )
| (?: ENTITY [\S\s]*? )
| (?: ELEMENT [\S\s]*? )
)
)
)
>
) # End atomic group
(*SKIP)(*FAIL)
| #or,
[<>] # Angle brackets
| #or,
[&] # Ampersand
(?! # Only if not an entity
(?i:
[a-z]+
| (?:
\#
(?:
[0-9]+
| x [0-9a-f]+
)
)
)
;
)
# Add more here
)
我正在 PHP 中开发一个电报机器人,我必须处理其中只有一些 basic HTML tag are allowed 和所有 <
、>
和 [=14] 的字符串=] 不属于标签或 HTML 实体的符号必须替换为相应的 HTML 实体(<
为 <
,>
为>
和 &
与 &
)
示例字符串
<b>bold</b>, <strong>bold</strong>
<i>italic</i>, <em>italic</em>
<a href="http://www.example.com/" >inline URL</a>
<code>inline fixed-width code</code>
<pre>pre-formatted fixed-width code block</pre>
yes<b bad<>b> <bad& hi>;<strong >b<a<
我设法使用 Regex 替换了 &
和 <
。例如,我在此模式 <(?!(?:(?:\/?)(?:(?:b>)|(?:strong>)|(?:i>)|(?:em>)|(?:code>)|(?:pre>)|(?:a(?:[^>]+?)?>))))
中使用负前瞻来摆脱 <
符号。
但是我无法构建一个模式来替换不属于任何标签的 >
符号。 PCRE 不支持后视中的不定量词。虽然它允许 lookbehinds 中的备选方案具有不同的长度,但要求每个备选方案具有固定长度。
所以,我尝试使用这种模式(仍然不完整)(?<!(?:(?:<b)|(?:<strong)|(?:<i)|(?:<em)|(?:<code)|(?:<pre>)|(?:<a)))>
,其中所有替代项都有固定长度,但它仍然显示 Compilation failed: lookbehind assertion is not fixed length
正确的答案是使用 DOM 解析器。不过,对于一种快速而肮脏(有时更快)的方法,您可以使用 PCRE
实现的 (*SKIP)(*FAIL)
机制:
<[^<>&]+>(*SKIP)(*FAIL)|[<>&]+
完整的
PHP
演练将是:
<?php
$string = <<<DATA
<b>bold</b>, <strong>bold</strong>
<i>italic</i>, <em>italic</em>
<a href="http://www.example.com/" >inline URL</a>
<code>inline fixed-width code</code>
<pre>pre-formatted fixed-width code block</pre>
yes<b bad<>b> <bad& hi>;<strong >b<a<
DATA;
$regex = '~<[^<>&]+>(*SKIP)(*FAIL)|[<>&]+~';
$string = preg_replace_callback($regex,
function($match) {
return htmlentities($match[0]);
},
$string);
echo $string;
?>
产生:
<b>bold</b>, <strong>bold</strong>
<i>italic</i>, <em>italic</em>
<a href="http://www.example.com/" >inline URL</a>
<code>inline fixed-width code</code>
<pre>pre-formatted fixed-width code block</pre>
yes<b bad<>b> <bad& hi>;<strong >b<a<
但是,正如之前在 Whosebug 上多次声明的那样,请考虑改用解析器,毕竟这就是它们的用途。
解析器方式可以是:
$dom = new DOMDocument();
$dom->loadHTML($string, LIBXML_HTML_NOIMPLIED | LIBXML_NOERROR);
echo $dom->saveHTML();
但是,您提供的代码段已损坏,因此正则表达式可能是处理它的唯一方法。
您可以找到合法的特殊符号来转换为这样的实体。
重要的是正确解析标签。
免责声明 - 如果您不按照下面的方式进行操作,则没有理由使用正则表达式,它不会起作用。
在每场比赛中,第 0 组将包含 <、> 或 &
您可以添加更多,请参阅底部的正则表达式
正则表达式
(?:(?><(?:(?:(?:(script|style|object|embed|applet|noframes|noscript|noembed)(?:\s+(?>"[\S\s]*?"|'[\S\s]*?'|(?:(?!/>)[^>])?)+)?\s*>)[\S\s]*?</\s*(?=>))|(?:/?[\w:]+\s*/?)|(?:[\w:]+\s+(?:"[\S\s]*?"|'[\S\s]*?'|[^>]?)+\s*/?)|\?[\S\s]*?\?|(?:!(?:(?:DOCTYPE[\S\s]*?)|(?:\[CDATA\[[\S\s]*?\]\])|(?:--[\S\s]*?--)|(?:ATTLIST[\S\s]*?)|(?:ENTITY[\S\s]*?)|(?:ELEMENT[\S\s]*?))))>)(*SKIP)(*FAIL)|[<>]|[&](?!(?i:[a-z]+|(?:\#(?:[0-9]+|x[0-9a-f]+)));))
已解释
(?:
(?> # Atomic group
< # Match tag forms and fail them with skip / fail verbs ( see below )
(?:
(?:
(?:
# Invisible content; end tag req'd
( # (1 start)
script
| style
#| head
| object
| embed
| applet
| noframes
| noscript
| noembed
) # (1 end)
(?:
\s+
(?>
" [\S\s]*? "
| ' [\S\s]*? '
| (?:
(?! /> )
[^>]
)?
)+
)?
\s* >
)
[\S\s]*? </ \s*
(?= > )
)
| (?: /? [\w:]+ \s* /? )
| (?:
[\w:]+
\s+
(?:
" [\S\s]*? "
| ' [\S\s]*? '
| [^>]?
)+
\s* /?
)
| \? [\S\s]*? \?
| (?:
!
(?:
(?: DOCTYPE [\S\s]*? )
| (?: \[CDATA\[ [\S\s]*? \]\] )
| (?: -- [\S\s]*? -- )
| (?: ATTLIST [\S\s]*? )
| (?: ENTITY [\S\s]*? )
| (?: ELEMENT [\S\s]*? )
)
)
)
>
) # End atomic group
(*SKIP)(*FAIL)
| #or,
[<>] # Angle brackets
| #or,
[&] # Ampersand
(?! # Only if not an entity
(?i:
[a-z]+
| (?:
\#
(?:
[0-9]+
| x [0-9a-f]+
)
)
)
;
)
# Add more here
)