io.UnsupportedOperation: not readable [duplicate] - python

This question already has answers here:
Python error message io.UnsupportedOperation: not readable
(5 answers)
Closed 6 months ago.
I am working on a problem that says to make a program that gets a user input for a file and then within the file removes a string that the user specifies. I'm not sure how to go from what I have(below) to what the question asks for. As always any and all help is greatly appreciated.
def main():
outfile = open(input("Enter a file name: "), "a")
string = input("Enter the string to be removed: ")
for string in outfile.readlines():
string = string.replace(string, "")
outfile.close()
print("Done")
main()
I took one of the suggestions and tried to get it to work but as I said in my comment below the code below does not return an error it creates an empty file. What am I missing to get the new file to be the old file with the string removed?
def main():
inpath = input("Enter an input file: ")
line = input("Enter what you want to remove: ")
outpath = input("Enter an output file: ")
with open(inpath, "r") as infile, open(outpath, "w") as outfile:
for line in infile:
outfile.write(line.replace(line, "") + "\n")
print("Done.")
main()

A few side notes before getting into the details: When you call string.replace(string, ""), you're telling the string to replace its entire self with the empty string—you might as well just do string = "". Presumably the first string is the search string to replace, so give it a different name, and then use it as, e.g., string.replace(searchString, ""). Also, you don't want to name a variable string, because it's the name of a standard library module. You're calling your input file "outfile", which is apt to be confusing. You probably want to use a with statement instead of an explicit close. Finally, you can iterate the lines in a file with just for line in f:; you don't need for line in f.readlines() (and, if you ever need to deal with Python 2.x, you'll be much happier avoiding readlines(), because it will read the entire file into memory, and then make a huge list of lines in memory).
The first problem, as JBernardo pointed out, is that you've opened the file in "a" mode, which means "write-only, appending to the end". You can use "a+" or "r+" if you want to read and write.
However, that won't really help you. After all, you can't write to the file in the middle of reading it.
There are a few common ways around this.
First, just write to standard output, and let the user do whatever he wants with the results—e.g., redirect it to a file. (In that case, you have print your prompt, "Done" message, etc. to standard error instead, so they don't get redirected to the file.) This is what many Unix tools like sed or sort do, so it's appropriate if you're building a Unix-style tool, but may not be right for other purposes.
def stderrinput(prompt):
sys.stderr.write(prompt)
sys.stderr.flush()
return input()
def main():
with open(stderrinput("Enter a file name: "), "r") as infile:
searchString = stderrinput("Enter the string to be removed: ")
for line in infile:
print(infile.replace(searchString, ""))
sys.stderr.write("Done\n")
Second, write to another file. Open the input file in "r" mode, and the output file in "w", mode, and then you're just copying lines:
def main():
inpath = input("Enter an input file: ")
outpath = input("Enter an output file: ")
with open(inpath, "r") as infile, open("outpath", "w") as outfile:
for line in infile:
outfile.write(line.replace(searchString, "") + "\n")
Third, read and process the whole file in memory, then truncate and rewrite the whole file:
def main():
path = input("Enter an input/output file: ")
with open(path, "r+") as inoutfile:
lines = [line.replace(searchString, "") for line in inoutfile]
inoutfile.seek(0)
inoutfile.truncate()
inoutfile.writelines(lines)
Finally, write to a temporary file (as with the second option), then move that temporary file on top of the original input file. Something like this:
def main():
path = input("Enter an input/output file: ")
with open(path, "r") as infile, tempfile.NamedTemporaryFile("w", delete=False) as outfile:
for line in infile:
outfile.write(line.replace(searchString, ""))
shutil.move(outfile.name, pathname)
This last one is a little tricky, because of the differences between POSIX and Windows. However, it has some big advantages. (For example, if your program gets killed in the middle of operation, no matter how it happens, you're guaranteed to have either the original file or the new file, not some half-written mess.)

Related

.txt file function read/write position

I have this homework which I need to define a function that does the following:
Write a function which given a text file object open in read and write mode and a string, inserts the text of the string in the file at the current read/write position. In other words, the function writes the string in the file without overwriting the rest of it. When exiting the function, the new read/write position has to be exactly at the end of the newly inserted string.
The algorithm is simple; the function needs to:
read the content of the file starting at the current read/write position
write the given string at the same position step 1 started
write the content read at step 1. at the position where step 2. ended
reposition the read/write cursor at the same position step2. ended (and step 3. started).
I'm very confused by the steps 1-4 as to what I need do to, and the professor has been no help.
here is the code that I did, but I don't think the function follows the parameter given
def readWrite(file, string):
file.read()
file.seek(0)
file.write(string)
give_file = input("enter a file name: ")
give_string = input("enter a string: ")
try:
readFile = open(give_file, "a+")
file_content = readWrite(readFile, give_string)
except FileNotFoundError:
print("File does not exist")
exit(1)
The whole code should ask for a simple .txt file, which it would take it and a string, and add it to the original file.
example:
the file is Twinkle.txt
Twinkle, twinkle, little bat!
How I wonder what you're at!
Up above the world you fly,
Like a teatray in the sky.
output:
twinkle.txt
1 Twinkle, twinkle, little bat!
2 How I wonder what you're at!
3 Up above the world you fly,
4 Like a teatray in the sky.
I have found the way to do it and here is the function:
def readWrite(file, string):
if not file.readable():
print("This file is not readable")
file.close()
return
if not file.writable():
print("This file is not writable")
file.close()
return
initialPosition = file.tell()
print(initialPosition)
readContent = file.read()
file.seek(initialPosition)
file.write(string)
secondPosition = file.tell()
print(secondPosition)
file.write(readContent)
file.seek(secondPosition)
basically, this takes in the file and appends to it the file name and line numbering to each row. (the code for line numbering is in the main body below).
main body:
give_file = input("enter a file name: ")
try:
openFile = open(give_file, "r+")
position = openFile.tell()
except FileNotFoundError:
print(give_file, " does not exist or could not open")
exit(1)
counter = 0
for line in openFile:
counter += 1
openFile.seek(position)
readWrite(openFile, give_file + "\n" + "\n")
spacing = 1
while spacing <= counter:
readWrite(openFile, str(spacing) + " ")
openFile.readline()
spacing += 1
openFile.close()
there might be shorter more elegant solutions out there, but this one works for me
I don't think I know the full answer, but I know it involves dealing with file streams. Look up the python docs for the TextIO object for that. file.write(content) in save_file deletes the existing content of the file in my experience. I believe you want to open the file in append mode at the bottom, for which the string parameter in open is 'a'
this link might help: https://www.guru99.com/reading-and-writing-files-in-python.html

converting first file to another file

I'm new at programming and i need help with converting first file to another file. The task is:
Write a program that asks the user for two filenames. The first one should mark any existing text file. The second filename may be new, so the file with this name may not exist.
The program's task is to take the first file of the file, convert it to capital letters, and write another file.
So far I have:
file_old = input("Which file do you want to take ? ")
file_new = input("In which file do you want to put the content? ")
file1 = open(file_old, encoding="UTF-8")
file2 = open(file_new, "w")
for rida in file1:
file2.write(rida.upper())
file1.close()
file2.close()
You have to write the full path to your file for your code to work.
I tested it and it works perfectly.
The input path should be like
C:\Users\yourUserName\PycharmProjects\test_folder\test_small_letters.txt
This should be instead of old.txt that you enter
for example:
"C:\Program Files\Python36\python.exe" C:/Users/userName/PycharmProjects/pythonSnakegame/test_file_capitalize.py
which file you want to take ? C:\Users\userName\PycharmProjects\test_folder\test_small_letters.txt
In which file you want to put the content? C:\Users\userName\PycharmProjects\test_folder\test_big_letters.txt
C:\Users\userName\PycharmProjects\test_folder\test_small_letters.txt
C:\Users\userName\PycharmProjects\test_folder\test_big_letters.txt
Process finished with exit code 0
The new file was created and capitalized.
You can do that in a more pythonic way, with the with statement. This creates a context manager which takes care to close() the file when you are done with it.
file_old = input("Which file do you want to take ? ")
file_new = input("In which file do you want to put the content? ")
with open(file_old, 'r') as f1:
with open(file_new, 'w') as f2:
for line in f1:
f2.write(line)

Using variable as part of name of new file in python

I'm fairly new to python and I'm having an issue with my python script (split_fasta.py). Here is an example of my issue:
list = ["1.fasta", "2.fasta", "3.fasta"]
for file in list:
contents = open(file, "r")
for line in contents:
if line[0] == ">":
new_file = open(file + "_chromosome.fasta", "w")
new_file.write(line)
I've left the bottom part of the program out because it's not needed. My issue is that when I run this program in the same direcoty as my fasta123 files, it works great:
python split_fasta.py *.fasta
But if I'm in a different directory and I want the program to output the new files (eg. 1.fasta_chromsome.fasta) to my current directory...it doesn't:
python /home/bin/split_fasta.py /home/data/*.fasta
This still creates the new files in the same directory as the fasta files. The issue here I'm sure is with this line:
new_file = open(file + "_chromosome.fasta", "w")
Because if I change it to this:
new_file = open("seq" + "_chromosome.fasta", "w")
It creates an output file in my current directory.
I hope this makes sense to some of you and that I can get some suggestions.
You are giving the full path of the old file, plus a new name. So basically, if file == /home/data/something.fasta, the output file will be file + "_chromosome.fasta" which is /home/data/something.fasta_chromosome.fasta
If you use os.path.basename on file, you will get the name of the file (i.e. in my example, something.fasta)
From #Adam Smith
You can use os.path.splitext to get rid of the .fasta
basename, _ = os.path.splitext(os.path.basename(file))
Getting back to the code example, I saw many things not recommended in Python. I'll go in details.
Avoid shadowing builtin names, such as list, str, int... It is not explicit and can lead to potential issues later.
When opening a file for reading or writing, you should use the with syntax. This is highly recommended since it takes care to close the file.
with open(filename, "r") as f:
data = f.read()
with open(new_filename, "w") as f:
f.write(data)
If you have an empty line in your file, line[0] == ... will result in a IndexError exception. Use line.startswith(...) instead.
Final code :
files = ["1.fasta", "2.fasta", "3.fasta"]
for file in files:
with open(file, "r") as input:
for line in input:
if line.startswith(">"):
new_name = os.path.splitext(os.path.basename(file)) + "_chromosome.fasta"
with open(new_name, "w") as output:
output.write(line)
Often, people come at me and say "that's hugly". Not really :). The levels of indentation makes clear what is which context.

Attempting to write to a file in Python makes the file go blank

I'm relatively new to Python, and I'm trying to create a piece of code in Python that looks through each line of a text file for a certain value, inputted by the user, then replaces a line with a new one created by the code. However, when I try the code, the file goes blank. I've got an f.close() but the code still won't write.
Here is my code:
import fileinput
f = open("task3.txt", "w+")
name = input("What is your name?")
lines = f.readlines()
print(lines)
for i in lines:
splitlines = i.split(":")
print(splitlines)
splitnums = splitlines[1].split(", ")
print(splitnums)
for i in splitnums:
i = int(i)
edit = input('Would you like to edit this entry?')
if edit == "Yes":
valueNew = input("Which new value would you like to add?")
del(splitnums[2])
splitnums.append(splitnums[1] + "\n")
splitnums[1] = splitnums[0] + ", "
splitnums[0] = valueNew + ", "
print(splitnums)
numstring = ''.join(splitnums)
splitlines[1] = ": "
splitlines.append(numstring)
newval = ''.join(splitlines)
print(newval)
f.write(newval)
else:
print("Okay.")
f.close()
You will have to read the entire file, then change the content before writing again, as w clears the file contents. If appending to (adding to the end of) the file suffices, you can change the mode argument to a to open the file in append mode.
You should also consider using with instead of manually closing the file:
with open('file.txt', 'w') as f:
...
, as this closes the file and cleans up regardless of errors thrown etc.
Mode w+ means "open the file for writing, truncating it first, and also let me read it"
So, it's doing exactly what you asked.
If you want to open the file for reading, but also let you write it, that's spelled r+. Or, in Python 3.0+, you can also just use plain +.
The docs for open even include examples of different modes that explain exactly what you're asking:
The default mode is 'r' (open for reading text, synonym of 'rt'). For binary read-write access, the mode 'w+b' opens and truncates the file to 0 bytes. 'r+b' opens the file without truncation.
Or, for the 2.x version:
Modes 'r+', 'w+' and 'a+' open the file for updating (reading and writing); note that 'w+' truncates the file.
That's the difference between r+ and w+ (or r+b and w+b): use the r one when you don't want truncation.

How to open a file using the open with statement

I'm looking at how to do file input and output in Python. I've written the following code to read a list of names (one per line) from a file into another file while checking a name against the names in the file and appending text to the occurrences in the file. The code works. Could it be done better?
I'd wanted to use the with open(... statement for both input and output files but can't see how they could be in the same block meaning I'd need to store the names in a temporary location.
def filter(txt, oldfile, newfile):
'''\
Read a list of names from a file line by line into an output file.
If a line begins with a particular name, insert a string of text
after the name before appending the line to the output file.
'''
outfile = open(newfile, 'w')
with open(oldfile, 'r', encoding='utf-8') as infile:
for line in infile:
if line.startswith(txt):
line = line[0:len(txt)] + ' - Truly a great person!\n'
outfile.write(line)
outfile.close()
return # Do I gain anything by including this?
# input the name you want to check against
text = input('Please enter the name of a great person: ')
letsgo = filter(text,'Spanish', 'Spanish2')
Python allows putting multiple open() statements in a single with. You comma-separate them. Your code would then be:
def filter(txt, oldfile, newfile):
'''\
Read a list of names from a file line by line into an output file.
If a line begins with a particular name, insert a string of text
after the name before appending the line to the output file.
'''
with open(newfile, 'w') as outfile, open(oldfile, 'r', encoding='utf-8') as infile:
for line in infile:
if line.startswith(txt):
line = line[0:len(txt)] + ' - Truly a great person!\n'
outfile.write(line)
# input the name you want to check against
text = input('Please enter the name of a great person: ')
letsgo = filter(text,'Spanish', 'Spanish2')
And no, you don't gain anything by putting an explicit return at the end of your function. You can use return to exit early, but you had it at the end, and the function will exit without it. (Of course with functions that return a value, you use the return to specify the value to return.)
Using multiple open() items with with was not supported in Python 2.5 when the with statement was introduced, or in Python 2.6, but it is supported in Python 2.7 and Python 3.1 or newer.
http://docs.python.org/reference/compound_stmts.html#the-with-statement
http://docs.python.org/release/3.1/reference/compound_stmts.html#the-with-statement
If you are writing code that must run in Python 2.5, 2.6 or 3.0, nest the with statements as the other answers suggested or use contextlib.nested.
Use nested blocks like this,
with open(newfile, 'w') as outfile:
with open(oldfile, 'r', encoding='utf-8') as infile:
# your logic goes right here
You can nest your with blocks. Like this:
with open(newfile, 'w') as outfile:
with open(oldfile, 'r', encoding='utf-8') as infile:
for line in infile:
if line.startswith(txt):
line = line[0:len(txt)] + ' - Truly a great person!\n'
outfile.write(line)
This is better than your version because you guarantee that outfile will be closed even if your code encounters exceptions. Obviously you could do that with try/finally, but with is the right way to do this.
Or, as I have just learnt, you can have multiple context managers in a with statement as described by #steveha. That seems to me to be a better option than nesting.
And for your final minor question, the return serves no real purpose. I would remove it.
Sometimes, you might want to open a variable amount of files and treat each one the same, you can do this with contextlib
from contextlib import ExitStack
filenames = [file1.txt, file2.txt, file3.txt]
with open('outfile.txt', 'a') as outfile:
with ExitStack() as stack:
file_pointers = [stack.enter_context(open(file, 'r')) for file in filenames]
for fp in file_pointers:
outfile.write(fp.read())

Categories

Resources