Python: Open a file, search then append, if not exist - python

I am trying to append a string to a file, if the string doesn't exit in the file. However, opening a file with a+ option doesn't allow me to do at once, because opening the file with a+ will put the pointer to the end of the file, meaning that my search will always fail. Is there any good way to do this other than opening the file to read first, close and open again to append?
In code, apparently, below doesn't work.
file = open("fileName", "a+")
I need to do following to achieve it.
file = open("fileName", "r")
... check if a string exist in the file
file.close()
... if the string doesn't exist in the file
file = open("fileName", "a")
file.write("a string")
file.close()

To leave the input file unchanged if needle is on any line or to append the needle at the end of the file if it is missing:
with open("filename", "r+") as file:
for line in file:
if needle in line:
break
else: # not found, we are at the eof
file.write(needle) # append missing data
I've tested it and it works on both Python 2 (stdio-based I/O) and Python 3 (POSIX read/write-based I/O).
The code uses obscure else after a loop Python syntax. See Why does python use 'else' after for and while loops?

You can set the current position of the file object using file.seek(). To jump to the beginning of a file, use
f.seek(0, os.SEEK_SET)
To jump to a file's end, use
f.seek(0, os.SEEK_END)
In your case, to check if a file contains something, and then maybe append append to the file, I'd do something like this:
import os
with open("file.txt", "r+") as f:
line_found = any("foo" in line for line in f)
if not line_found:
f.seek(0, os.SEEK_END)
f.write("yay, a new line!\n")

There is a minor bug in the previous answers: often, the last line in a text file is missing an ending newline. If you do not take that that into account and blindly append some text, your text will be appended to the last line.
For safety:
needle = "Add this line if missing"
with open("filename", "r+") as file:
ends_with_newline = True
for line in file:
ends_with_newline = line.endswith("\n")
if line.rstrip("\n\r") == needle:
break
else: # not found, we are at the eof
if not ends_with_newline:
file.write("\n")
file.write(needle + "\n") # append missing data

Related

Can you delete all the lines of a .txt file after a specific line?

I am trying to delete all the lines in a text file after a line that contains a specific string. What I am trying to do is find the number of the line in said file and rewrite the whole text up until that line.
The code that I'm trying is the following:
import itertools as it
with open('sampletext.txt', "r") as rf:
for num, line in enumerate(rf, 1): #Finds the number of the line in which a specific string is contained
if 'string' in line:
print(num)
with open('sampletext_copy.txt', "w") as wf:
for line in it.islice(rf, 0, num):
wf.write(line)
Also would appreciate any tips on how to do this. Thank you!
You could do it like this:
with open('sampletext.txt', "r") as rf, open('sampletext_copy.txt', "w") as wf:
for line in rf:
if 'string' in line:
break
wf.write(line)
Basically, you open both files at the same time, then read the input file line-by-line. If string is in the line, then you're done - otherwise, write it to the output file.
In case if you want to apply changes to original file, it's possible to do using .truncate() method of file object:
with open(r"sampletext.txt", "r+") as f:
while line := f.readline():
if line.rstrip() == "string": # line.startswith("string")
f.truncate(f.tell()) # removes all content after current position
break
Here we iterating over file until reach this specific line and resize stream to size of bytes we've already read (to get it we use .tell()).
Just to complement Donut's answer, if you want to modify the file in place, there's a much more efficient solution:
with open('sampletext.txt', "r+") as f:
for line in iter(f.readline, ''): # Can't use for line in f: because it disables
# tell for txt
# Or for walrus lovers:
# while line := f.readline():
if 'string' in line:
f.seek(0, 1) # Needed to ensure underlying handle matches logical read
# position; f.seek(f.tell()) is logically equivalent
f.truncate()
break
If issue #26158 is ever fixed (so calling truncate on a file actually truncates at the logical position, not the arbitrary position of the underlying raw handle that's likely advanced a great deal due to buffering), this simpler code would work:
with open('sampletext.txt', "r+") as f:
for line in f:
if 'string' in line:
f.truncate()
break

Check if value exists in file

I am trying to read the following file line by line and check if a value exists in the file. What I am trying currently is not working. What am I doing wrong?
If the value exists I do nothing. If it does not then I write it to the file.
file.txt:
123
345
234
556
654
654
Code:
file = open("file.txt", "a+")
lines = file.readlines()
value = '345'
if value in lines:
print('val ready exists in file')
else:
# write to file
file.write(value)
There are two problems here:
.readlines() returns lines with \n not trimmed, so your check will not work properly.
a+ mode opens a file with position set to the end of the file. So your readlines() currently returns an empty list!
Here is a direct fixed version of your code, also adding context manager to auto-close the file
value = '345'
with open("file.txt", "a+") as file:
file.seek(0) # set position to start of file
lines = file.read().splitlines() # now we won't have those newlines
if value in lines:
print('val ready exists in file')
else:
# write to file
file.write(value + "\n") # in append mode writes will always go to the end, so no need to seek() here
However, I agree with #RoadRunner that better is to just use r+ mode; then you don't need the seek(0). But the cleanest is just to split out your read and write phases completely, so you don't run into file position problems.
I would consider several changes.
1: Use with to automatically close the file.
2: Use strip() to remove leading or trailing stuff, like \n
3: Use a break for the loop.
4: Add \n in the write part.
value = "345"
with open("file.txt", "a+") as file:
file.seek(0)
for line in file.readlines():
if line.strip("\n") == value:
print('val ready exists in file')
break
else:
# write to file
file.write(f"\n{value}")
when working with io the recomended approach is to use the context manager. Context managers allow you to allocate and release resources precisely when you want to. The most widely used example of context managers is the with statement. if you have a large file better not to use file.readlines() or the read() method. The readlines() method returns a list containing each line in the file as a list item. better to iterate on the file stream line by line (generator). always use try except with io operations! :
values=['123','233'...]
try:
with open("file.txt", "r+") as fp:
for line in fp:
for val in values:
if val not in line.strip():
fp.write(val)
else:
print('val ready exists in file')
except (OSError,...): #catch what ever you think this code above can raise, and re raise in except block if you want.
#do exception handling
Since you want to open the file for reading and writing, I suggest using the r+ mode from open(). This will open the file at the beginning of the file, since we want to first read all lines. Using a+ will open the file for reading and writing at the end of the file, which will cause lines to give you an empty list from readlines().
You also need to strip newlines from lines before checking if the value exists. This is because 345 is not equal to 345/n. We can use a list comprehension to strip the newlines from lines using str.rstrip(), which strips whitespace from the right. Additionally, If you have to do repetitive lookups for multiple values, it might be worth converting lines to a set for constant time lookups, instead of doing a linear search with a list.
Its also worth using With Statement Context Managers when reading files, since the closing of the file is handled for you.
value = '345'
with open("file.txt", mode="r+") as file:
lines = [line.rstrip() for line in file.readlines()]
if value in lines:
print('value ready exists in file')
else:
file.write(f"{value}\n")
The other choice is to use f.seek(0) with a+ to set the position at the beginning of the file, as shown in #Cihan Ceyhan's answer. However I think this overcomplicates things, and its just easier to use the r+ mode.
This should work :)
filename = 'file.txt'
value = '345'
with open(filename) as f:
if value in f.read(): # read if value is in file
print('Value is in the file')
This will check if the value is in the file and if it's not there. It will add value to the file.
filename = 'file_1.txt'
value = '999'
with open(filename, 'r+') as f:
if value in f.read():
print(f"Value {value} is in the file")
else:
print("The value not in the file.\nTherefore, saving the value in the file.")
f.write(f"{value}\n")
When you are opening file in "a+" mode the file cursor from where the readlines() method will start reading will be at the end so readlines would read nothing. You need to do f.seek(0) in order to move the cursor to the beginning.
file = open("file.txt", "a+")
file.seek(0)
lines = [line.strip() for line in file.readlines()]
print(lines)
value = '345'
if value in lines:
print('val ready exists in file')
else:
print("else")
# write to file
file.write(value)
file.close()
Your python script works for me properly. I guess the last line in the else statement should be file.write(value) instead of file.write(val).
The readlines() method returns a list containing \n with each element so when the condition is checked it is not compared with value with \n so the statement is always false so i have a code that solve your problem.
f_name = "file.txt"
text = "102"
def check_in_file(file_name, value):
with open(file_name, 'r') as read_obj:
for line in read_obj:
if value in line:
return True
return False
if check_in_file(f_name, text):
print('Yes, string found in file')
else:
print('String not found in file')
file = open(f_name, 'a')
file.write("\n"+text)
file.close()

Read/Write Loop Text File with Python

I want to open an existing txt file and search for line of text appearing many times and in different places. Each time search found, insert 2 new rows below it with specified text.
I tried this code but got 'AttributeError' on 'Path.write' line ('str' object has no attribute 'write').
Path = '...\\Test.txt'
searchString = '* Start *'
with open(Path, 'r+') as f:
content = f.readlines()
nextLine = False
for line in content:
if searchString in line:
nextLine = not nextLine
else:
if nextLine:
Path.write('Name\nDirection')
nextLine = not nextLine
else:
pass
I must also allocate to 'Direction' line a number, starting at 0 and increment by 15 until all file is read. So after first instance is found, two lines are inserted into existing txt file like this;
...some text in the existing text file....
* Start *
Name
Direction 0
0 then changes to 15 on next instance (ie Direction 15), then 30 (ie Direction 30) etc until end of file.
EDITED CODE: Simplified coded. Anyone vote me up I'd appreciate
Path = '...\\Test.txt'
direction_number = 0
#Open new file
Newfile = open(Path, 'w')
#read other file
with open(Path, 'r') as f:
content = f.readlines()
#if find special text, write other lines to new file
for line in content:
Newfile.write(line)
if searchString in line:
Newfile.write('Name\nDirection %d' % direction_number)
direction_number += 15
Newfile.close()
Instead of trying to reopen and insert lines into the original file, you should just write a new file. So for each line in the old file, write it to the new file, and write the two additional lines if it contains the text in question.
direction_number = 0
with open("newfile.txt", 'w') as g:
# Loop through every line of text we've already read from
# the first file.
for line in content:
# Write the line to the new file
g.write(line)
# Also, check if the line contains the <searchString> string.
# If it does, write the "Name" and "Direction [whatever]" line.
if searchString in line:
g.write('Name\nDirection %d\n' % direction_number)
direction_number += 15
EDIT: To explain more about this second with open statement: Remember earlier that you used with open(Path, 'r+') as f: to READ your file.
The Path part is where the name of the file is stored, the r+ part means that you're opening it for reading, and the "f" is just a variable that essentially says, "Anything we do on f, we do to the file". Likewise, to start working with a new file, I wrote with open("newfile.txt", 'w') as g:. The "newfile.txt" is the name of the file. The "w" means you're opening up this file for writing to it instead of reading from it (if the file doesn't exist, it will create it; if it exists already, it will completely write over it). Then the "g" is just a variable I picked to refer to this file. So g.write(line) just writes the next line of text from the first file to the next line of text in the second file. I suppose you could use "f" again here, since at this point you've already read all of the lines from the old file. But using a different variable cuts down on any ambiguity of what file you're dealing with, especially if you ever wanted to change this so that you simultaneously have one file still open for reading as you have a second file open for writing.

Python: Deleting lines from a file with certain criteria

I'm trying to delete lines from a file with certain criteria but when I run the script it just deletes the whole file. When I change the script to just 'read' the lines it returns the lines with the search criteria but when I open the file in 'write' mode it and change it from printing each line to remove each line it empties the whole thing.
#!/usr/bin/env python
f = raw_input('Enter filename > ')
with open(f, 'w+') as fobj:
criteria = raw_input('Enter criteria > ')
for eachLine in fobj:
if criteria in eachLine:
fobj.remove(eachLine)
break
fobj.close()
I hope you wanted to remove the line having particular criteria. You can simply create another file with and write the content in that file as following:
output = []
with open('test.txt', 'r') as f:
lines = f.readlines()
criteria = 'test'
output =[line for line in lines if criteria not in line]
fin = open('newfile.txt', 'wb')
fin.writelines(output)
From the docs:
w+ Open for reading and writing. The file is created if it does not
exist, otherwise it is truncated. The stream is positioned at
the beginning of the file.
a+ Open for reading and writing. The file is created if it does not
exist. The stream is positioned at the end of the file. Subse-
quent writes to the file will always end up at the then current
end of file, irrespective of any intervening fseek(3) or similar.
so you are truncating the file on the line containing the with open. You probably want to create a new file with a different name and rename it at the end of your program.

How to create file if it does not exists, if exists then don't truncate - Python27

I have this code
with codecs.open("file.json", mode='a+', encoding='utf-8') as f:
I want:
1) Create file if it does not exists, and start writing from the start of file.
2) If exists, first read it and truncate it and then write something.
I found this somewhere
``r'' Open text file for reading. The stream is positioned at the
beginning of the file.
``r+'' Open for reading and writing. The stream is positioned at the
beginning of the file.
``w'' Truncate file to zero length or create text file for writing.
The stream is positioned at the beginning of the file.
``w+'' Open for reading and writing. The file is created if it does not
exist, otherwise it is truncated. The stream is positioned at
the beginning of the file.
``a'' Open for writing. The file is created if it does not exist. The
stream is positioned at the end of the file. Subsequent writes
to the file will always end up at the then current end of file,
irrespective of any intervening fseek(3) or similar.
``a+'' Open for reading and writing. The file is created if it does not
exist. The stream is positioned at the end of the file. Subse-
quent writes to the file will always end up at the then current
end of file, irrespective of any intervening fseek(3) or similar.
a+ mode suits me best but what it does that it only lets me write at end of file,
With a+ mode I have this f.seek(0) immediately after opening file, but it has no affect, it does not seek to the start of file.
Let's say you have a file a with content:
first line
second line
third line
If you need to write from the start of the file, just do:
with open('a','r+') as f:
f.write("forth line")
Output:
forth line
second line
third line
If you need to remove the current content and write from the start, do:
with open('a','r+') as f:
f.write("forth line")
f.truncate()
Output:
forth line
If you need to append after the existing file, do:
with open('a','a') as f:
f.write("forth line")
Output:
first line
second line
third line
forth line
And, as you suspected, you will not be able to seek to 0 in a+ mode. You might see details from here
Edit:
Yes, you can dump json with this configuration and still indent. Demo:
dic = {'a':1,"b":2}
import json
with open('a','r+') as f:
json.dump(dic,f, indent=2)
Output:
{
"a": 1,
"b": 2
}third line
Use os.path.isfile():
import os
if os.path.isfile(filename):
# do stuff
else:
# do other stuff
As to your second question about writing to the begging of a file, then don't use a+. See here for how to prepend to a file. I'll post the relevant bits here:
# credit goes to #eyquem. Not my code
def line_prepender(filename, line):
with open(filename, 'r+') as f:
content = f.read()
f.seek(0, 0)
f.write(line.rstrip('\r\n') + '\n' + content)
You can check if the file exists, and then branch accordingly like so:
import os.path
file_exists = os.path.isfile(filename)
if file_exists:
# do something
else:
# do something else
Hope this helps!
You can open the file using os.open to be able to seek and have more control, but you won't be able to use codecs.open or a context manager then, so it's a bit more manual labor:
import os
f = os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), 'r+')
try:
content = f.read()
f.seek(0)
f.truncate()
f.write("Your new data")
finally:
f.close()

Categories