我正在编写一个简单的 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
标志进行调整以返回所有子字符串,包括与正则表达式匹配的断点,但是当使用此标志时,文档没有显示输出数据结构,所以我不知道如何迭代输出数组,只处理不匹配模式的部分。
此函数输出什么和/或是否有更好/更快的方法在匹配其他模式的区域之外执行模式检测?
答案 0 :(得分:1)
一种选择可能是使用 SKIP FAIL 使在同一行上仅以 $
开头和结尾的部分不属于匹配项。
然后在捕获组中捕获 *
或 _
并使用反向引用 \1
匹配相同的字符,而不匹配两者之间的相同字符。
^\$+(?:\R(?!\$+$).*)*\R\$+$(*SKIP)(*FAIL)|([*_])((?:(?!\1).)+)\1
模式匹配:
^
字符串开头\$+
匹配 1+ 次出现的 $
(?:\R(?!\$+$).*)*
匹配所有不只有 $
\R\$+$
匹配一行只有 $
(*SKIP)(*FAIL)|
跳过当前匹配的内容([*_])
在组 1*
或 _
((?:(?!\1).)+)
重复匹配除捕获的字符以外的所有字符\1
反向引用组 1,与捕获的字符匹配示例
$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)