简体   繁体   中英

How to loop until all lines read from a file?

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

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)

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM