How do I search and replace text in a file using Python 3?
Here is my code:
import os
import sys
import fileinput
print ("Text to search for:")
textToSearch = input( "> " )
print ("Text to replace it with:")
textToReplace = input( "> " )
print ("File to perform Search-Replace on:")
fileToSearch = input( "> " )
#fileToSearch = 'D:\dummy1.txt'
tempFile = open( fileToSearch, 'r+' )
for line in fileinput.input( fileToSearch ):
if textToSearch in line :
print('Match Found')
else:
print('Match Not Found!!')
tempFile.write( line.replace( textToSearch, textToReplace ) )
tempFile.close()
input( '\n\n Press Enter to exit...' )
Input file:
hi this is abcd hi this is abcd
This is dummy text file.
This is how search and replace works abcd
When I search and replace 'ram' by 'abcd' in above input file, it works as a charm. But when I do it vice-versa i.e. replacing 'abcd' by 'ram', some junk characters are left at the end.
Replacing 'abcd' by 'ram'
hi this is ram hi this is ram
This is dummy text file.
This is how search and replace works rambcd
As pointed out by michaelb958, you cannot replace in place with data of a different length because this will put the rest of the sections out of place. I disagree with the other posters suggesting you read from one file and write to another. Instead, I would read the file into memory, fix the data up, and then write it out to the same file in a separate step.
# Read in the file
with open('file.txt', 'r') as file :
filedata = file.read()
# Replace the target string
filedata = filedata.replace('abcd', 'ram')
# Write the file out again
with open('file.txt', 'w') as file:
file.write(filedata)
Unless you've got a massive file to work with which is too big to load into memory in one go, or you are concerned about potential data loss if the process is interrupted during the second step in which you write data to the file.
fileinput already supports inplace editing. It redirects stdout to the file in this case:
#!/usr/bin/env python3
import fileinput
with fileinput.FileInput(filename, inplace=True, backup='.bak') as file:
for line in file:
print(line.replace(text_to_search, replacement_text), end='')
As Jack Aidley had posted and J.F. Sebastian pointed out, this code will not work:
# Read in the file
filedata = None
with file = open('file.txt', 'r') :
filedata = file.read()
# Replace the target string
filedata.replace('ram', 'abcd')
# Write the file out again
with file = open('file.txt', 'w') :
file.write(filedata)`
But this code WILL work (I've tested it):
f = open(filein,'r')
filedata = f.read()
f.close()
newdata = filedata.replace("old data","new data")
f = open(fileout,'w')
f.write(newdata)
f.close()
Using this method, filein and fileout can be the same file, because Python 3.3 will overwrite the file upon opening for write.
You can do the replacement like this
f1 = open('file1.txt', 'r')
f2 = open('file2.txt', 'w')
for line in f1:
f2.write(line.replace('old_text', 'new_text'))
f1.close()
f2.close()
You can also use pathlib.
from pathlib2 import Path
path = Path(file_to_search)
text = path.read_text()
text = text.replace(text_to_search, replacement_text)
path.write_text(text)
(pip install python-util)
from pyutil import filereplace
filereplace("somefile.txt","abcd","ram")
Will replace all occurences of "abcd" with "ram".
The function also supports regex by specifying regex=True
from pyutil import filereplace
filereplace("somefile.txt","\\w+","ram",regex=True)
Disclaimer: I'm the author (https://github.com/MisterL2/python-util)
Open the file in read mode. Read the file in string format. Replace the text as intended. Close the file. Again open the file in write mode. Finally, write the replaced text to the same file.
try:
with open("file_name", "r+") as text_file:
texts = text_file.read()
texts = texts.replace("to_replace", "replace_string")
with open(file_name, "w") as text_file:
text_file.write(texts)
except FileNotFoundError as f:
print("Could not find the file you are trying to read.")
Late answer, but this is what I use to find and replace inside a text file:
with open("test.txt") as r:
text = r.read().replace("THIS", "THAT")
with open("test.txt", "w") as w:
w.write(text)
DEMO
With a single with block, you can search and replace your text:
with open('file.txt','r+') as f:
filedata = f.read()
filedata = filedata.replace('abc','xyz')
f.truncate(0)
f.write(filedata)
Your problem stems from reading from and writing to the same file. Rather than opening fileToSearch for writing, open an actual temporary file and then after you're done and have closed tempFile, use os.rename to move the new file over fileToSearch.
My variant, one word at a time on the entire file.
I read it into memory.
def replace_word(infile,old_word,new_word):
if not os.path.isfile(infile):
print ("Error on replace_word, not a regular file: "+infile)
sys.exit(1)
f1=open(infile,'r').read()
f2=open(infile,'w')
m=f1.replace(old_word,new_word)
f2.write(m)
Using re.subn it is possible to have more control on the substitution process, such as word splitted over two lines, case-(in)sensitive match. Further, it returns the amount of matches which can be used to avoid waste of resources if the string is not found.
import re
file = # path to file
# they can be also raw string and regex
textToSearch = r'Ha.*O' # here an example with a regex
textToReplace = 'hallo'
# read and replace
with open(file, 'r') as fd:
# sample case-insensitive find-and-replace
text, counter = re.subn(textToSearch, textToReplace, fd.read(), re.I)
# check if there is at least a match
if counter > 0:
# edit the file
with open(file, 'w') as fd:
fd.write(text)
# summary result
print(f'{counter} occurence of "{textToSearch}" were replaced with "{textToReplace}".')
Some regex:
add the re.I flag, short form of re.IGNORECASE, for a case-insensitive match
for multi-line replacement re.subn(r'\n*'.join(textToSearch), textToReplace, fd.read()), depending on the data also '\n{,1}'. Notice that for this case textToSearch must be a pure string, not a regex!
Besides the answers already mentioned, here is an explanation of why you have some random characters at the end:
You are opening the file in r+ mode, not w mode. The key difference is that w mode clears the contents of the file as soon as you open it, whereas r+ doesn't.
This means that if your file content is "123456789" and you write "www" to it, you get "www456789". It overwrites the characters with the new input, but leaves any remaining input untouched.
You can clear a section of the file contents by using truncate(<startPosition>), but you are probably best off saving the updated file content to a string first, then doing truncate(0) and writing it all at once.
Or you can use my library :D
I got the same issue. The problem is that when you load a .txt in a variable you use it like an array of string while it's an array of character.
swapString = []
with open(filepath) as f:
s = f.read()
for each in s:
swapString.append(str(each).replace('this','that'))
s = swapString
print(s)
I tried this and used readlines instead of read
with open('dummy.txt','r') as file:
list = file.readlines()
print(f'before removal {list}')
for i in list[:]:
list.remove(i)
print(f'After removal {list}')
with open('dummy.txt','w+') as f:
for i in list:
f.write(i)
you can use sed or awk or grep in python (with some restrictions). Here is a very simple example. It changes banana to bananatoothpaste in the file. You can edit and use it. ( I tested it worked...note: if you are testing under windows you should install "sed" command and set the path first)
import os
file="a.txt"
oldtext="Banana"
newtext=" BananaToothpaste"
os.system('sed -i "s/{}/{}/g" {}'.format(oldtext,newtext,file))
#print(f'sed -i "s/{oldtext}/{newtext}/g" {file}')
print('This command was applied: sed -i "s/{}/{}/g" {}'.format(oldtext,newtext,file))
if you want to see results on the file directly apply: "type" for windows/ "cat" for linux:
####FOR WINDOWS:
os.popen("type " + file).read()
####FOR LINUX:
os.popen("cat " + file).read()
I have done this:
#!/usr/bin/env python3
import fileinput
import os
Dir = input ("Source directory: ")
os.chdir(Dir)
Filelist = os.listdir()
print('File list: ',Filelist)
NomeFile = input ("Insert file name: ")
CarOr = input ("Text to search: ")
CarNew = input ("New text: ")
with fileinput.FileInput(NomeFile, inplace=True, backup='.bak') as file:
for line in file:
print(line.replace(CarOr, CarNew), end='')
file.close ()
I modified Jayram Singh's post slightly in order to replace every instance of a '!' character to a number which I wanted to increment with each instance. Thought it might be helpful to someone who wanted to modify a character that occurred more than once per line and wanted to iterate. Hope that helps someone. PS- I'm very new at coding so apologies if my post is inappropriate in any way, but this worked for me.
f1 = open('file1.txt', 'r')
f2 = open('file2.txt', 'w')
n = 1
# if word=='!'replace w/ [n] & increment n; else append same word to
# file2
for line in f1:
for word in line:
if word == '!':
f2.write(word.replace('!', f'[{n}]'))
n += 1
else:
f2.write(word)
f1.close()
f2.close()
def word_replace(filename,old,new):
c=0
with open(filename,'r+',encoding ='utf-8') as f:
a=f.read()
b=a.split()
for i in range(0,len(b)):
if b[i]==old:
c=c+1
old=old.center(len(old)+2)
new=new.center(len(new)+2)
d=a.replace(old,new,c)
f.truncate(0)
f.seek(0)
f.write(d)
print('All words have been replaced!!!')
I have worked this out as an exercise of a course: open file, find and replace string and write to a new file.
class Letter:
def __init__(self):
with open("./Input/Names/invited_names.txt", "r") as file:
# read the list of names
list_names = [line.rstrip() for line in file]
with open("./Input/Letters/starting_letter.docx", "r") as f:
# read letter
file_source = f.read()
for name in list_names:
with open(f"./Output/ReadyToSend/LetterTo{name}.docx", "w") as f:
# replace [name] with name of the list in the file
replace_string = file_source.replace('[name]', name)
# write to a new file
f.write(replace_string)
brief = Letter()
Like so:
def find_and_replace(file, word, replacement):
with open(file, 'r+') as f:
text = f.read()
f.write(text.replace(word, replacement))
def findReplace(find, replace):
import os
src = os.path.join(os.getcwd(), os.pardir)
for path, dirs, files in os.walk(os.path.abspath(src)):
for name in files:
if name.endswith('.py'):
filepath = os.path.join(path, name)
with open(filepath) as f:
s = f.read()
s = s.replace(find, replace)
with open(filepath, "w") as f:
f.write(s)
I'm trying to write Python code to detect the first non-whitespace character in each line in a file and check if it is a "}". For example, if the contents of my file are...
a
fox {
}
{ jumped
} up
... I'd want to detect the "}" in the third line despite it having two whitespaces, and in the fifth line.
I've tried doing something like this but I'm stuck:
full_file = open ("filename", "r")
each_line = full_file.readlines()
for item in each_line
if item[0].find('}') != -1:
# do something, such as print (item)
full_file.close()
Help much appreciated!
You may try calling strip() on each line, then checking just the first character:
full_file = open ("filename", "r")
each_line = full_file.readlines()
for item in each_line
if item.strip() and item.strip()[0] == '}':
print(item)
full_file.close()
You could use this function to get the first non whitespace character.
This only reads one line at a time, so it could save you some trouble if you're dealing with a very large file.
def get_first_non_whitespace_char(file_name):
with open(file_name, 'r') as fileobj:
for line in fileobj:
for character in line:
if character != " ":
return character
return None
Trying out with a file like this:
sample.txt
}hello
Using the function
file_name = "sample.txt"
first_nsp_char = get_first_non_whitespace_char(file_name)
if first_nsp_char != None:
print("First Non space character in file:", first_nsp_char)
print("Character is '}' :", first_nsp_char == '}')
else:
print("No non-whitespace characters found in file:", file_name)
Output:
First Non space character in file: }
Character is '}' : True
my version on slight different input (sample.txt has two empty lines at the end, '\n'):
a
fox {
}
{ jumped
} up
is:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Jun 10 11:28:29 2021
#author: Pietro
https://stackoverflow.com/questions/67733277/how-can-i-detect-the-first-non-whitespace-character-in-each-line-of-a-file-opene/67733429#67733429
"""
def get_first_non_whitespace_char(file_name):
listz ={}
index = 0
with open(file_name, 'r') as fileobj:
for line in fileobj:
index += 1
#print('line: ',line)
for character in line:
if character != " " and character != '\n':
listz[index] = character
# index += 1
break
else:
pass
return listz
file_name = "sample.txt"
first_nsp_char = get_first_non_whitespace_char(file_name)
for i in first_nsp_char:
print('line # ',i,' first character is : ',first_nsp_char[i])
output:
line # 1 first character is : a
line # 2 first character is : f
line # 3 first character is : }
line # 4 first character is : {
line # 7 first character is : }
I'm trying to modify a specific line in a js file using python.
Here's the js file :
...
hide: [""]
...
Here's my python code :
with open('./config.js','r') as f:
lines = f.readlines()
with open('./config.js','w') as f:
for line in lines:
line = line.replace('hide', 'something')
f.write(line)
So it works but this is not what I want to do.
I want to write 'something' between the brackets and not replace 'hide'.
So I don't know how to do it: Do I have to replace the whole line or can I just add a word between the brackets?
Thanks
If you want to replace text at this exact line you could just do:
with open('./config.js','r') as f:
lines = f.readlines()
with open('./config.js','w') as f:
new_value = 'Something New'
for line in lines:
if line.startswith('hide'):
line = 'hide: ["{}"]'.format(new_value)
f.write(line)
or alternatively in the conditional
if line.startswith('hide'):
line = line.replace('""', '"Something new"')
Here's way to replace any value in brackets for hide that starts with any spacing.
lines = '''\
first line
hide: [""]
hide: ["something"]
last line\
'''
new_value = 'new value'
for line in lines.splitlines():
if line.strip().startswith('hide'):
line = line[:line.index('[')+2] + new_value + line[line.index(']')-1:]
print(line)
Output:
first line
hide: ["new value"]
hide: ["new value"]
last line
You can use fileinput and replace it inplace:
import fileinput
import sys
def replaceAll(file,searchExp,replaceExp):
for line in fileinput.input(file, inplace=1):
if searchExp in line:
line = line.replace(searchExp,replaceExp)
sys.stdout.write(line)
replaceAll("config.js",'hide: [""]','hide: ["something"]')
Reference
If hide: [""] is not ambiguous, you could simply load the whole file, replace and write it back:
newline = 'Something new'
with open('./config.js','r') as f:
txt = f.read()
txt = txt.replace('hide: [""]', 'hide: ["' + newline + '"]')
with open('./config.js','w') as f:
f.write(txt)
As long as you don't have "hide" anywhere else in the file, then you could just do
with open('/config.js','r') as f:
lines = f.readlines()
with open('./config.js','w') as f:
for line in lines:
line = line.replace('hide [""]', 'hide ["something"]')
f.write(line)
You can do this using re.sub()
import re
with open('./config.js','r') as f:
lines = f.readlines()
with open('./config.js','w') as f:
for line in lines:
line = re.sub(r'(\[")("\])', r'\1' + 'something' + r'\2', line)
f.write(line)
It works by searching for a regular expression, but forms a group out of what you want on the left ((\[")) and the right (("\])). You then concatenate these either side of the text you want to insert (in this example 'something').
The bounding ( ) makes a group which can be accessed in the replace with r'\1', then second group is r'\2'.
I'm using Python Fabric, trying to comment all lines in a file that begin with "#", unless that "#" is followed by 2 specific IP addresses. So if the file contains (without the bullets)
#hi
#IP1
some stuff here
#IP2
then the resulting file should be (also without the bullets)
##hi
#IP1
some stuff here
#IP2
This is what I have so far:
def verify():
output = sudo("/sbin/service syslog status")
#if syslog is running
if 'is running...' in output:
#then set output to the value of the conf file
output = sudo("cat /etc/syslog.conf")
#If pattern is matched
if "#" in output and not "#IP1" and not "#IP2":
#read all the lines in the conf file
sys.stdout = open('/etc/syslog.conf', 'r+').readlines()
#and for every line, comment if it matches pattern
for line in sys.stdout:
if "#" in line and not "#1P1" and not "#IP2":
line = "#" + line
else:
print GOOD
else:
print RSYSLOG
I get that when I say
if "#" in output and not "#IP1" and not "#IP2"
Python is thinking that I am saying "do some thing if there is an # in the file, but ONLY if you also do not have #IP1 and #IP2." What I'm trying to say is "do some thing to any line starting with an #, except the lines #IP1 and #IP2." Also I know there are other errors in my code, but I'm working on just this now.
thanks.
Regex solution:
You can use the following regex to match:
^(?=#(?!(IP1|IP2)))
And replace with #
See DEMO
Code:
re.sub(r'^(?=#(?!(IP1|IP2)))', r'#', myStr)
I would do like,
if not "#IP1" in output or not "#IP2" in output:
if output.startswith("#"):
// stuff here
Check if criteria exists in the glob, and if so, open file, read line-by-line, and use re.sub() to add a # inline to the lines that require it.
import re
ip1 = '1.1.1.1'
ip2 = '2.2.2.2'
fh = open('in.txt', 'r')
f = fh.read()
fh.close()
if re.search(r'(#(?!({0}|{1})))'.format(ip1, ip2), f):
fh = open('in.txt', 'r')
for line in fh:
line = re.sub(r'^(#(?!({0}|{1})))'.format(ip1, ip2), r'#\1', line)
print(line)
Input file:
#1.1.1.1
#this
#2.2.2.2
#3.3.3.3
#
no #
blah
Output:
#1.1.1.1
##this
#2.2.2.2
##3.3.3.3
##
no #
blah
Input.txt File
12626232 : Bookmarks
1321121:
126262
Here 126262: can be anything text or digit, so basically will search for last word is : (colon) and delete the entire line
Output.txt File
12626232 : Bookmarks
My Code:
def function_example():
fn = 'input.txt'
f = open(fn)
output = []
for line in f:
if not ":" in line:
output.append(line)
f.close()
f = open(fn, 'w')
f.writelines(output)
f.close()
Problem: When I match with : it remove the entire line, but I just want to check if it is exist in the end of line and if it is end of the line then only remove the entire line.
Any suggestion will be appreciated. Thanks.
I saw as following but not sure how to use it in here
a = "abc here we go:"
print a[:-1]
I believe with this you should be able to achieve what you want.
with open(fname) as f:
lines = f.readlines()
for line in lines:
if not line.strip().endswith(':'):
print line
Here fname is the variable pointing to the file location.
You were almost there with your function. You were checking if : appears anywhere in the line, when you need to check if the line ends with it:
def function_example():
fn = 'input.txt'
f = open(fn)
output = []
for line in f:
if not line.strip().endswith(":"): # This is what you were missing
output.append(line)
f.close()
f = open(fn, 'w')
f.writelines(output)
f.close()
You could have also done if not line.strip()[:-1] == ':':, but endswith() is better suited for your use case.
Here is a compact way to do what you are doing above:
def function_example(infile, outfile, limiter=':'):
''' Filters all lines in :infile: that end in :limiter:
and writes the remaining lines to :outfile: '''
with open(infile) as in, open(outfile,'w') as out:
for line in in:
if not line.strip().endswith(limiter):
out.write(line)
The with statement creates a context and automatically closes files when the block ends.
To search if the last letter is : Do following
if line.strip().endswith(':'):
...Do Something...
You can use a regular expression
import re
#Something end with ':'
regex = re.compile('.(:+)')
new_lines = []
file_name = "path_to_file"
with open(file_name) as _file:
lines = _file.readlines()
new_lines = [line for line in lines if regex.search(line.strip())]
with open(file_name, "w") as _file:
_file.writelines(new_lines)