1

我正在用 PHP 开发 Telegram Bot,我必须在其中处理仅允许使用一些基本 HTML 标记和 All的字符串<>并且&必须将不属于标记或 HTML 实体的符号替换为相应的 HTML 实体(<with &lt;, >with&gt;&with &amp;)
示例字符串

<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<

我设法替换&<使用正则表达式。例如,我在此模式中使用负前瞻<(?!(?:(?:\/?)(?:(?:b>)|(?:strong>)|(?:i>)|(?:em>)|(?:code>)|(?:pre>)|(?:a(?:[^>]+?)?>))))来摆脱<符号。

但是我无法构建一个模式来替换>不属于任何标签的符号。PCRE 不支持后视中的不定量词。尽管它允许lookbehinds 中的备选方案具有不同的长度,但要求每个备选方案具有固定长度。

所以,我尝试使用这种模式(仍然不完整)(?<!(?:(?:<b)|(?:<strong)|(?:<i)|(?:<em)|(?:<code)|(?:<pre>)|(?:<a)))>,其中所有替代方案都有固定的长度,但它仍然说Compilation failed: lookbehind assertion is not fixed length

4

2 回答 2

1

正确的答案是改用 DOM 解析器。但是,对于一种快速而肮脏(有时甚至更快)的方式,您可以使用实现的(*SKIP)(*FAIL)机制PCRE

<[^<>&]+>(*SKIP)(*FAIL)|[<>&]+

在 regex101.com 上查看演示


完整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&lt;b bad&lt;&gt;b&gt; &lt;bad&amp; hi&gt;;<strong >b&lt;a&lt;

但是,正如之前在 StackOverflow 上多次指出的那样,请考虑使用解析器,毕竟这就是它们的用途。


解析器方式可以是:

$dom = new DOMDocument();
$dom->loadHTML($string, LIBXML_HTML_NOIMPLIED | LIBXML_NOERROR);

echo $dom->saveHTML();

但是,您提供的代码段已损坏,因此正则表达式可能是处理它的唯一方法。

于 2017-03-09T16:21:33.440 回答
1

您可以找到要转换为这样的实体的合法特殊符号。

最重要的是正确解析标签。
免责声明 - 如果您不按照以下方式进行操作,甚至没有理由使用正则表达式,它将无法正常工作。

在每场比赛中,第 0 组将包含 <、> 或 &
您可以添加更多,请参阅底部的正则表达式

正则表达式
(?:(?><(?:(?:(?:(script|style|object|embed|applet|noframes|noscript|noembed)(?:\s+(?>"[\S\s]*?"|'[\S\s]*?'|(?:(?!/>)[^>])?)+)?\s*>)[\S\s]*?</\1\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]*? </ \1 \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
 )
于 2017-03-09T17:44:04.393 回答