我想创建一个解析器,它接受任何 LaTeX 格式的字符串并返回一个 Python 可以计算的表达式。
我在分数方面遇到了一些问题。以下是一些示例:
LaTeX(输入) | 可互操作的字符串(输出) |
---|---|
|
|
|
|
|
|
|
|
这是我迄今为止尝试过的:
fraction_re = re.compile(r"\\frac{(.*?)}{(.*?)}")
def parser(expression):
fractions = fraction_re.findall(expression)
for numerator, denominator in fractions:
pattern = r"\\frac\{%s\}\{%s\}" % (numerator, denominator)
replace = f"(({numerator})/({denominator}))"
expression = re.sub(pattern=pattern, repl=replace, string=expression)
return expression
这对于情况一和情况二来说效果很好(见表),但对于情况三和情况四就有问题。我怀疑
-
和 +
符号引起了问题,因为它们本身就是正则表达式元字符。
我想添加一些额外的行来转义它们,例如
numerator = re.sub(pattern='+', repl='\+', string=numerator)
但这在我看来并不是一个好的长期策略。我还尝试向
pattern
变量添加方括号(因为方括号中的普通正则表达式符号不会被解释为这样),即
pattern = r"\\frac\{[%s]\}\{[%s]\}" % (numerator, denominator)
但这也不起作用。
接下来我可以尝试什么?
我知道这个问题之前已经被问过很多次了(例如Python Regex to Simplify LaTex FractionsUsing Python Regex to Simplify Latex FractionsUsing if-then-else条件与Python正则表达式替换)但我觉得他们的问题与我的有点不同,我找不到对我有很大帮助的答案。
我也知道已经存在开箱即用的解析器可以完全满足我的要求(例如:https://github.com/augustt198/latex2sympy),但我真的很想构建这个我自己。
我不确定你为什么要采取两阶段方法;正如您所指出的,它会导致第二阶段的正则表达式元字符出现问题。您可以在匹配时使用
re.sub
: 进行替换
import re
fraction_re = re.compile(r'\\frac{([^}]+)}{([^}]+)}')
def parser(expression):
return fraction_re.sub(r'((\1)/(\2))', expression)
print(parser(r'\frac{1}{2} \frac{x}{3b} \frac{2-m}{3} \frac{7}{5+y}'))
输出
((1)/(2)) ((x)/(3b)) ((2-m)/(3)) ((7)/(5+y))
请注意,在正则表达式中使用
[^}]+
比使用 .*?
更有效,因为它会减少回溯。
您可以在
re.sub()
中使用简单的 lambda 函数,如下所示:
import re
data = r"""
some very cool \textbf{Latex} stuff
\begin{enumerate}
\item even a very cool item
\end{enumerate}
Here comes the fun
\frac{1}{2}
\frac{x}{3b}
\frac{2-m}{3}
\frac{7}{5+y}
"""
rx = re.compile(r'\\frac\{(?P<numerator>[^{}]+)\}\{(?P<denominator>[^{}]+)\}')
data = rx.sub(lambda m: f"(({m.group('numerator')}/({m.group('denominator')})", data)
print(data)
这会产生
some very cool \textbf{Latex} stuff
\begin{enumerate}
\item even a very cool item
\end{enumerate}
Here comes the fun
((1/(2)
((x/(3b)
((2-m/(3)
((7/(5+y)
这个表达式可以归结为
\\frac\{(?P<numerator>[^{}]+)\}\{(?P<denominator>[^{}]+)\}
确实不需要使用命名组,只是为了使其清晰可见。