Ganti case sensitif

173

Apa cara termudah untuk melakukan penggantian string case-insensitive dengan Python?

Adam Ernst
sumber

Jawaban:

217

The stringtipe tidak mendukung ini. Anda mungkin lebih baik menggunakan sub metode ekspresi reguler dengan opsi 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'
Blair Conrad
sumber
11
Jika Anda hanya melakukan penggantian tunggal, atau ingin menyimpan baris kode, lebih efisien menggunakan subtitusi tunggal dengan re.sub dan flag (? I): re.sub ('(? I)' + re .escape ('hippo'), 'jerapah', 'Saya ingin hIPpo untuk ulang tahun saya')
D Coetzee
3
Mengapa kembali hanya untuk string huruf saja? Terima kasih.
Elena
8
@ Elena, itu tidak diperlukan 'hippo', tetapi akan berguna jika nilai to-replace dimasukkan ke fungsi, jadi ini benar-benar lebih dari contoh yang baik daripada yang lainnya.
Blair Conrad
2
Selain harus berusaha keras re.escape, ada jebakan lain di sini yang gagal dihindari jawaban ini, yang dicatat di stackoverflow.com/a/15831118/1709587 : sejak re.subproses melarikan diri dari urutan, seperti dicatat dalam docs.python.org/library/re.html#re .sub , Anda harus melarikan diri semua backslash dalam string pengganti Anda atau menggunakan lambda.
Mark Amery
84
import re
pattern = re.compile("hello", re.IGNORECASE)
pattern.sub("bye", "hello HeLLo HELLO")
# 'bye bye bye'
Tidak dikenal
sumber
17
Atau satu kalimat: re.sub('hello', 'bye', 'hello HeLLo HELLO', flags=re.IGNORECASE)
Louis Yang
Perhatikan bahwa re.subhanya mendukung flag ini sejak Python 2.7.
fuenfundachtzig
47

Dalam satu baris:

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'

Atau, gunakan argumen "bendera" opsional:

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'
Viebel
sumber
14

Melanjutkan jawaban bFloch, fungsi ini tidak akan berubah satu, tetapi semua kejadian lama dengan yang baru - dalam kasus yang tidak sensitif.

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
rsmoorthy
sumber
Bagus sekali. Jauh lebih baik daripada regex; ia menangani semua jenis karakter, sedangkan regex sangat cerewet tentang apa pun yang non-alfanumerik. IMHO jawaban yang dipilih.
fyngyrz
Yang harus Anda lakukan adalah keluar dari regex: jawaban yang diterima jauh lebih pendek dan lebih mudah dibaca daripada ini.
Fisikawan Gila
Escape hanya berfungsi untuk pencocokan, backslash di tujuan dapat mengacaukan semuanya.
ideasman42
4

Seperti kata Blair Conrad string.replace tidak mendukung ini.

Gunakan regex re.sub, tapi ingat untuk melarikan diri dari string pengganti terlebih dahulu. Perhatikan bahwa tidak ada opsi-flag di 2.6 untuk re.sub, jadi Anda harus menggunakan pengubah tertanam'(?i)' (atau objek-RE, lihat jawaban Blair Conrad). Juga, jebakan lain adalah bahwa sub akan memproses backslash lolos dalam teks pengganti, jika string diberikan. Untuk menghindari yang satu ini, Anda bisa memasukkan lambda.

Inilah fungsinya:

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'
John
sumber
4

Fungsi ini menggunakan fungsi str.replace()dan re.findall(). Ini akan menggantikan semua kejadian patternin stringdengan replcara case-insensitive.

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
Nico Bako
sumber
3

Ini tidak memerlukan RegularExp

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):] 
bFloch
sumber
3
Bagus, namun ini tidak mengubah semua kejadian lama dengan yang baru, tetapi hanya kejadian pertama.
rsmoorthy
5
Itu kurang terbaca dari versi regex. Tidak perlu menemukan kembali roda di sini.
Johannes Bittner
Akan menarik untuk melakukan perbandingan kinerja antara ini dan versi yang ditingkatkan, mungkin lebih cepat, yang penting untuk beberapa aplikasi. Atau mungkin lebih lambat karena ia bekerja lebih banyak di Python ditafsirkan.
D Coetzee
2

Pengamatan menarik tentang detail dan opsi sintaks:

Python 3.7.2 (tag / v3.7.2: 9a3ffc0492, 23 Desember 2018, 23:09:28) [MSC v.1916 64 bit (AMD64)] di win32

import re
old = "TREEROOT treeroot TREerOot"
re.sub(r'(?i)treeroot', 'grassroot', old)

'akar rumput akar rumput'

re.sub(r'treeroot', 'grassroot', old)

'TREEROOT akar rumput TREerOot'

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

'akar rumput akar rumput'

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

'TREEROOT akar rumput TREerOot'

Jadi awalan (? I) dalam ekspresi kecocokan atau menambahkan "flags = re.I" sebagai argumen keempat akan menghasilkan kecocokan case-insensitive. TETAPI, hanya menggunakan "re.I" sebagai argumen keempat tidak menghasilkan kecocokan case-sensitive.

Untuk perbandingan,

re.findall(r'treeroot', old, re.I)

['TREEROOT', 'treeroot', 'TREerOot']

re.findall(r'treeroot', old)

['akar pohon']

Murray
sumber
Ini tidak memberikan jawaban untuk pertanyaan itu. harap edit jawaban Anda untuk memastikan bahwa jawaban itu meningkat setelah jawaban lain yang sudah ada dalam pertanyaan ini.
hongsy
1

Saya sedang dikonversi ke urutan escape (gulir sedikit ke bawah), jadi saya mencatat bahwa re.sub mengkonversi karakter melarikan diri backslashed untuk melarikan diri dari urutan.

Untuk mencegahnya saya menulis yang berikut ini:

Ganti case sensitif.

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

Juga, jika Anda ingin mengganti dengan karakter escape, seperti jawaban lain di sini yang mendapatkan makna khusus karakter bashslash dikonversi untuk keluar dari urutan, cukup decode pencarian Anda dan, atau ganti string. Dalam Python 3, mungkin harus melakukan sesuatu seperti .decode ("unicode_escape") # python3

findtxt = findtxt.decode('string_escape') # python2
replacetxt = replacetxt.decode('string_escape') # python2
data = ireplace(findtxt, replacetxt, data)

Diuji dalam Python 2.7.8

Semoga itu bisa membantu.

Stan S.
sumber
0

tidak pernah memposting jawaban sebelumnya dan utas ini benar-benar tua tetapi saya datang dengan solusi lain dan mengira saya bisa mendapatkan respons Anda, saya tidak berpengalaman dalam pemrograman Python jadi jika ada kelemahan yang muncul untuk itu, harap tunjukkan karena pembelajaran yang baik: )

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))
anddan
sumber
2
Untuk belajar: umumnya ketika Anda melakukan pencarian dan mengganti sebuah string, lebih baik tidak harus mengubahnya menjadi array terlebih dahulu. Itu sebabnya jawaban pertama mungkin yang terbaik. Meskipun menggunakan modul eksternal, ia memperlakukan string sebagai satu keseluruhan string. Ini juga sedikit lebih jelas apa yang terjadi dalam proses.
isaaclw
Untuk belajar: sangat sulit bagi pengembang tanpa konteks untuk membaca kode ini dan menguraikan apa yang dilakukannya :)
Todd