共计 1450 个字符,预计需要花费 4 分钟才能阅读完成。
问题背景分析
当开发者从 ChatGPT 复制数学公式到 Typora 时,常遇到以下格式问题:

- 行内公式差异:
- ChatGPT 默认使用
$...$语法 -
Typora 推荐使用
`$...$`或\(...\)语法 -
多行公式冲突:
- ChatGPT 的
$$...$$块可能包含换行符 -
Typora 对多行公式的渲染有特殊对齐要求
-
转义字符问题:
- 下划线
_和反斜杠\需要二次转义 - 某些 Unicode 符号需要重新编码
技术方案设计
采用正则表达式实现五层转换逻辑:
-
基础语法转换
import re def convert_inline_math(text): # 使用零宽断言确保 $ 两侧不是空格 return re.sub(r'(?<!\s)\$(.*?)\$(?!\s)', r'`$\1$`', text) -
多行公式处理
- 非贪婪匹配避免跨公式捕获
-
自动添加
\\换行符 -
特殊字符转义
- 对
_^&等符号添加转义 - 保留
\begin{}...\end{}环境
完整实现代码
import re
from typing import List
class FormulaConverter:
"""公式转换器核心类"""
@staticmethod
def _escape_special_chars(formula: str) -> str:
"""处理 LaTeX 特殊字符"""
escape_map = {
'_': r'\_',
'^': r'\^',
'&': r'\&'
}
return ''.join(escape_map.get(c, c) for c in formula)
def convert(self, text: str) -> str:
"""主转换方法"""
# 处理块公式
text = re.sub(r'\$\$\n?(.*?)\n?\$\$',
lambda m: f'$$\n{self._escape_special_chars(m.group(1))}\n$$',
text, flags=re.DOTALL)
# 处理行内公式
text = re.sub(r'(?<!`)\$(?!\s)(.*?)(?<!\s)\$(?!`)',
lambda m: f'`${self._escape_special_chars(m.group(1))}$`',
text)
return text
性能优化对比
测试三种正则方案处理 1000 次转换的耗时:
- 基础模式:
r'\$(.*?)\$' - 平均耗时:4.2ms
-
问题:可能错误匹配货币符号
-
增强模式:
r'(?<!\s)\$(.*?)\$(?!\s)' - 平均耗时:4.5ms
-
优点:避免误匹配
-
预编译模式:
PATTERN = re.compile(r'(?<!\s)\$(.*?)\$(?!\s)') PATTERN.sub(...) - 平均耗时:3.8ms
- 推荐:复用编译结果
常见问题解决方案
- 美元符号冲突
- 现象:
价格 $199被误转换 -
解决:添加负面预测
(?<!\d)\$ -
代码块嵌套
- 现象:
`$...$`被重复处理 -
解决:排除已有反引号的公式
-
多环境支持
- 现象:
\begin{equation}未正确处理 - 解决:扩展正则匹配范围
扩展适配方案
支持其他编辑器的转换策略:
- VS Code:
- 需转换
$...$为\(...\) -
添加扩展检测逻辑
-
Obsidian:
- 保持
$$...$$语法 -
需处理 wiki 链接冲突
-
Jupyter Notebook:
- 转为 Markdown 单元格
- 处理 IPython 特殊语法
实战练习
尝试改进脚本实现以下功能:
- 自动检测
align、gather等 LaTeX 环境 - 处理
\[... \]语法变体 - 添加命令行进度显示
提示:可使用 re.finditer() 实现渐进式处理,避免大文本内存问题。
正文完
