Reputation: 1348
I have three lists: filePath, textToFind, textToReplace. I need to open each file at the given filePath, find a line and replace a line. The lists are always in order and always the same length. Here the code:
for i, path in enumerate(filePath):
for line in fileinput.input(path, inplace=1):
sys.stdout.write(line.replace(textToFind[i], textToReplace[i]))
The problem is that textToFind can be found numerous times in the file and so this code replaces all occurrences of the text it finds with the current index position of textToReplace. I need it to break when it finds the item the first time and then move on to the next iteration. How can I do this?
Upvotes: 0
Views: 441
Reputation: 140307
You need to detect when there's something to replace. If found, flag, but continue writing the rest of the lines or you'll truncate the file.
(note that using zip
on the 3 lists avoids carrying the index)
for path,find,replace in zip(filePath,textToFind,textToReplace):
match_found = False
for line in fileinput.input(path, inplace=1):
if match_found:
# keep writing the rest of lines, unchanged
sys.stdout.write(line)
else:
# try to replace
rep = line.replace(find, replace)
sys.stdout.write(rep)
if line!=rep:
# flag: don't replace anything till the end
match_found = True
EDIT: after a small discussion with the author of another answer, I think his 2 loops pattern is better than 1 with a flag, so I borrowed it, saves the need for the flag, must be slightly faster:
for path,find,replace in zip(filePath,textToFind,textToReplace):
handle = fileinput.input(path, inplace=1)
for line in handle:
rep = line.replace(find, replace)
sys.stdout.write(rep)
if line!=rep:
break
for line in handle:
sys.stdout.write(line)
Upvotes: 2
Reputation: 1100
My solution has significantly been reduced in elegance. However I still feel like offering an alternative with a temporary file.
# loop over every path, thing to find and replacement in the lists
for path, needle, replacement in zip(filePath, textToFind, textToReplace):
with open(path) as read_handle:
with open(path + '.tmp', 'w+') as write_handle:
# first print and replace
for line in read_handle:
write_handle.write(line.replace(needle, replacement))
# if we found something
if needle in line:
break # quit this the inner for-loop
# the remaining lines should be printed without modification
for line in read_handle:
write_handle.write(line)
# overwrite the file with the temporary file
shutil.move(path + '.tmp', path)
Upvotes: 2
Reputation: 48120
As an answers to your actual question:
"How to find and replace line only once when found multiple times in Python".
str.replace()
has a optional maxreplace
option that limits the number of occurrence to be replaced. As per the document:
string.replace(s, old, new[, maxreplace]):
Return a copy of string s with all occurrences of substring old replaced by new. If the optional argument maxreplace is given, the first maxreplace occurrences are replaced.
For example:
>>> my_test_string = 'Hello Hello Hello'
# v maxreplace as `1`
>>> my_test_string.replace('Hello', 'World', 1)
'World Hello Hello'
# ^ Only first word is replaced
Upvotes: 1