Not very nice, but this seems to work:
import re
def complex_replace(subject, ignore_lst, txt_to_replace, replacement_txt):
ignore_pattern = '|'.join([re.escape(ignore_lst[i]) for i in range(len(ignore_lst))])
str_idxs = [idx for tu in re.finditer(ignore_pattern, subject) for idx in tu.span()]
split_str = [
(subject[str_idxs[i]:str_idxs[i+1]], 'U' if i % 2 == 0 else 'M')
for i in range(len(str_idxs) - 1)
]
split_str.append((subject[str_idxs[-1]:len(subject)], 'M'))
res = ''.join(
[
substr[0].replace(txt_to_replace, replacement_txt)
if substr[1] == 'M' else substr[0] for substr in split_str
]
)
return res
What this does is the following:
- Build a regex pattern with the ignore list (items in the ignore list separated by |
- Build a list of indexes marking the start and the end of the substrings of the subject string matching the items in the ignore list
- Build a substrings list, where each of this list items consist of a tuple with the substring and a flag to mark the substring as not mutable ('U') or mutable ('M'). Add the end of the subject string (from the last index found in step 2 to the end of the subject string) to that substrings list
- Do the replacement using join and list comprehension, based on the tuples in the substrings list built in 3: only do the replacement if the substring is flagged as mutable ('M'), otherwise ('U'), take the substring unchanged
The following tests:
ignore_list = ['id_1']
test_str = "id_1Testid"
to_replace = 'id'
replacement = 'test2'
print(complex_replace(test_str, ignore_list, to_replace, replacement))
ignore_list = ['test', 'blah']
test_str = 'test blah testbidtest bitest testblue'
to_replace = 'bi'
replacement = 'tooTooT'
print(complex_replace(test_str, ignore_list, to_replace, replacement))
ignore_list = ['id_1', 'id_2']
test_str = "id_1id_2Testid_id_id"
to_replace = 'id'
replacement = 'test2'
print(complex_replace(test_str, ignore_list, to_replace, replacement))
ignore_list = ['aaidaa']
test_str = "aaidaaTestid"
to_replace = 'id'
replacement = 'test2'
print(complex_replace(test_str, ignore_list, to_replace, replacement))
ignore_list = ['aaidaa', 'aaidbb']
test_str = "aaidaaaaidbbTestidbyidby"
to_replace = 'id'
replacement = 'test2'
print(complex_replace(test_str, ignore_list, to_replace, replacement))
give the output below:
id_1Testtest2
test blah testtooTooTdtest tooTooTtest testblue
id_1id_2Testtest2_test2_test2
aaidaaTesttest2
aaidaaaaidbbTesttest2bytest2by