需求是:原字符串内含有特殊记号,比如"V_{CC} = V_{DD}"
,其中_{CC}
是latex 的下标语法,实际显示出来是下面这样:
V C C = V D D V_{CC} = V_{DD} VCC=VDD
现在要把下标部分提取出来,并把原字符串用这个记号切割开,也就是把原字符串切割成:"V", "CC", " = V", "DD"
,这种感觉,方便之后单独处理下标。re
库里官方的split
函数太“简陋”了,不太好使。
函数代码
总之代码如下:
import re
from typing import Union, List, Tuple, Optional
def text_find_and_split(pattern: Union[str, re.Pattern], text: str) -> List[Tuple[str, Optional[re.Match]]]:
'''
将字符串按正则匹配分割,返回被分割的字符串片段和匹配结果,匹配的片段会从原字符串中被整体切割出去。
比如:原字符串: "AAAA $01$ BBBB $02$ CCCC" 正则:r"$(\d+)$",
切割后的字符串变成"AAAA ", " BBBB ", " CCCC" ,被匹配的片段 "$01$" 和 "$02$" 通过Match 对象返回.
字符串片段和Match 对象被混合起来作为列表返回,每个列表元素是一个二元组,前者是匹配位置前的字符串片段,后者是Match 对象.
上面的例子的返回形式为:
[
("AAAA ", Match("$01$")),
(" BBBB ", Match("$02$")),
(" CCCC", None)
]
最后一个片段后面没有匹配,所以Match 对象用None 代替
Parameters
----------
pattern : Union[str, re.Pattern]
正则表达式,可以是字符串或编译后的Pattern 对象.
text : str
原字符串.
Returns
-------
result_list : List[Tuple[str, Optional[re.Match]]]
返回值是一个列表,其中每个元组包含两个元素,前者是匹配前的字符串片段,后者是Match 对象.
'''
m_iter = pattern.finditer(text) if isinstance(pattern, re.Pattern) else re.finditer(pattern, text)
result_list = []
last_stop = 0
for m in m_iter:
start, stop = m.span()
s = text[last_stop: start]
last_stop = stop
result_list.append((s, m))
result_list.append((text[last_stop:], None))
return result_list
注释里说的应该够清楚了。
使用效果
要处理的字符串是:
s = r"V_{CM}=V_{S} / 2,V_{OUT}=V_{S} / 2,V_{S}=V_{+} - V_{-} =5V,V_{+} =2.5V,V_{-} = -2.5V"
用来匹配记号的正则是:
sub_pattern = r"_\{(.+?)\}"
p = re.compile(sub_pattern) # 可能编译后正则匹配会更快吧
函数调用:
text_find_and_split(p, s) # 第一个参数可以是字符串sub_pattern,也可以是编译后的p
切割结果:
lm = [('V', <re.Match object; span=(1, 6), match='_{CM}'>),
('=V', <re.Match object; span=(8, 12), match='_{S}'>),
(' / 2,V', <re.Match object; span=(18, 24), match='_{OUT}'>),
('=V', <re.Match object; span=(26, 30), match='_{S}'>),
(' / 2,V', <re.Match object; span=(36, 40), match='_{S}'>),
('=V', <re.Match object; span=(42, 46), match='_{+}'>),
(' - V', <re.Match object; span=(50, 54), match='_{-}'>),
(' =5V,V', <re.Match object; span=(60, 64), match='_{+}'>),
(' =2.5V,V', <re.Match object; span=(72, 76), match='_{-}'>),
(' = -2.5V', None)]
然后再做一些小处理,就可以把原字符串插入到word 里面,并且自动添加上下标格式:
for i in lm:
s, m = i
s_run = cp0.add_run(s)
s_run.font.name = "Times New Roman"
if m is None:
break
gs = m.groups()
assert len(gs) == 1
sub_text = gs[0]
sub_run = cp0.add_run(sub_text)
sub_run.font.name = "Times New Roman"
sub_run.font.subscript = True
效果如下图: