How to loop until all lines read from a file?

时间:2019-04-17 00:09:47

标签: python list loops file-handling namedtuple

I'm trying to set up a score system where I need to input all the scores from a text file into an 'array of records'.

I'm fairly new to Python and hope for a simple solution.

In my program, the array of records would technically class as a list of namedtuples.

Currently I have:

Player = namedtuple("Player", ["name", "result", "difficulty", "score"])

Playerlist = []
while str(f.readline) != '':
    player = Player(
        f.readline(),
        f.readline(),
        f.readline(),
        f.readline())
    Playerlist.append(player)

I tried to print(Playerlist[0]), but nothing shows up.

I have also tried to print(Playerlist[0]) without any loop and got the expected result, though I won't have stored all the data from the text file into my program.

An example of what is in the text file (scores.txt):

George
lost
H
18
Holly
lost
H
28
Marcus
won
H
30

EDIT: I tried:

with open("scores.txt", "r") as f:
    for line in f:
        player = Player(
            f.readline(),
            f.readline(),
            f.readline(),
            f.readline())
        Playerlist.append(player)

However all of the contents came out mixed up:

Player(name='H\n', result='28\n', difficulty='Marcus\n', score='won\n')

2 个答案:

答案 0 :(得分:0)

There are several problems with both this code and this approach. There are many file formats that are useful for this kind of thing; one very popular one that has built-in Python support is JSON.

import json
from pprint import pprint


old_players = [
    {
        'name': 'Bob',
        'result': 'success?',
        'difficulty': 'hard',
        'score': 55,
    },
    {
        'name': 'Tatsuki',
        'result': 'embarrassment',
        'difficulty': 'easy',
        'score': -2,
    },
]

with open('player-file.json', 'w') as outfile:
    outfile.write(json.dumps(old_players))

with open('player-file.json', 'r') as infile:
    new_players = json.loads(infile.read())

pprint(new_players)
# [{'difficulty': 'hard', 'name': 'Bob', 'result': 'success?', 'score': 55},
#  {'difficulty': 'easy', 'name': 'Tatsuki', 'result': 'embarrassment', 'score': -2}]

namedtuple isn't something I see used often. Using it with JSON can be a bit wonky, and while there are workarounds, it might be a better idea to either use a Player class with a simple custom serializer, subclass a class generated by namedtuple that defines a method to return either JSON or a JSON-formattable dict (pretty convoluted), or write a separate function that explicitly translates your namedtuple objects into JSON.

Regarding reading your existing format:

from pprint import pprint
from collections import namedtuple


Player = namedtuple("Player", ["name", "result", "difficulty", "score"])


def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in range(0, len(l), n):
        yield l[i:i + n]


with open('/tmp/players.old', 'r') as infile:
    lines = [l.strip() for l in infile.readlines()]

for player_values in chunks(lines, 4):
    pprint(Player(*player_values))

# Player(name='George', result='lost', difficulty='H', score='18')
# Player(name='Holly', result='lost', difficulty='H', score='28')
# Player(name='Marcus', result='won', difficulty='H', score='30')

The chunks function comes from this answer. It depends on you knowing the number of values you're unpacking per player, which can't change for this format.

When lines is read here, a list comprehension is used to strip the newline from the end of each value.

Finally, the Player tuple is instantiated with player_values, a list generated by chunks and expanded using *. This means, instead of passing the list player_values to the function Player.__init__(...), the individual values will be sent as *args. So effectively, instead of Player([name, result, difficulty, score]), the method call becomes Player(name, result, difficulty, score).

While this technically retrieves the values, note that the score that's assigned here is a string, not a numeric value. If you want that to be cast to an int, for example, you'd need to write out the full instantiation:

# ...

for player_values in chunks(lines, 4):
    pprint(Player(
        player_values[0],
        player_values[1],
        player_values[2],
        int(player_values[3]),
    ))

# Player(name='George', result='lost', difficulty='H', score=18)
# Player(name='Holly', result='lost', difficulty='H', score=28)
# Player(name='Marcus', result='won', difficulty='H', score=30)

答案 1 :(得分:-1)

Since you are using while str(f.readline) != '': it reads first line. Therefore, first line(and by extension all lines between records) must have blank line. Also readline in your while is missing paranthesis(). Other than that the code is working in python 3. You can use with to open file:

with open('test.txt', 'r') as f:
    while True:
        player = Player(f.readline(), f.readline(), f.readline(), f.readline())
        if "" in player:
            break;
        Playerlist.append(player)

for i in range(len(Playerlist)):
    print (Playerlist[i])

It automatically closes files and handles details for you. However, using json or other formats in a better option.