PHP正则表达式解析避免子字符串

时间:2021-02-21 05:31:31

标签: php regex

我正在编写一个简单的 Markdown 解析器来为也使用一些 LaTeX 方程的页面输出 HTML。例如,对于斜体:

  // italic
  $content = preg_replace_callback(
    '/(\*|_)(.+)\1/',
    function ($m) {
      return "<i>" . $m[2] . "</i>";
    },
    $content
  );

不幸的是,很多 Markdown 格式与 LaTeX 符号(以及代码块)发生冲突,因此我需要先转义 LaTeX 部分,并仅在这些部分之外解析 Markdown。 LaTeX 位由 $$$ 分隔,因此很容易发现它们:

preg_match("/\$+(.*?)\$+/", $content)

例如,这是此类页面的示例:


## Section title

Lorem ipsum *dolores* sic amet. $E = mc^2$, and since :

$$
\cos(3*\pi*\sqrt{2}) = \delta
$$

所以......斜体和乘法之间的冲突。

我的第一个猜测是我应该将内容分成 2 个数组:一个包含带有索引的 LaTeX 位,一个包含位于 LaTeX 位之间的非 LaTeX 位,将第二个数组放在一边,然后将它们合并回来在一起。

preg_split() 中断所述模式并返回中间子串,但丢弃与模式匹配的子串。似乎可以使用 PREG_SPLIT_DELIM_CAPTURE 标志进行调整以返回所有子字符串,包括与正则表达式匹配的断点,但是当使用此标志时,文档没有显示输出数据结构,所以我不知道如何迭代输出数组,只处理不匹配模式的部分。

此函数输出什么和/或是否有更好/更快的方法在匹配其他模式的区域之外执行模式检测?

1 个答案:

答案 0 :(得分:1)

一种选择可能是使用 SKIP FAIL 使在同一行上仅以 $ 开头和结尾的部分不属于匹配项。

然后在捕获组中捕获 *_ 并使用反向引用 \1 匹配相同的字符,而不匹配两者之间的相同字符。

^\$+(?:\R(?!\$+$).*)*\R\$+$(*SKIP)(*FAIL)|([*_])((?:(?!\1).)+)\1

模式匹配:

  • ^ 字符串开头
  • \$+ 匹配 1+ 次出现的 $
  • (?:\R(?!\$+$).*)* 匹配所有不只有 $
  • 的行
  • \R\$+$ 匹配一行只有 $
  • (*SKIP)(*FAIL)| 跳过当前匹配的内容
  • ([*_])组 1
  • 中捕获 *_
  • ((?:(?!\1).)+) 重复匹配除捕获的字符以外的所有字符
  • \1 反向引用组 1,与捕获的字符匹配

Regex demo | Php demo

示例

$content= <<<'DATA'
## Section title

Lorem ipsum *dolores* sic amet. $E = mc^2$, and since :

$$
\cos(3*\pi*\sqrt{2}) = \delta
$$
DATA;

$content = preg_replace_callback(
    '/^\$+(?:\R(?!\$+$).*)*\R\$+$(*SKIP)(*FAIL)|([*_])((?:(?!\1).)+)\1/m',
    function ($m) {
        return "<i>" . $m[2] . "</i>";
    },
    $content
);

echo $content;

输出

## Section title

Lorem ipsum <i>dolores</i> sic amet. $E = mc^2$, and since :

$$
\cos(3*\pi*\sqrt{2}) = \delta
$$

我必须注意,使用正则表达式获取标记可能很脆弱并且存在边缘情况。

您可以通过例如断言空白边界来使模式更加具体。

^\$+(?:\R(?!\$+$).*)*\R\$+$(*SKIP)(*FAIL)|(?<!\S)([*_])((?:(?!\1).)+)\1(?!\S)

Regex demo

相关问题