13

I'm trying my hand at this rosalind problem and am running into an issue. I believe everything in my code is correct but it obviously isn't as it's not running as intended. i want to delete the contents of the file and then write some text to that file. The program writes the text that I want it to, but it doesn't first delete the initial contents.

def ini5(file):
raw = open(file, "r+")
raw2 = (raw.read()).split("\n")
clean = raw2[1::2]
raw.truncate()
for line in clean:
    raw.write(line)
    print(line)

I've seen:

How to delete the contents of a file before writing into it in a python script?

But my problem still persists. What am I doing wrong?

Scott
  • 4,974
  • 6
  • 35
  • 62
Tare Gaskin
  • 1,209
  • 2
  • 10
  • 13
  • As an aside, this isn't good practice -- better to create a separate temporary file for output, and rename it over your original when done; that way you don't destroy your input file if your program fails to complete, and other processes on the system running at the same time can always access one version or the other, be it the original or updated form. – Charles Duffy Jan 28 '17 at 21:48

3 Answers3

34

truncate() truncates at the current position. Per its documentation, emphasis added:

Resize the stream to the given size in bytes (or the current position if size is not specified).

After a read(), the current position is the end of the file. If you want to truncate and rewrite with that same file handle, you need to perform a seek(0) to move back to the beginning.

Thus:

raw = open(file, "r+")
contents = raw.read().split("\n")
raw.seek(0)                        # <- This is the missing piece
raw.truncate()
raw.write('New contents\n')

(You could also have passed raw.truncate(0), but this would have left the pointer -- and thus the location for future writes -- at a position other than the start of the file, making your file sparse when you started writing to it at that position).

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • this is really helpful, there are very few guides mentioning that truncate leaves the cursor not at the beginning of the file – Val Feb 14 '20 at 10:38
  • I think `seek`, `write`, `truncate` might be more efficient. – Yay295 Aug 23 '21 at 01:54
  • @Yay295, ...depends on your filesystem; in not every filesystem does an in-place write actually modify the data blocks directly. Journaled data is a thing. The other question is what the failure cases look like. Data that's truncated is (IMHO) more obvious for someone doing forensics or recovery than data that's half-overwritten, so given the choice, I prefer it as a failure mode. – Charles Duffy Aug 23 '21 at 15:45
10

If you want to completley overwrite the old data in the file, you should use another mode to open the file.

It should be:

raw = open(file, "w") # or "wb"

To resolve your problem, First read the file's contents:

with open(file, "r") as f: # or "rb"
    file_data = f.read()
# And then:
raw = open(file, "w")

And then open it using the write mode.This way, you will not append your text to the file, you'll just write only your data to it.

Read about mode files here.

Community
  • 1
  • 1
Ofer Arial
  • 1,129
  • 1
  • 10
  • 25
  • The OP wants to perform a read before the overwrite -- otherwise, yes, they would have just used `w`, but it's not suitable to that purpose. If you're proposing that they close the read handle and then create a second write handle, that's workable, but you might be more explicit about it. – Charles Duffy Jan 28 '17 at 21:49
  • 1
    upvote for simplicity. I'd avoid opening a handle for read/write if I can avoid it. Although I suspect reusing the same handle is faster. – Jean-François Fabre Jan 28 '17 at 22:07
  • Ok, so I got it to run the way that I want it to. Thanks everyone! To clarify though -- To extract contents (like string information) I have to open the file in read mode, however to edit the actual file I have to open it in write mode. To do both I have to open the file in read mode and then write mode (not using "r+")? – Tare Gaskin Jan 29 '17 at 01:49
  • Not exactly, you can use "r+". In your particular case, it was fine to the both. Just seperated them for simplicity. – Ofer Arial Jan 29 '17 at 11:11
  • 1
    @TareGaskin, `r+` certainly works -- my answer describes how to truncate and go back to the beginning of the file before your write. As such, you have two options, with two different behaviors -- which one is more appropriate depends on your use case. Opening a separate file handle means that you can potentially get a different inode to write to than you read from, if the directory entry was changed out from under you, so these are very much two different solutions each with their own semantics. – Charles Duffy Jan 30 '17 at 21:30
-1

Best way would be:

with open('shoot.txt', 'r+') as file:
    text = file.read()  # get text for later use

    file.truncate(0)  # Note the "0" param
    file.seek(0)

    file.write("new text")
Ben Slade
  • 478
  • 5
  • 11
Scott
  • 4,974
  • 6
  • 35
  • 62
  • I missed the zero in truncate(0). Apologies. Yes it works. FYI, I can' remove my downvote unless your answer is edited – Ben Slade May 16 '23 at 18:22