替换“<code>&#34; tags</code>内的所有”*“*

时间:2009-11-23 15:29:01

标签: php regex latex lookahead lookbehind

首先要做的事情:thisthisthisthis都没有回答我的问题。所以我会开一个新的。

请阅读

好的,好的。我知道,正则表达式不是解析常规HTML的方法。请注意,创建的文档是使用受限制的受控HTML子集编写的。撰写文档的人都知道他们在做什么。他们都是IT专业人士!

鉴于受控语法, 可以使用regex解析我在这里的文档。

我不是试图从网上下载任意文件并解析它们!

如果解析 失败,则会对文档进行编辑,因此它将进行解析。我在这里解决的问题比这更普遍(即不替换其他两种模式中的模式)。

一点背景(你可以跳过这个......)

在我们的办公室,我们应该“打印”我们的文档。因此,为什么有些人想出把它全部放入Word文档中。到目前为止,我们还没有到那里。而且,如果我完成这项工作,我们可能不需要。

当前状态(......和此)

文档的主要部分存储在TikiWiki数据库中。我创建了一个daft PHP脚本,它将文档从HTML(通过LaTeX)转换为PDF。其中一个必须具有所选Wiki系统的功能,这是一个WYSIWYG编辑器。正如预期的那样,我们将文件留给了不那么正式的DOM。

因此,我使用“简单”正则表达式对文档进行音译。到目前为止,这一切都很好(大部分)都很好,但是我遇到了一个我自己没有想到的问题。

问题

某些特殊字符需要替换为LaTeX标记。对于exaple,\字符应替换为$\backslash$(除非有人知道另一种解决方案?)。

verbatim区块中

除外!

我会用<code>部分替换verbatim个标签。但是,如果此code块包含反斜杠(与Windows文件夹名称的情况一样),则脚本仍会替换这些反斜杠。

我估计我可以使用负面LookBehinds和/或LookAheads来解决这个问题。但我的尝试没有用。

当然,我会更好地使用真正的解析器。事实上,这是我的“脑内路线图”中的内容,但它目前超出了范围。该脚本适用于我们有限的知识领域。创建一个解析器需要我从头开始。

我的尝试

示例输入

The Hello \ World document is located in:
<code>C:\documents\hello_world.txt</code>

预期输出

The Hello $\backslash$ World document is located in:
\begin{verbatim}C:\documents\hello_world.txt\end{verbatim}

这是迄今为止我能想到的最好的结果:

<?php
$patterns = array(
    "special_chars2" => array( '/(?<!<code[^>]*>.*)\\\\[^$](?!.*<\/code>)/U', '$\\backslash$'),
);

foreach( $patterns as $name => $p ){
    $tex_input = preg_replace( $p[0], $p[1], $tex_input );
}
?>

请注意,这只是一个摘录,[^$]是另一个LaTeX要求。

似乎的另一种尝试:

<?php
$patterns = array(
    "special_chars2" => array( '/\\\\[^$](?!.*<\/code>)/U', '$\\backslash$'),
);

foreach( $patterns as $name => $p ){
    $tex_input = preg_replace( $p[0], $p[1], $tex_input );
}
?>

...换句话说:忽略负面的背后。

但是这看起来更容易出错,而不是看后卫和前瞻。

相关问题

您可能已经注意到,该模式不合适(/.../U)。那么这只会在<code>区块内尽可能少地匹配吗?考虑到环顾?

6 个答案:

答案 0 :(得分:6)

如果我,我将尝试找到HTML解析器,并将使用它。

另一种选择是尝试将字符串分为<code>.*?</code>其他部分

并将更新其他部分,并将重新组合。

$x="The Hello \ World document is located in:\n<br>
<code>C:\documents\hello_world.txt</code>";

$r=preg_split("/(<code>.*?<\/code>)/", $x,-1,PREG_SPLIT_DELIM_CAPTURE);

for($i=0;$i<count($r);$i+=2)
    $r[$i]=str_replace("\\","$\\backslash$",$r[$i]);

$x=implode($r);

echo $x;

结果如下。

The Hello $\backslash$ World document is located in: 
C:\documents\hello_world.txt

抱歉,如果我的方法不适合您。

答案 1 :(得分:3)

  

我认为我可以使用负面的LookBehinds和/或LookAheads来解决这个问题。

你觉得错了。 Regular expressions are not a replacement for a parser

我建议您通过htmltidy管道html,然后使用dom-parser读取它,然后将dom转换为目标输出格式。有什么阻止你走这条路吗?

答案 2 :(得分:2)

Parser FTW,好的。但是如果你不能使用解析器,你可以确定<code>标签永远不会嵌套,你可以尝试以下方法:

  1. 查找文件的<code>.*?</code>部分(可能需要启用点匹配换行模式)。
  2. #?#?#?#
  3. 之类的独特内容替换该部分内的所有反斜杠
  4. 将1中的部分替换为新部分
  5. $\backslash$
  6. 替换所有反斜杠
  7. <code>替换为\begin{verbatim},将所有</code>替换为\end{verbatim}
  8. #?#?#?#替换为\
  9. 仅供参考,PHP中的正则表达式不支持可变长度的lookbehind。因此,这使得两个边界之间的条件匹配变得困难。

答案 3 :(得分:1)

Pandoc? Pandoc在一堆格式之间进行转换。你也可以将一堆苍蝇连在一起然后将它们隐藏起来。也许一些shell脚本与你的php抓取脚本相结合?

使用“预期输入”和命令pandoc -o text.tex test.html,输出为:

The Hello \textbackslash{} World document is located in:
\verb!C:\documents\hello_world.txt!

pandoc可以从stdin读取,写入stdout或管道直接进入文件。

答案 4 :(得分:0)

如果您的<code>块未嵌套,则此正则表达式会在^字符串开头或</code>之后找到反斜杠,而中间没有<code>。< / p>

((?:^|</code>)(?:(?!<code>).)+?)\\
    |            |              |
    |            |              \-- backslash
    |            \-- least amount of anything not followed by <code>
    \-- start-of-string or </code>

并将其替换为:

$1$\backslash$

您必须以“单线”模式运行此正则表达式,因此.匹配换行符。您还必须多次运行它,指定全局替换是不够的。每次替换只会替换字符串开头后的第一个符合条件的反斜杠或</code>

答案 5 :(得分:0)

根据DOMDocument之类的HTML或XML解析器编写解析器。遍历已解析的DOM,并将\替换为code节点后代不是$\backslash$节点的每个文本节点,code节点\begin{verbatim} … \end{verbatim}节点{{1}} 1}}。