5

The following code throws up a mysterious error that I cannot find the solution to. It works fine when I tested it in a bigger module, so cannot see why this doesn't work:

Code

import csv

with open('studentinfo.txt','a') as fo: #open the file in append mode (add to file, we don't wish to overwrite!)
        studentfileWriter=csv.writer(fo) #fo = file out (this can be called anything you like)
        id=input("Enter Student Id:")
        firstname=input("Enter firstname:")
        surname=input("Enter Surname:")
        test1=input("Enter test1 score:")
        test2=input("Enter test2 score:")
        test3=input("Enter test3 score:")
        studentfileWriter.writerow([id,firstname,surname,"Test1:"+test1,"Test2:"+test2,"Test3:"+test3])
        print("Record has been written to file")


with open("studentinfo.txt", "r") as f:
    reader = csv.reader(f)
    sorted_list = list(reader)  # turn the reader iterator into a list
    sorted_list.sort(key=lambda x: x[2])  # use the third column as a sorting key
    print("\n".join(str(row) for row in sorted_list))  # prettier print

Error Message

sorted_list.sort(key=lambda x: x[2])  # use the third column as a sorting key
IndexError: list index out of range

It is worth noting that the code works fine when there are no additions to the file contents. On adding a student to the file, the SORT does not work.

Original File contents

001,Joe,Bloggs,Test1:99,Test2:100,Test3:1
002,Ash,Smith,Test1:20,Test2:20,Test3:100
003,Jonathan,Peter,Test1:99,Test2:33,Test3:44

File Contents on adding a test student:

001,Joe,Bloggs,Test1:99,Test2:100,Test3:1
002,Ash,Smith,Test1:20,Test2:20,Test3:100
003,Jonathan,Peter,Test1:99,Test2:33,Test3:44
006,Mulch,Cart,Test1:99,Test2:22,Test3:11

The resultant error occurs at this stage (when the new student has been added). The sort function otherwise works fine.

Update and clarification:

For teaching purposes, I need it to work both on repl.it AND IDLE>

If someone could post a repl.it as an answer (With my code above, working), which also works when implemented in IDLE with a txt file, I will accept as an answer.

jwodder
  • 54,758
  • 12
  • 108
  • 124
Compoot
  • 2,227
  • 6
  • 31
  • 63

1 Answers1

6

The reason of your issue here is that you're not appending to csv properly.

On Windows, the csv module has a bug/limitation when running Windows. It adds extra blank lines every line (actually it adds an extra carriage return char) if you don't open the file properly. So to fix it:

Python 3:

with open('studentinfo.txt','a',newline='') as fo:

Python 2:

with open('studentinfo.txt','ab') as fo:

So csv module is adding an extra \r at the end of your file. And when reading it back again, it issues an empty row.

It works fine in repl.it because they're using a python engine which runs on a Linux sandbox) , but the documentation still advises to open the files like I've shown.

(the documentation of the csv module is clear about this, even if it advises to do the same for read mode, and I never had any issue with a simple open("file.csv"))

Also see an old question of mine: portable way to write csv file in python 2 or python 3

If there's a double carriage return char at the end of the file, you don't see it (use Notepad++ with "show all symbols" to see a double CRCR character) but csv.reader returns an empty row which fails when sort uses your key function to compare it.

Now, if you want to be robust to that (because other people could edit your database, for instance, using excel csv mode or other terrible stuff):

I would filter out & sort & convert to list at the same time using filter(None,...) which removes "falsy" (i.e empty) rows:

sorted_list = sorted(filter(None,reader),key=lambda x: x[2])

Of course if a row has only 1 or 2 items, that will also fail. In that case, drop filter because we'd have to write a lambda and it's not worth, in favor of a generator comprehension:

sorted_list = sorted((x for x in reader if len(x)>2),key=lambda x: x[2])
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • I think this is a pretty elegant solution, or at the very least, Pythonic –  Jul 20 '17 at 19:01
  • @MissComputing if you apply the fix when appending to your csv file, then you don't even need the filtering. – Jean-François Fabre Jul 20 '17 at 19:12
  • I'm afraid I've just tried that: with open('studentinfo.txt','a',newline="") as fo Error persists: sorted_list.sort(key=lambda x: x[2]) # use the third column as a sorting key IndexError: list index out of range – Compoot Jul 20 '17 at 19:17
  • you have to remove the empty lines from your already existing file first, ... or apply my filter fix... choose. – Jean-François Fabre Jul 20 '17 at 19:17
  • There are no empty lines! I've explained in the question, that it works FINE in repl.it. When I try it with IDLE and a physical txt file, it doesn't. https://repl.it/JbPx/0 – Compoot Jul 20 '17 at 19:18
  • you are right. But there are lines with double carriage return chars. It's the same for csv which sees that as empty lines. See my edit. It works fine in repl.it because they're using _up to date_ python install which doesn't have the bug. @MissComputing – Jean-François Fabre Jul 20 '17 at 19:20
  • Which part are you suggesting I use? the newline bit doesn't work...can you (in a comment) post a repl.it that will also work in IDLE. For teaching purposes, I need it to work in both, and so that is the solution I'm after. Thanks for your help! – Compoot Jul 20 '17 at 20:41
  • python 2 I suppose: use `"ab"` mode. Really, everything you need is in my answer. And delete prior data files since they're corrupt (or use filter trick to read them back) – Jean-François Fabre Jul 20 '17 at 20:43
  • Python 3 as evidenced by the print(). As mentioned, I understand what you are saying but cannot replicate it working. If you or anyone else can post a repl.it that also works in IDLE, cutting and pasting from my solution, then I can accept your answer.Updating question to indicate this – Compoot Jul 20 '17 at 20:44
  • that kind of print also works in python 2. I don't know what more to say. The answer has been upvoted 4 times, and tested, and it works (I have tested your code and found the issue: missing `newline=""` on my 3.4.4 release. – Jean-François Fabre Jul 20 '17 at 20:46
  • I've tried it, and it doesn't work .... with open('studentinfo.txt','a',newline='') as fo: still comes up with the same error –  Jul 20 '17 at 20:49
  • ok, I have forked your code adding the newline stuff AND the filtering: https://repl.it/JbPx/3 – Jean-François Fabre Jul 20 '17 at 20:50
  • 1
    I think you'll see the problem here. With your forked and saved code: https://repl.it/JbPx/5 (note that on adding a new entry, it doesn't do so correctly). Note, that it is probably to do with where the cursor is left on saving the txt file? i.e, in this case, it should be on the next line. Try that and it should work, and you can accept this answer! –  Jul 20 '17 at 20:55