如何反向正则替换?
回到这个例子,
在Python中使用正则表达式处理相似字符以打印不同内容时遇到问题
我在想,我该如何把我做的正则替换反过来,直接打印出原来的文本呢?
也就是说,如果我有
text = "This is my first regex python example yahooa yahoouuee bbbiirdd"
作为我的原始文本,那么它的输出会是:
re.sub text = "tookhookisook isook mookyook fookirooksooktook pookyooktookhookonook..."
然后我想把这个输出再转换回原来的文本。
我该怎么做呢?
3 个回答
在Python或者其他正则表达式的工具中,你不能把正则替换的过程“反向转换”。
这是因为替换是单向的,就像一条单行道,只会返回一个新的字符串,没有什么神奇的反向功能。
下面用string.replace()来举个例子:
original_string = 'abc'
newstring = original_string.replace('a','b')
'bbc'
把新字符串变成'abc',并不是简单地把'a'替换成'b'。你不能从任何给定的正则表达式中创建一个“反向”的正则表达式。如果在这个例子中把'b'替换成'a',结果会是'aac',而不是'bbc'。
正则表达式的功能和string.replace()是一样的——它们都会返回一个新的字符串。它们并不会返回一个知道每次替换具体状态的对象。
如果你想做你想做的事情,有两个选择:
1- 创建一个自定义的类,代表一个字符串,并跟踪无限数量的正则表达式操作,这样你就可以在每个状态之间进行比较。
2- 跟大家一样,很多人也建议的做法:你只需要把原始字符串(或者它的副本)放到一边保存起来。
(这是为了简化@StoryTeller的回答)
Python中的字符串是不可变的。这意味着你并没有真正去“改变”原来的字符串,而是创建了一个新的字符串。你只需要保留对原字符串的引用。
补充说明
这里说的不可变,意思是字符串一旦创建,它的实际内容就不会再改变了。
>>> s = "abc"
>>> s[0]
'a'
>>> s[1] = 'd'
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
s[1] = 'd'
TypeError: 'str' object does not support item assignment
>>>
在上面的例子中,我可以让变量 s
指向另一个对象,但我赋值给它的字符串是固定的。所以当你使用 s.replace()
时,得到的结果是一个新的字符串,而原来的字符串并没有被改变。
>>> s.replace ('a', 'd')
'dbc'
>>> s
'abc'
>>>
看起来这个方法有效:
import re
tu = ('This is my first regex python example '
'yahooa yahoouuee bbbiirdd',
'bbbiirdd',
'fookirooksooktook',
'crrsciencezxxxxxscienceokjjsciencq')
reg = re.compile(r'([bcdfghj-np-tv-z])(\1?)')
dereg = re.compile('science([^aeiou])|([^aeiou])ook')
def Frepl(ma):
g1,g2 = ma.groups()
if g2: return 'science' + g2
else: return g1 + 'ook'
def Fderepl(ma):
g = ma.group(2)
if g: return g
else: return 2*ma.group(1)
for strt in tu:
resu = reg.sub(Frepl , strt)
bakk = dereg.sub(Fderepl, resu)
print ('----------------------------------\n'
'strt = %s\n' 'resu == %s\n'
'bakk == %s\n' 'bakk == start : %s'
% (strt, resu, bakk, bakk==strt))
编辑
首先,我更新了上面的代码:我去掉了 re.I
这个标志。之前这个标志会把像 'dD' 这样的部分当作重复字母来处理,所以它会变成 'scienceD',然后又变回 'DD'。
其次,我在代码中加入了一个字典。
它不是简单地把一个字母替换成字母+'ook',而是根据字母来进行替换。
比如,我选择把 'b' 替换成 'BAR',把 'c' 替换成 'CORE'……我把字典里的值都用大写字母写出来,这样结果看起来更清晰。其实可以用其他任何值。
程序会处理字母的大小写。我只在字典里放了 'T'、'Y'、'X',这只是为了测试。
import re
d = {'b':'BAR','c':'CORE','d':'DEAD','f':'FAN',
'g':'GO','h':'HHH','j':'JIU','k':'KOAN',
'l':'LOW','m':'MY','n':'NERD','p':'PI',
'q':'QIM','r':'ROAR','s':'SING','t':'TIP',
'v':'VIEW','w':'WAVE','x':'XOR',
'y':'YEAR','z':'ZOO',
'T':'tears','Y':'yearling','X':'xylophone'}
ded = dict((v,k) for k,v in d.iteritems())
print ded
tu = ('This is my first regex python example '
'Yahooa yahoouuee bbbiirdd',
'bbbiirdd',
'fookirooksooktook',
'crrsciencezxxxxxXscienceokjjsciencq')
reg = re.compile(r'([bcdfghj-np-tv-zBCDFGHJ-NP-TV-Z])(\1?)')
othergr = '|'.join(ded.keys())
dereg = re.compile('science([^aeiouAEIOU])|(%s)' % othergr)
def Frepl(ma, d=d):
g1,g2 = ma.groups()
if g2: return 'science' + g2
else: return d[g1]
def Fderepl(ma,ded=ded):
g = ma.group(2)
if g: return ded[g]
else: return 2*ma.group(1)
for strt in tu:
resu = reg.sub(Frepl , strt)
bakk = dereg.sub(Fderepl, resu)
print ('----------------------------------\n'
'strt = %s\n' 'resu == %s\n'
'bakk == %s\n' 'bakk == start : %s'
% (strt, resu, bakk, bakk==strt))
结果
----------------------------------
strt = This is my first regex python example Yahooa yahoouuee bbbiirdd
resu == tearsHHHiSING iSING MYYEAR FANiROARSINGTIP ROAReGOeXOR PIYEARTIPHHHoNERD eXORaMYPILOWe yearlingaHHHooa YEARaHHHoouuee sciencebBARiiROARscienced
bakk == This is my first regex python example Yahooa yahoouuee bbbiirdd
bakk == start : True
----------------------------------
strt = bbbiirdd
resu == sciencebBARiiROARscienced
bakk == bbbiirdd
bakk == start : True
----------------------------------
strt = fookirooksooktook
resu == FANooKOANiROARooKOANSINGooKOANTIPooKOAN
bakk == fookirooksooktook
bakk == start : True
----------------------------------
strt = crrsciencezxxxxxXscienceokjjsciencq
resu == COREsciencerSINGCOREieNERDCOREeZOOsciencexsciencexXORxylophoneSINGCOREieNERDCOREeoKOANsciencejSINGCOREieNERDCOREQIM
bakk == crrsciencezxxxxxXscienceokjjsciencq
bakk == start : True