正则表达式中的算术运算

时间:2011-07-26 02:06:07

标签: python regex

我使用的是gedit regex插件(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 个答案:

答案 0 :(得分:8)

您可以传递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

您可以轻松地将其应用于您的问题。

答案 1 :(得分: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标志

答案 2 :(得分:3)

我不知道你可以在正则表达式中进行算术运算或其他计算。如果有一个支持它的正则表达式引擎,它会非常漂亮!但我的理解是,如果没有大幅减慢正则表达式引擎的速度,这将是不切实际的。

我认为您最好的选择是使用sub正则表达式函数/方法:

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

返回字符串   通过替换最左边的非重叠事件获得   替换repl中的字符串模式。如果找不到模式,   string返回不变。 repl可以是字符串或函数;如果   它是一个字符串,其中的任何反斜杠转义都被处理。那就是\ n   转换为单个换行符,\ r \ n转换为a   换行,等等。诸如\ 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'
     

模式可以是字符串或RE对象。

     

可选参数count是模式的最大数量   要被替换的事件; count必须是非负整数。如果   省略或为零,所有出现都将被替换。空的比赛   只有在与前一个匹配不相邻时才替换该模式,   所以sub('x *',' - ','abc')返回'-a-b-c - '。

     

除了描述的字符转义和反向引用   在上面,\ g将使用由名为group的组匹配的子字符串   name,由(?P ...)语法定义。 \ g使用​​了   相应的组号; \克LT 2 - ;因此相当于\ 2,但是   在诸如\ g< 2> 0的替换中不是模糊的。 \ 20会   解释为对第20组的引用,而不是对第2组的引用   后跟字面字符'0'。反向引用\ g< 0>   在RE匹配的整个子字符串中替换。

您可以将repl作为一个函数传递,该函数计算要替换回原始字符串的值。

答案 3 :(得分:1)

除非gedit是Python的超集,否则它将不允许在您尝试使用(\3-2)时在replace-regex中进行操作。在任何情况下,\3都是一个字符串,您需要先使用int()进行转换。 因此,您必须将其分解为单独的 re.search(...),计算插入的pageno,然后插入。

第二个问题是你没有匹配'2'的页面长度,你在=中硬编码了 - 你想要你的正则表达式从第二行的开头匹配吗?

(在任何情况下,你的多线匹配只会匹配PART之后的一行,如果这是你的意图。)

这里用普通的Python正则表达式实现:

for (chap,sect,page) in re.finditer(r'^(PART.*)\n(.*\s+(\d+))\n', input, re.M):
    print chap, int(page)-2
    print sect

(我试图将其包装为repl fn paginate_chapter(matchobj),但是无法让re.sub可靠地调用它...)