【问题标题】:How can I replace LaTeX $...$ and $$...$$ notations by something like <div>$...$</div>?如何用 <div>$...$</div> 之类的东西替换 LaTeX $...$ 和 $$...$$ 符号?
【发布时间】:2014-01-02 23:28:48
【问题描述】:

我目前遇到的问题是 Jekyll 不能很好地与 Markdown 和 LaTeX 配合使用。所以我有很多文章有$\frac{some}{latex}$$$\int^e_v {en} more$$

如何将$...$ 替换为&lt;span&gt;$...$&lt;/span&gt;$$...$$ 替换为&lt;div&gt;$$..$$&lt;/div&gt;

使这项任务变得困难的事情是:

  • ... 可能包含换行符。事实上,... 可能包含任何内容,除了$
  • 第一个 $ 替换为 &lt;span&gt;$,但第二个替换为 $&lt;/span&gt;
  • $...$$$...$$ 可以在同一个文档中使用(但始终至少用一个空格分隔)

编辑:我刚刚看到我还需要一些转义。所以任务又多了一个难度:

  • \$ 不应与上述两种情况中的任何一种匹配。

【问题讨论】:

  • 您可以先运行两个替换,一个先替换$$ ... $$,然后替换$ ... $... 可以与否定类匹配:[^$]+(假设不能有 $$$$$$ 之类的东西(对于第一种类型的 LaTeX,因为这在我的文档中没有意义) .

标签: python regex


【解决方案1】:

知道您要求使用正则表达式,但您会因为提到您将手动处理的边缘情况而头疼。 (如果发布了其他正则表达式解决方案,请将此答案与他们的答案进行比较)。有了这个,改变双和单 TeX 标记的行为和处理 TeX 代码中的转义就很简单了。这是一个非常简单的 pyparsing 示例,可以满足您的需求:

from pyparsing import *

D1 = QuotedString("$",escChar='\\')
D2 = QuotedString("$$",escChar='\\')

div_action = lambda x: "<div>$%s$</div>"%x[0]
span_action = lambda x: "<span>$$%s$$</span>"%x[0]
D1.setParseAction(span_action)
D2.setParseAction(div_action)
others  = Word(printables)
grammar = OneOrMore(D2 | D1 | others).leaveWhitespace()

还有一个用例:

S = "$\LaTeX$ is worth $$x=\$3.40$$"
print grammar.transformString(S)

给予:

<span>$\LaTeX$</span> is worth <div>$$x=$3.40$$</div>

【讨论】:

  • 我不知道 pyparsing 存在 (+1)。再一次,我的 CS 1 课程的一个很好的实用示例:-)
  • 如果D1D2 处于高亮环境中,是否可以使用pyparsing 进行检查?我刚刚注意到,如果它们在 {% highlight [language] %}code of that language that might contain $, e.g. PHP {% endhighlight %} 中,则不应发生替换。
  • @moose 是的,但我这样做的方式有点像黑客。使用.setParseAction 并设置一个函数来跟踪您感兴趣的全局“状态”。
【解决方案2】:

我们可以通过两步替换来实现:

import re
str = "$rac{some}{latex}$$$\int^e_v {en} more$$\$rac{some}{latex}$$$\int^e_v {en} more$$\n$rac{some}{latex}$\n$$\int^e_v {en} more$$\n\$rac{some}{latex}$\n$$\int^e_v {en} more$$"

#first step:
str = re.sub(r'(?<![\\])\$\$([^\$]+)\$\$', "<div>$$\g<1>$$</div>", str)
#second step:
str = re.sub(r'(?<![\$\\])\$([^\$]+)(?:(?<!\<div\>)(?<!\\)\$)', "<span>$\g<1>$</span>", str)
print str

解释:

第一步:

我们仅在出现$$ 时执行替换,将其替换为&lt;div&gt;$$\g&lt;1&gt;$$&lt;/div&gt;\g&lt;1&gt; 将替换为正则表达式中定义的第一个组)。

str = re.sub(r'(?<![\\])\$\$([^\$]+)\$\$', "<div>$$\g<1>$$</div>", str)

意识到我们正在使用这个 regex (?&lt;![\\])\$\$([^\$]+)\$\$ regex101 example,它的工作方式如下:

  1. (?&lt;![\\]) ... 定义我们正在匹配... 前面没有\ [在正则表达式中:(?&lt;![\\])]。所以首先我们说我们不想在表达式前加上\
  2. ... \$\$ ... 定义我们必须在字符串的开头出现 $$
  3. ... ([^\$]+) 定义我们想要除上一步之后的 $ 之外的所有内容[在正则表达式中 [^\$]+]。然后我们把它放到一个捕获组(...),供以后在代码中引用。
  4. ... \$\$ 毕竟我们完成了表达式,说我们必须在字符串的最后出现$$

第二步:

我们仅在出现$ 时执行替换,将其替换为&lt;span&gt;$\g&lt;1&gt;$&lt;/span&gt;(同样,\g&lt;1&gt; 将被正则表达式中定义的第一个组匹配替换)

str = re.sub(r'(?<![\$\\])\$([^\$]+)(?:(?<!\<div\>)(?<!\\)\$)', "<span>$\g<1>$</span>", str)

还要意识到我们正在使用另一个 regex (?&lt;![\$\\])\$([^\$]+)(?:(?&lt;!\&lt;div\&gt;)(?&lt;!\\)\$)(是的,有点难)regex101 example,其工作方式如下:

  1. (?&lt;![\$\\]) ... 定义我们匹配 ... 之前没有 \ $ [在正则表达式中:(?&lt;![\\\$])]。所以首先我们说我们一开始不想要\$
  2. ... \$ ... 定义我们的字符串需要以一个 $ 开头
  3. ... ([^\$]+) ... 定义一个捕获组,其中包含除 $ 之外的所有内容,以供将来回调。
  4. ... (?:(?&lt;!\&lt;div\&gt;)(?&lt;!\\)\$) 我们说完我们的字符串以 $ 结尾,但前面没有 div [在正则表达式中:?&lt;!\&lt;div\&gt;)]\ [在正则表达式中: (?&lt;!\\)]。 (然后我们把它全部放到一个非捕获组中说这一切都只是一件事(?:(?&lt;!\&lt;div\&gt;)(?&lt;!\\)\$)

注意:也许有更有效的方法可以得到这个结果。

【讨论】:

  • 非常好!但也许你(或者我,如果你愿意的话)可以添加一些解释:([^\$]*) 匹配除$ 之外的所有内容。所以第一个将$$...$$ 替换为&lt;div&gt;$$...$$&lt;/div&gt;。但我不知道! 是什么意思。
  • 我真的很喜欢你的回答。如果您可以扩展它以处理转义,我将添加 50 声望的赏金(两天后;我现在无法添加赏金)。
  • (?&lt;!) 表示前面没有。在这种情况下,我们尝试匹配前面没有其他 $$。 (与(?&lt;=)相反)
  • 用新的问题编辑并添加了相应的解释。问候
  • 哇,太酷了!非常感谢!我会尽快给你赏金。如果我忘记了,请在此处添加评论。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-03
  • 1970-01-01
  • 1970-01-01
  • 2012-04-19
相关资源
最近更新 更多