正则表达式仅在不成对的情况下拆分特定字符

1 投票
4 回答
779 浏览
提问于 2025-04-15 17:13

在这个讨论串中,我找到了最快的字符串替换算法,链接在这里。我一直在尝试修改其中一个算法,以满足我的需求,特别是这个由gnibbler提供的。

我在这里再解释一下我的问题,以及我遇到的麻烦。

假设我有一个字符串,看起来像这样:

str = "The &yquick &cbrown &bfox &Yjumps over the &ulazy dog"

你会注意到字符串中有很多地方有一个“&”符号,后面跟着一个字符(比如“&y”和“&c”)。我需要把这些字符替换成我在字典中有的合适值,像这样:

dict = {"y":"\033[0;30m",
        "c":"\033[0;31m",
        "b":"\033[0;32m",
        "Y":"\033[0;33m",
        "u":"\033[0;34m"}

使用我之前讨论串中提供的gnibbler的解决方案,我现在的解决方案是:

myparts = tmp.split('&')
myparts[1:]=[dict.get(x[0],"&"+x[0])+x[1:] for x in myparts[1:]]
result = "".join(myparts)

这个方法可以正确替换字符,并且在找不到字符时不会出错。唯一的问题是,没有简单的方法可以真正在输出中保留一个“&”符号。我能想到的最简单的方法是把我的字典改成:

dict = {"y":"\033[0;30m",
        "c":"\033[0;31m",
        "b":"\033[0;32m",
        "Y":"\033[0;33m",
        "u":"\033[0;34m",
        "&":"&"}

然后把我的“split”调用改成对那些后面没有其他“&”符号的“&”进行正则表达式分割。

>>> import re
>>> tmp = "&yI &creally &blove A && W &uRootbeer."
>>> tmp.split('&')
['', 'yI ', 'creally ', 'blove A ', '', ' W ', 'uRootbeer.']
>>> re.split('MyRegex', tmp)
['', 'yI ', 'creally ', 'blove A ', '&W ', 'uRootbeer.']

基本上,我需要一个正则表达式,它可以在一对中的第一个“&”符号和每个单独的“&”符号上进行分割,以便我可以通过我的字典来处理它。

如果有人有更好的解决方案,请随时告诉我。

4 个回答

0

可以用一个循环来查找字符串中的'&'符号,具体做法是用这个表达式:while (q = str.find('&', p)) != -1。这个循环会一直进行,直到找不到'&'符号为止。在每次找到'&'符号后,我们可以把它左边的部分(从p + 2到q - 1之间的内容)和替换的值一起添加到结果中。

0

re.sub可以满足你的需求。它需要一个正则表达式模式,并且可以接受一个函数来处理匹配到的内容,然后返回替换后的结果。下面的例子中,如果&后面的字符不在字典里,就不会进行替换。而&&会被替换成&,这样可以避免后面跟着字典中字符的&被误处理。

另外,'str'和'dict'这两个变量名不好,因为它们会覆盖掉Python内置的同名函数。

在下面的's'中,'& cat'不会受到影响,而'&&cat'会变成"&cat",这样就抑制了&c的翻译。

import re

s = "The &yquick &cbrown &bfox & cat &&cat &Yjumps over the &ulazy dog"

D = {"y":"\033[0;30m",
     "c":"\033[0;31m",
     "b":"\033[0;32m",
     "Y":"\033[0;33m",
     "u":"\033[0;34m",
     "&":"&"}

def func(m):
    return D.get(m.group(1),m.group(0))

print repr(re.sub(r'&(.)',func,s))

输出结果:

'The \x1b[0;30mquick \x1b[0;31mbrown \x1b[0;32mfox & cat &cat \x1b[0;33mjumps over the \x1b[0;34mlazy dog'

-Mark

2

你可以使用一种叫做“负向前瞻”的技巧(前提是你用的正则表达式引擎支持这个功能),这样就可以只匹配那些前面没有另一个“&”符号的“&”符号。

/(?<!&)&/

撰写回答