正则表达式中的算术运算

13 投票
4 回答
4967 浏览
提问于 2025-04-16 22:15

我正在使用gedit的正则表达式插件(Python风格的正则表达式)。我想对一个组的回溯引用进行一些数学运算。

举个例子:

PART 1 DATA MODELS Chapter  
2 Entity-Relationship Model 27

我想把它改成这样:

PART 1 DATA MODELS Chapter  25
2 Entity-Relationship Model 27

我的正则表达式是 ^(PART.*)\n(.*\s(\d+))\n,我想用类似 \1 (\3-2)\n\2\n 的内容来替换,其中 \3-2 是指把回溯引用 \3 减去2。但是我用的替换正则表达式不对。我想知道该怎么做?谢谢!

4 个回答

3

我不知道正则表达式里能做算术运算或其他计算。如果有哪个正则引擎支持这个,那真是太酷了!不过我觉得这样做可能会让正则引擎变得非常慢,不太实际。

我觉得你最好的选择是使用 sub 这个正则函数/方法:

re.sub(pattern, repl, string[, count, flags])

这个函数会返回一个字符串,它是通过把字符串中最左边不重叠的模式替换成你指定的内容得到的。如果没有找到这个模式,原字符串就会保持不变。你指定的内容可以是一个字符串,也可以是一个函数;如果是字符串,里面的反斜杠转义字符会被处理。比如说,\n 会变成一个换行符,\r 会变成换行,其他未知的转义字符像 \j 会保持不变。像 \6 这样的反向引用会被替换成模式中第6组匹配的子字符串。例如:

>>> re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(\s*\):',
...        r'static PyObject*\npy_\1(void)\n{',
...        'def myfunc():')
'static PyObject*\npy_myfunc(void)\n{'

如果 repl 是一个函数,它会对每一个不重叠的模式调用一次。这个函数会接收一个匹配对象作为参数,并返回替换用的字符串。例如:

>>> def dashrepl(matchobj):
...     if matchobj.group(0) == '-': return ' '
...     else: return '-'
>>> re.sub('-{1,2}', dashrepl, 'pro----gram-files')
'pro--gram files'
>>> re.sub(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE)
'Baked Beans & Spam'

模式可以是一个字符串或者一个正则表达式对象。

可选的参数 count 是要替换的模式出现的最大次数;count 必须是一个非负整数。如果省略或者是零,所有出现的地方都会被替换。对于模式的空匹配,只有在不和之前的匹配相邻时才会被替换,所以像 sub('x*', '-', 'abc') 这样的调用会返回 '-a-b-c-'。

除了上面提到的字符转义和反向引用,\g 会使用由 (?P...) 语法定义的命名组匹配的子字符串。 \g 使用对应的组号;因此 \g<2> 和 \2 是等价的,但在像 \g<2>0 这样的替换中不会产生歧义。 \20 会被理解为对第20组的引用,而不是对第2组的引用后面跟着字面字符 '0'。反向引用 \g<0> 会替换为正则表达式匹配的整个子字符串。

你可以把 repl 作为一个函数传入,这个函数会计算出要替换回原字符串的值。

5

下面的代码可以处理你给出的那个字符串示例。需要注意的是,这段代码非常针对这个特定格式的字符串,不能处理任何其他形式的字符串。换句话说,它只能适用于这种类型的字符串格式。

import re

ss = '''PART 1 DATA MODELS Chapter
2 Entity-Relationship Model 27

The sun is shining

PART 1 DATA MODELS Chapter
13 Entity-Relationship Model 45
'''

regx = re.compile('^(PART.*)(\n(\d*).*\s(\d+)\n)',re.MULTILINE)

def repl(mat):
    return ''.join((mat.group(1),' ',
                    str(int(mat.group(4))-int(mat.group(3))),
                    mat.group(2)))

for mat in regx.finditer(ss):
    print mat.groups()

print

print regx.sub(repl,ss)

结果

('PART 1 DATA MODELS Chapter', '\n2 Entity-Relationship Model 27\n', '2', '27')
('PART 1 DATA MODELS Chapter', '\n13 Entity-Relationship Model 45\n', '13', '45')

PART 1 DATA MODELS Chapter 25
2 Entity-Relationship Model 27

The sun is shining

PART 1 DATA MODELS Chapter 32
13 Entity-Relationship Model 45

编辑:我忘记加上 re.MULTILINE 这个标志了。

14

你可以给 re.sub 传递一个 lambda 函数,这个函数会接收每一个不重叠的匹配结果(也就是 re.MatchObject 对象),然后返回一个替换的字符串。举个例子:

import re    
print re.sub("(\d+)\+(\d+)",
             lambda m: str(int(m.group(1))+int(m.group(2))),
             "If 2+2 is 4 then 1+2+3+4 is 10")

输出结果是

如果 4 是 4,那么 3+7 就是 10

你可以很简单地把这个方法应用到你的问题上。

撰写回答