【问题标题】:How to loop until all lines read from a file?如何循环直到从文件中读取所有行?
【发布时间】:2019-04-17 00:09:46
【问题描述】:

我正在尝试建立一个分数系统,我需要将文本文件中的所有分数输入到“记录数组”中。

我对@9​​87654322@ 还很陌生,希望有一个简单的解决方案。

在我的程序中,记录数组在技术上将归类为namedtuples 的列表。

目前我有:

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)

我尝试print(Playerlist[0]),但没有任何显示。

我也尝试print(Playerlist[0]) 没有任何循环并得到了预期的结果,尽管我不会将文本文件中的所有数据都存储到我的程序中。

文本文件中的内容示例 (scores.txt):

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

编辑: 我试过了:

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

然而所有的内容都搞混了:

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

【问题讨论】:

  • 你能提供一个示例文本文件吗?
  • 查看answer
  • 是的,所有数据都用新行分隔
  • 所以我用 while len(line) != 0: 替换了 while 语句,现在它给了我一个错误,说 line is not defined?

标签: python list loops file-handling namedtuple


【解决方案1】:

这段代码和这种方法都存在一些问题。有许多文件格式对这类事情有用;一个非常流行的内置 Python 支持的是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 不是我经常看到的。将它与 JSON 一起使用可能有点不稳定,虽然有 workarounds,但使用带有简单自定义序列化程序的 Player 类可能是一个更好的主意,子类由 namedtuple 生成的类定义一个方法返回 JSON 或 JSON 格式的 dict(相当复杂),或者编写一个单独的函数,将您的 namedtuple 对象显式转换为 JSON。

关于阅读您现有的格式:

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

chunks 函数来自this answer。这取决于您知道要为每个播放器解包的值的数量,对于这种格式,这些值无法更改。

在此处读取lines 时,将使用list comprehension 从每个值的末尾去除换行符。

最后,Player 元组用player_values 实例化,这是一个由chunks 生成并使用* 扩展的列表。这意味着,不是将列表player_values 传递给函数Player.__init__(...),而是将各个值作为*args 发送。如此有效,而不是Player([name, result, difficulty, score]),方法调用变为Player(name, result, difficulty, score)

虽然这在技术上检索值,但请注意此处分配的 score 是字符串,而不是数值。例如,如果您希望将其转换为 int,则需要写出完整的实例化:

# ...

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)

【讨论】:

  • 哇,有很多东西可以立即吸收,而且您的反应也非常快!我唯一遇到的问题是我不知道如何使用它来读取文件,我程序中的老玩家,他们都是从保存分数的文件中读取的,而不是自己输入的。
  • 如果您以前没有使用过 JSON,那么需要熟悉一下;它是一种常见且非常有用的存储格式,并且经常用于 API 响应(特别是在 XML 可能是替代格式或遗留格式的情况下)。它的显着优势是定义明确,让 Python 原生代码同时处理编码和解码,因此您不必担心为自定义存储格式手动实现这两件事。
  • 检查遗留文件(以不同的名称存储它们)并编写转换函数以从该文件中提取(如果没有 JSON 版本)应该不难(之后写入 JSON 文件,以便转换只发生一次)。还是你被这种格式卡住了?
  • 我会看看我可以用 JSON 做什么,我对编程非常陌生,尤其是 OOP。
  • 明白。我已经用您现有格式的一个选项更新了答案,但如果可能的话,我建议切换到 JSON(甚至CSV,尽管 JSON 是一个更好的选择)。
【解决方案2】:

由于您使用的是while str(f.readline) != '':,它会读取第一行。因此,第一行(以及记录之间的所有行)必须有空行。此外,while 中的 readline 缺少括号()。除此之外,代码在 python 3 中运行。您可以使用with 打开文件:

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

它会自动关闭文件并为您处理详细信息。但是,使用 json 或其他格式是更好的选择。

【讨论】:

  • 这看起来很有希望,如果它有效,那正是我想要的!
  • @MarcusYip 请注意,使用您的原始代码,您需要文本文件的第一行和记录之间的所有行都为空(当然不是空的,但它不会被读取)。然而,上面的代码 ^ 不应该有空行。使用这样的格式真的很痛苦(一个换行符可能会弄乱整个程序)所以如果可能的话,请查看像 json 这样的格式。
  • 我明白你的意思(我认为..?)虽然这只是我必须在 4 天内提交的项目的一部分。我迫切希望立即得到答案,而你给了我!
  • 我的程序实际上也将分数写入文本文件,我确保输入的值逐行跟随。我还需要担心空行吗?因为文本文件中输入的数据是由程序完成的,而不是人。
  • 如果您绝对确定文件遵循格式没有问题。
猜你喜欢
  • 2021-12-13
  • 2015-10-30
  • 1970-01-01
  • 2019-07-28
  • 1970-01-01
  • 2018-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多