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
replace, and you can involve a list of strings in that expression. It would seem that's totally doable if you expand the list into the expression string.idis found? If not possible before and after that? What result do you expect withtest_str = "abababidab_ababidab_abidababidab_idididid"andignore_list = ["ababidab", "2id", "idt"]. Having one unique solution to this input enable us to easily discriminate correct answers from wrong ones.print(complex_replace("id_id_1Testid", ['id_1'], 'id', 'test2')).