问题描述
我想创建一个解析器,它接受任何 LaTeX 格式的字符串并返回一个 Python 可以计算的表达式。
我有几个关于分数的问题。下面是一些例子:
LaTeX(输入) | 可互操作的字符串(输出) |
---|---|
\frac{1}{2} |
((1)/(2)) |
\frac{x}{3b} |
((x)/(3b)) |
\frac{2-m}{3} |
((2-m)/(3)) |
\frac{7}{5+y} |
((7)/(5+y)) |
这是我迄今为止尝试过的:
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)
但这也没有用。
有人可以帮我吗?
提前致谢。
附上
我知道之前已经在 SO 上多次问过这个问题(例如 Python Regex to Simplify LaTex Fractions Using Python Regex to Simplify Latex Fractions Using if-then-else conditionals with Python regex replacement),但我觉得他们的问题与我的有点不同,我没有问过能够找到对我有很大帮助的答案。
我也知道已经存在开箱即用的解析器,可以完全满足我的需求(例如: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>[^{}]+)\}
不需要使用命名组,真的,只是为了使它清晰。