Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

io.UnsupportedOperation: not readable

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()
like image 453
John Joseph Avatar asked Jan 16 '23 01:01

John Joseph


1 Answers

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.)

like image 182
abarnert Avatar answered Jan 21 '23 17:01

abarnert