Лучший способ сделать замену без учета регистра, но соответствовать регистру слова, подлежащего замене? - PullRequest
7 голосов
/ 09 февраля 2012

До сих пор я придумал метод, описанный ниже, но у меня вопрос: есть ли более короткий метод с таким же результатом?

Мой код:

input_str       = "myStrIngFullOfStUfFiWannAReplaCE_StUfFs"
replace_str     = "stuff"
replacer_str    = "banana"


print input_str
# prints: myStrIngFullOfStUfFiWannAReplaCE_StUfFs

if replace_str.lower() in input_str.lower():            # Check if even in the string
    begin_index = input_str.lower().find( replace_str )
    end_index   = begin_index + len( replace_str )

    replace_section = input_str[ begin_index : end_index ]

    case_list = []
    for char in replace_section:                        # Get cases of characters in the section to be replaced
        case_list.append( char.istitle() )

    while len( replacer_str ) > len(case_list):
        case_list += case_list

    sameCase_replacer_str   = ""                        # Set match the replacer string's case to the replace
    replacer_str            = replacer_str.lower()
    for index in range( len(replacer_str) ):
        char = replacer_str[ index ]
        case = case_list[ index ]
        if case == True:
            char = char.title()

        sameCase_replacer_str += char

    input_str = input_str.replace( replace_section , sameCase_replacer_str )

print input_str
# prints: myStrIngFullOfBaNaNAiWannAReplaCE_BaNaNAs

Ответы [ 3 ]

5 голосов
/ 09 февраля 2012

Я бы использовал что-то вроде этого:

import re

def replacement_func(match, repl_pattern):
    match_str = match.group(0)
    repl = ''.join([r_char if m_char.islower() else r_char.upper()
                   for r_char, m_char in zip(repl_pattern, match_str)])
    repl += repl_pattern[len(match_str):]
    return repl

input_str = "myStrIngFullOfStUfFiWannAReplaCE_StUfFs"
print re.sub('stuff',
             lambda m: replacement_func(m, 'banana'),
             input_str, flags=re.I)

Пример вывода:

myStrIngFullOfBaNaNaiWannAReplaCE_BaNaNas

Примечания:

  • Это обрабатывает случай, в котором разные совпадения имеют разные комбинации прописных и строчных букв.
  • Предполагается, что шаблон замены находится в нижнем регистре (в любом случае, его очень легко изменить).
  • Если шаблон замены длиннее совпадения, используется тот же случай, что и в шаблоне.
3 голосов
/ 09 февраля 2012

Вы можете передать flags=re.I в re.sub() в игнорирующем регистре

>>> input_str       = "myStrIngFullOfStUfFiWannAReplaCE_StUfFs"
>>> replace_str     = "stuff"
>>> replacer_str    = "banana"
>>> 
>>> import re
>>> from itertools import zip_longest
>>> tr = lambda x, y: ''.join([i[0].upper() if i[1] else i[0].lower() for i in zip_longest(y, [c.isupper() for c in x], fillvalue=(lambda : '' if len(x)>len(y) else x[-1].isupper())())])
>>> re.sub(replace_str, lambda m: tr(m.group(0), replacer_str), input_str, flags=re.I)
'myStrIngFullOfBaNaNaiWannAReplaCE_BaNaNas'
1 голос
/ 09 февраля 2012

Из кода очевидно, что шаблон регистра для замены создается из шаблона регистра соответствия путем его повторения (т. Е. StufF -> BanaNA). Имея это в виду, я сначала найду шаблон регистра для всей строки, а затем приведу строку в нужный регистр:

def to_case(s, cmap):
    'returns string cased according to map'
    return ''.join([c.upper() if m else c for (c,m) in zip(s,cmap)])

input_str       = "myStrIngFullOfStUfFiWannAReplaCE_StUfFs"
replace_str     = "stuff"
replacer_str    = "banana"

case_map = [c.istitle() for c in input_str] # initial case map
input_str_lower = input_str.lower()    

while replace_str.lower() in input_str_lower:            # Check if even in the string
    ind = input_str_lower.find(replace_str)  # find index
    cases = [case_map[(ind + i % len(replace_str))] for i in range(len(replacer_str))] # replacement case pattern
    case_map = case_map[:ind] + cases + case_map[ind + len(replace_str):]
    input_str_lower = input_str_lower.replace(replace_str, replacer_str, 1)

print to_case(input_str_lower, case_map)
# prints: myStrIngFullOfBaNaNAiWannAReplaCE_BaNaNAs
...