使用正则表达式保留中间字符串

Posted

技术标签:

【中文标题】使用正则表达式保留中间字符串【英文标题】:Retain intermediate string using regex 【发布时间】:2021-11-27 00:01:11 【问题描述】:

我想在多个 python 代码文件中的所有出现处将子字符串附加到字符串模式。然而,原始字符串遵循一个模式,并且每次都不是完全相同的字符串。 以下是一些变体示例:

Original Code:  a.b();
Want Code:      a.b().c();
Original Code:  a.b(param1=1);
Want Code:      a.b(param1=1, param2=2).c();
Original Code:  a.b(param1=1, param2=2);
Want Code:      a.b(param1=1, param2=2).c();
Original Code:  a.b(param1=D());
Want Code:      a.b(param1==D()).c();
Original Code:  X(a.b(param1=D()));
Want Code:      X(a.b(param1==D()).c());

更新: 由于我试图替换文件中的代码,因此该文件包含缩进和新行以提高可读性: 例如

Original Code:  X(a.b(
                     param1=D()
                     )
                 );

Want Code:      X(a.b(
                     param1=D()
                     ).c()
                 );
Original Code:  X(a.b(
                     param1=D(),
                     param2="qwerty"
                     )
                 );

Want Code:      X(a.b(
                     param1=D(),
                     param2="qwerty"
                     ).c()
                 );
Original Code:  X(a.b(
                       newObj())
                 );

Want Code:      X(a.b(
                       newObj()).c()
                 );

我并不真正关心函数 b 中传递的参数。我只需要在每次调用 a.b() 时附加对 c() 的调用。

我正在使用正则表达式'a.b(.*?)' 来检测适当的原始代码。我尝试使用 以下解决方案正则表达式:a.b($1).c()a.b(\1).c() 但无济于事。

【问题讨论】:

【参考方案1】:

你可以使用

a\.b\([^()]*\)(?=;)
a\.b 从字面上匹配并转义点 \([^()]*\) 使用否定字符类从左括号匹配到右括号 (?=;) 正向前瞻,在右侧断言 ;

Regex demo | Python demo

并替换为完整匹配 \g<0> 后跟 .c()

\g<0>.c()

例如:

import re

regex = r"a\.b\([^()]*\)(?=;)"

s = ("a.b();\n"
    "a.b(param1=1);\n"
    "a.b(param1=1, param2=2);")

result = re.sub(regex, r"\g<0>.c()", s)

if result:
    print (result)

输出

a.b().c();
a.b(param1=1).c();
a.b(param1=1, param2=2).c();

使用PyPi regex module匹配平衡括号:

a\.b(\((?>[^()]++|(?1))*\))

模式匹配:

a\.b匹配.b ( 捕获第 1 组 \(匹配( (?&gt;原子组(无回溯) [^()]++ 匹配除 () 之外的任何字符的 1+ 次出现 |或者 (?1) 递归第一个子模式(组 1) )* 关闭群组并可选择重复 \)匹配) )关闭第一组

Regex demo | Python demo

import regex

pattern = r'a\.b(\((?>[^()]++|(?1))*\))'
strings = [
    "a.b();",
    "a.b(param1=1);",
    "a.b(param1=1, param2=2);",
    "a.b(param1=d(abc=\"123\"));"
]

for s in strings:
    m = regex.match(pattern, s)
    if m:
        print(f"m.group().c()")

输出

a.b().c()
a.b(param1=1).c()
a.b(param1=1, param2=2).c()
a.b(param1=d(abc="123")).c()

【讨论】:

\([^()]*\) 假定第一个右括号是正确的。有没有办法确保捕获正确的右括号?例如对于输入 a.b(param1=d(abc="123"))。使用建议的正则表达式,它将替换不正确的a.b(param1=d(abc="123").c()) 的输入。预期的替换字符串应该是a.b(param1=d(abc="123")).c() @learningMyWayThru 如果要匹配平衡括号,则必须使用支持递归的PyPi regex module。查看此模式 a\.b(\((?&gt;[^()]++|(?1))*\)) regex101.com/r/lNXtKK/1 并查看 this example code 如何使用它 感谢您的详细解释。我正在使用此正则表达式替换以不同方式缩进的文件中的字符串。因此,上面的正则表达式适用于字符串在一行中但在有新行时不起作用。我尝试使用regex.match(pattern, s, flags=DOTALL) 让它忽略换行符,但无济于事。 @alwaysAStudent 看到这个Python demo 我认为上面链接的 Python 演示有效。非常感谢!!!!【参考方案2】:

这个怎么样:

模式:(a\.b\(.*?\))

替换:\1.c()

结果:

a.b(param1=1).c();
a.b(param1=1, param2=2).c();
a.b().c();

https://regex101.com/r/VwxWR3/1/

【讨论】:

以上是关于使用正则表达式保留中间字符串的主要内容,如果未能解决你的问题,请参考以下文章

C#正则表达式保留头尾替换中间为*

python使用正则表达式删除字符串中的其它字符只保留数字和字母

仅使用正则表达式在字符串中查找中间字符

正则表达式:数字开头中间字母结尾数字

小数点后保留2位小数的正则表达式

使用正则表达式替换仅保留正斜杠和数字