regex 如何使用字典在一个字符串中进行多次替换?

exdqitrt  于 2023-02-05  发布在  其他
关注(0)|答案(8)|浏览(147)

假设我们有:

d = {
    'Спорт':'Досуг',
    'russianA':'englishA'
}

s = 'Спорт russianA'

如何将d的任何键在s中的每一次出现替换为相应的值(在本例中,结果将是'Досуг englishA')?

ovfsdjhp

ovfsdjhp1#

使用re:

import re

s = 'Спорт not russianA'
d = {
'Спорт':'Досуг',
'russianA':'englishA'
}

pattern = re.compile(r'\b(' + '|'.join(d.keys()) + r')\b')
result = pattern.sub(lambda x: d[x.group()], s)
# Output: 'Досуг not englishA'

这将只匹配整个单词。如果不需要,请使用以下模式:

pattern = re.compile('|'.join(d.keys()))

注意,在这种情况下,如果某些字典条目是其他条目的子字符串,则应该按长度降序对单词进行排序。

cczfrluj

cczfrluj2#

您可以使用reduce函数:

reduce(lambda x, y: x.replace(y, dict[y]), dict, s)
3df52oht

3df52oht3#

解决方案在这里找到(我喜欢它的简单):

def multipleReplace(text, wordDict):
    for key in wordDict:
        text = text.replace(key, wordDict[key])
    return text
vbkedwbf

vbkedwbf4#

单向,无环

d = {
'Спорт':'Досуг',
'russianA':'englishA'
}

s = 'Спорт russianA'.split()
for n,i in enumerate(s):
    if i in d:
        s[n]=d[i]
print ' '.join(s)
d4so4syb

d4so4syb5#

与ghostdog74几乎相同,尽管是独立创建的。一个不同之处是,使用d.get()代替d[]可以处理不在dict中的项。

>>> d = {'a':'b', 'c':'d'}
>>> s = "a c x"
>>> foo = s.split()
>>> ret = []
>>> for item in foo:
...   ret.append(d.get(item,item)) # Try to get from dict, otherwise keep value
... 
>>> " ".join(ret)
'b d x'
htrmnn0y

htrmnn0y6#

如果key有空间,它会失败,这是一个类似ghostdog74和extaneons的压缩解决方案:

d = {
'Спорт':'Досуг',
'russianA':'englishA'
}

s = 'Спорт russianA'

' '.join(d.get(i,i) for i in s.split())
wqnecbli

wqnecbli7#

我在类似的情况下使用了这个方法(我的字符串都是大写的):

def translate(string, wdict):
    for key in wdict:
        string = string.replace(key, wdict[key].lower())
    return string.upper()

希望这能在某种程度上有所帮助...:)

bkhjykvo

bkhjykvo8#

使用正则表达式

我们可以创建一个正则表达式来匹配查找字典中的任意键,方法是创建正则表达式来匹配每个键,并将它们与|组合在一起。我们使用re.sub来进行替换,方法是给它一个函数来进行替换(当然,这个函数将进行字典查找)。

import re

# assuming global `d` and `s` as in the question

# a function that does the dict lookup with the global `d`.
def lookup(match):
    return d[match.group()]

# Make the regex.
joined = '|'.join(re.escape(key) for key in d.keys())
pattern = re.compile(joined)

result = pattern.sub(lookup, s)

这里,re.escape用于转义替换中任何具有特殊含义的字符(这样它们就不会干扰正则表达式的构建,并且可以按字面进行匹配)。
此正则表达式模式将匹配出现在任何位置的子字符串,即使它们是单词的一部分或跨越多个单词。要避免此问题,请修改正则表达式,使其检查单词边界:

# pattern = re.compile(joined)
pattern = re.compile(rf'\b({joined})\b')

迭代使用str.replace

只需遍历查找字典的.items(),并使用each调用.replace,由于这个方法是returns a new string, and does not (cannot) modify the string in place,我们必须在循环中重新分配结果:

for to_replace, replacement in d.items():
    s = s.replace(to_replace, replacement)

这种方法编写简单,易于理解,但是它有许多警告。
首先,它的缺点是它按特定顺序连续工作。也就是说,每个替换都有可能干扰其他替换。请考虑:

s = 'one two'
s = s.replace('one', 'two')
s = s.replace('two', 'three')

这将产生'three three',而不是'two three',因为来自第一替换的'two'本身将在第二步骤中被替换。然而,在它 * 应该 * 以这种方式工作的罕见情况下,这种方法是唯一实用的方法。
这种方法也不能很容易地固定以尊重单词边界,因为它必须匹配文本,并且“单词边界”可以用多种不同的方式来标记--通过不同种类的空格,但也可以在字符串的开头和结尾使用 without text
最后,请记住,dict不是这种方法的理想数据结构,如果我们将遍历dict,那么它的键查找能力将是无用的;和in Python 3.5 and below, the order of dict s is not guaranteed(这使得顺序替换问题变得更糟)。相反,最好为替换指定一个元组列表:

d = [('Спорт', 'Досуг'), ('russianA', 'englishA')]
s = 'Спорт russianA'

for to_replace, replacement in d: # no more `.items()` call
    s = s.replace(to_replace, replacement)

通过标记化

如果首先将字符串分割(tokenized),那么问题就会变得简单得多,这样一来,任何应该被替换的内容现在都是dict键的 exact match,这样就可以直接使用dict的查找,一次处理完整个字符串,同时也不用构建自定义正则表达式。
假设我们想要匹配完整的单词,我们可以使用一个更简单的、硬编码的正则表达式来匹配空格,它使用一个捕获组;通过将其传递给re.split,我们将字符串拆分为空白和非空白部分。

import re

tokenizer = re.compile('([ \t\n]+)')
tokenized = tokenizer.split(s)

现在我们在字典中查找每个标记:如果存在,就应该用对应的值替换,否则就不去管它(相当于用它自己替换它),字典.get方法很适合这个任务,最后,我们使用join the pieces back up,这样:

s = ''.join(d.get(token, token) for token in tokenized)

更普遍的情况是,例如,如果要替换的字符串中可能有空格,则需要不同的标记化规则。然而,通常可以找到比第一节中的正则表达式更简单的标记化规则(通过暴力强制匹配所有键)。

特殊情况:替换单个字符

如果dict的键都是一个字符(从技术上讲,是Unicode码位),则可以使用更多特定的技术,详细信息请参见Best way to replace multiple characters in a string?

相关问题