Knight
Knight

Reputation: 573

Python file read and write

I have file with scores:

Foo 12
Bar 44

I try to sort it, erase it and than write sorted scores into it. But I get error:

ValueError: I/O operation on closed file

Here is my function:

def Sort():
scores = []
with open("results.txt") as x:
    for linia in x:
        name,score=linia.split(' ')
        score=int(score)
        scores.append((name,score))
    scores.sort(key=lambda sc: sc[1])
x.truncate()
for name, score in scores:
    x.write(name+' '+str(score))

Upvotes: 0

Views: 122

Answers (1)

xrisk
xrisk

Reputation: 3898

The file remains open only inside the with block. After that, Python will automatically close it. You need to open it a second time, using a second with block.

def Sort():
    scores = []
    with open("results.txt") as x:
        # x is open only inside this block!
        for linia in x:
            name, score=linia.split(' ')
            score = int(score)
            scores.append((name, score))
        scores.sort(key=lambda sc: sc[1])

    with open("results.txt", "w") as x:
        # open it a second time, this time with `w`
        for name, score in scores:
            x.write(name + ' ' + str(score))

Sort()

This can also be done using just a single file open. In this case, you open the file in a dual read/write mode (r+) and use truncate to erase the previous file contents.

def Sort():
    scores = []
    with open("results.txt", "r+") as x:
        for linia in x:
            name, score = linia.split(' ')
            score = int(score)
            scores.append((name, score))
        scores.sort(key=lambda sc: sc[1])

        # Go to beginning of file
        x.seek(0)

        # http://devdocs.io/python~3.5/library/io#io.IOBase.truncate
        # If no position is specified to truncate, 
        # then it resizes to current position

        x.truncate()
        # note that x.truncate(0) does **not** work,
        # without an accompanying call to `seek`.
        # a number of control characters are inserted
        # for reasons unknown to me.

        for name, score in scores:
            x.write(name + ' ' + str(score) + '\n')

However, personally, I feel that the first approach is better, and you’re less likely to shoot yourself in the foot.

Upvotes: 2

Related Questions