不区分大小写的替换

241

在Python中,最简单的进行不区分大小写字符串替换的方法是什么?

11个回答

280

string类型不支持此操作。最好使用带有正则表达式sub方法re.IGNORECASE选项。

>>> import re
>>> insensitive_hippo = re.compile(re.escape('hippo'), re.IGNORECASE)
>>> insensitive_hippo.sub('giraffe', 'I want a hIPpo for my birthday')
'I want a giraffe for my birthday'

12
如果您只需要进行一次替换,或者想节省代码行数,使用re.sub和(?i)标志进行单个替换更为高效:re.sub('(?i)' + re.escape('hippo'), 'giraffe', 'I want a hIPpo for my birthday')。将输出结果中'hippo'替换为'giraffe',并且不区分大小写。 - Chiara Coetzee
5
为什么针对只包含字母的字符串需要使用re.escape?谢谢。 - Eleno
12
@Elena,如果要替换的值被传递到一个函数中,这个技巧对'hippo'不是必需的,但会很有用,因此这只是一个好的例子而已。 - Blair Conrad
4
除了需要使用 re.escape 转义你的搜索词之外,还有一个陷阱,这个答案没有避免,在https://dev59.com/5XNA5IYBdhLWcg3wjOhS#15831118中已经注意到:由于`re.sub`处理转义序列,如 https://docs.python.org/library/re.html#re.sub 中所示,你需要在替换字符串中要么转义所有反斜杠,要么使用 lambda 函数。 - Mark Amery
1
r'xxxA\BCxxxA\BCxxx'中用r'A\BC'替换为r'D\EF'是无效的 - 正确答案是来自johv的那个。 - stenci

111
import re
pattern = re.compile("hello", re.IGNORECASE)
pattern.sub("bye", "hello HeLLo HELLO")
# 'bye bye bye'

38
或者一句话概括:re.sub('hello', 'bye', 'hello HeLLo HELLO', flags=re.IGNORECASE) 使用正则表达式将字符串中的所有'hello'替换为'bye',忽略大小写。 - Louis Yang
1
请注意,re.sub 仅自 Python 2.7 起支持此标志。 - fuenfundachtzig
有时候它会返回一些随机字符串。 - urek mazino

61
在一行中:
import re
re.sub("(?i)hello","bye", "hello HeLLo HELLO") #'bye bye bye'
re.sub("(?i)he\.llo","bye", "he.llo He.LLo HE.LLO") #'bye bye bye'

或者,使用可选的 "flags" 参数:

import re
re.sub("hello", "bye", "hello HeLLo HELLO", flags=re.I) #'bye bye bye'
re.sub("he\.llo", "bye", "he.llo He.LLo HE.LLO", flags=re.I) #'bye bye bye'

23

在bFloch的答案基础上,这个函数将会以不区分大小写的方式替换所有出现的旧字符串为新字符串。

def ireplace(old, new, text):
    idx = 0
    while idx < len(text):
        index_l = text.lower().find(old.lower(), idx)
        if index_l == -1:
            return text
        text = text[:index_l] + new + text[index_l + len(old):]
        idx = index_l + len(new) 
    return text

3
做得非常好,比正则表达式更好;它处理各种字符,而正则表达式对任何非字母数字的东西都很挑剔。在我看来,这是首选答案。 - fyngyrz
你所要做的就是转义正则表达式: 接受的答案比这个要短得多,更易于阅读。 - Mad Physicist
转义只适用于匹配,目标字符串中的反斜杠仍然可能会导致问题。 - ideasman42
可能是不区分大小写替换的最快方法,已经测试过使用数组字符串和使用正则表达式。 - Eugene

10

正如Blair Conrad所说,string.replace不支持此操作。

可以使用正则表达式re.sub,但请记得先转义替换字符串。注意,在2.6版本的re.sub中没有flags选项,因此您需要使用嵌入式修饰符'(?i)'(或RE对象,请参见Blair Conrad的答案)。另外,另一个陷阱是如果传入的是字符串,sub将处理替换文本中的反斜杠转义字符。一种避免这个问题的方法是使用lambda函数。

以下是一个函数示例:

import re
def ireplace(old, repl, text):
    return re.sub('(?i)'+re.escape(old), lambda m: repl, text)

>>> ireplace('hippo?', 'giraffe!?', 'You want a hiPPO?')
'You want a giraffe!?'
>>> ireplace(r'[binfolder]', r'C:\Temp\bin', r'[BinFolder]\test.exe')
'C:\\Temp\\bin\\test.exe'

7

此函数同时使用了 str.replace()re.findall() 函数。它会以不区分大小写的方式,将 string 中所有出现的 pattern 替换为 repl

def replace_all(pattern, repl, string) -> str:
   occurences = re.findall(pattern, string, re.IGNORECASE)
   for occurence in occurences:
       string = string.replace(occurence, repl)
       return string

6

这不需要使用正则表达式

def ireplace(old, new, text):
    """ 
    Replace case insensitive
    Raises ValueError if string not found
    """
    index_l = text.lower().index(old.lower())
    return text[:index_l] + new + text[index_l + len(old):] 

3
很好,但是这并没有把所有“old”替换成“new”,只替换了第一个出现的。 - rsmoorthy
6
正则表达式的版本比较易读。这里不需要重新发明轮子。 - Johannes Bittner
做一次性能比较这个版本和被赞同的版本之间的差异会很有趣,它可能会更快,对于某些应用程序来说这很重要。或者它可能会更慢,因为它在解释Python时需要执行更多的工作。 - Chiara Coetzee

6

有关语法细节和选项的有趣观察:

# Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)] on win32
>>> import re
>>> old = "TREEROOT treeroot TREerOot"

>>> re.sub(r'(?i)treeroot', 'grassroot', old)
'grassroot grassroot grassroot'

>>> re.sub(r'treeroot', 'grassroot', old)
'TREEROOT grassroot TREerOot'

>>> re.sub(r'treeroot', 'grassroot', old, flags=re.I)
'grassroot grassroot grassroot'

>>> re.sub(r'treeroot', 'grassroot', old, re.I)
'TREEROOT grassroot TREerOot'

在匹配表达式中使用(?i)前缀或在第四个参数中添加flags=re.I将导致不区分大小写的匹配,但仅使用re.I作为第四个参数不会导致不区分大小写的匹配。
与之相比:
>>> re.findall(r'treeroot', old, re.I)
['TREEROOT', 'treeroot', 'TREerOot']

>>> re.findall(r'treeroot', old)
['treeroot']

这并没有回答问题。请[编辑]您的回答,确保它比已经存在的其他答案更好。 - hongsy
1
re.sub文档中可以看到它有5个参数:re.sub(pattern, repl, string, count=0, flags=0),这就是为什么flags=re.I可以工作,但尝试将其作为位置参数传递会失败,因为它的位置不正确。 - DavidP

1
i='I want a hIPpo for my birthday'
key='hippo'
swp='giraffe'

o=(i.lower().split(key))
c=0
p=0
for w in o:
    o[c]=i[p:p+len(w)]
    p=p+len(key+w)
    c+=1
print(swp.join(o))

2
学习时:通常在对字符串进行搜索和替换时,最好不要先将其转换为数组。这就是为什么第一个答案可能是最好的原因。虽然它使用了外部模块,但它将字符串视为一个整体。在过程中发生的事情也更加清晰。 - isaaclw
学习时:对于没有上下文的开发人员来说,阅读这段代码并解密它的功能非常困难 :) - Todd
任何含有 counter++ 的代码通常都不好。 - LazerDance

1

我遇到了\t被转换成转义序列的问题(请向下滚动一点),所以我注意到re.sub会将反斜杠转义字符转换为转义序列。

为了防止这种情况,我写了以下内容:

不区分大小写地替换。

import re
    def ireplace(findtxt, replacetxt, data):
        return replacetxt.join(  re.compile(findtxt, flags=re.I).split(data)  )

此外,如果您希望将其替换为转义字符,就像这里的其他答案一样,将特殊含义的反斜杠字符转换为转义序列,只需解码您的查找和(或)替换字符串即可。在Python 3中,可能需要执行类似于.decode("unicode_escape")的操作。# python3
findtxt = findtxt.decode('string_escape') # python2
replacetxt = replacetxt.decode('string_escape') # python2
data = ireplace(findtxt, replacetxt, data)

在Python 2.7.8中测试通过


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接