跳至主要内容

如何在 Python 中逐行读取文件

学习在 Python 中处理文本文件、日志与大型数据集的实用模式。
· 6分钟 阅读

使用 AI 探索

Python 提供了多种逐行读取文件的方式,乍一看似乎都可以互换。但在实践中,您的选择会影响内存占用、性能,甚至代码的可读性。有些方法在处理大型文件时能平稳扩展,而另一些则会在不明显的情况下引入问题。

在本教程中,您将学习在 Python 中逐行读取文件的推荐方式,以及为什么它通常应成为您的默认选择。我们还会看看几个替代方案,解释它们各自适用的场景,并指出一些常见误区。读完之后,无论是编写小脚本还是实现更复杂的任务,您都能自信而高效地读取文件。

在 Python 中逐行读取文件的推荐方式

如果只从本文记住一个模式,就选这个。在大多数情况下,直接迭代文件对象是逐行读取文件最简单、安全且高效的方法,也是您在真实 Python 代码中最常见到的做法。

在基本层面上,Python 的文件对象本身就是一个迭代器。这意味着您可以直接对它进行循环,一次获取一行:

with open("example.txt", "r", encoding="utf-8") as file:
    for line in file:
        print(line)

这段代码的行为直观明了。Python 打开文件,并在循环运行时逐行产出。无需手动索引、无需额外函数调用,也没有隐藏复杂度。

这种方法最大的优势之一是内存高效。Python 不会把整份文件载入内存,而是读取一行、处理一行,然后继续下一行。这让该模式即便面对非常大的文件(如日志或原始数据导出)也很安全。

这也是最“Pythonic”的解决方案。直接迭代文件对象清晰、易读,任何熟悉 Python 的人都能立刻理解。随着脚本规模增长,或在他人需要阅读您的代码时,这一点尤为重要。

基于这些好处,除非您有非常特定的理由使用其他方式,否则应默认采用该方法。它能够自动处理大文件,随文件大小增长而良好扩展,并避免不必要的内存占用——且无需增加额外复杂度。

在 Python 中读取行时处理换行与空白

如果您打印过文件中的行,发现多出空行,或疑惑字符串看起来不太对,您很可能遇到了Python 的换行行为。这是初学者最常见的痛点之一,值得尽早理解。

当 Python 逐行读取文件时,每一行通常都以换行符(\n)结尾。这个换行符是行本身的一部分,而不是 Python 之后添加的。

例如,一个看起来像这样的文件:

apple
banana
cherry

实际上读取到的是:

apple\n、banana\n 和 cherry\n。

这种行为会保留文件的原始结构,在格式很重要时尤其关键。但在处理文本时——例如比较值、解析数据或清理输入——您通常希望去掉这些多余的空白。

为处理而清理行

最常见的做法是在使用每一行之前去除空白:

with open("data.txt", encoding="utf-8") as file:
    for line in file:
        clean_line = line.strip()
        print(clean_line)

strip() 方法会移除首尾空白,包括空格、制表符和换行符。以下场景通常都需要这样做:

  • 将行与期望值进行比较
  • 将字符串转换为数字
  • 基于文件内容构建结构化数据

常见的空白处理模式

根据您的用例,也可以选择更有针对性的方法:

  • line.strip() 同时移除两端的空白

  • line.rstrip() 只移除右侧的空白

  • line.rstrip("\n") 仅移除换行符

例如,如果缩进很重要而末尾换行无关紧要:

clean_line = line.rstrip("\n")

何时不应去除空白

同样重要的是,知道何时不该去除空白。如果您正在处理:

  • 预格式化文本
  • 定宽文件
  • 空格有意义的日志
  • Markdown 或代码片段

移除空白可能会破坏数据结构。在这些情况下,请使用原始行,并有意识地处理格式。

一个不错的经验法则:处理内容时去除空白;格式重要时保留空白。

如何在 Python 中记录行号

在逐行读取文件后,通常会想知道当前所处的行号。对于日志、校验错误,以及调试混乱的输入文件,这尤其有用。

Python 通过内置的 enumerate() 函数让这一点变得很容易:

with open("data.txt", encoding="utf-8") as file:
    for line_number, line in enumerate(file, start=1):
        print(line_number, line.strip())

其工作原理如下:

  • 文件仍是一次产出一行

  • enumerate() 为每行附带一个计数器

  • start=1 符合人类对行号的习惯

这种模式依旧内存高效,对大文件同样适用。

什么时候行号很重要

跟踪行号在以下场景尤其有用:

  • 日志:定位事件发生的位置
  • 校验:精确报告问题数据出现的位置
  • 调试:追踪解析失败
  • 用户反馈:指向输入文件中的具体行

由于 enumerate() 能让代码保持简洁易读,它几乎总是优于手动维护计数器。

使用 readline() 在 Python 中一次读取一行

Python 还提供了一个更“手动”的选项:readline()。您不常需要它,但理解它能帮助您识别何时适合使用。

readline() 方法每次调用读取单行。当文件读完时,它会返回空字符串:

with open("example.txt", encoding="utf-8") as file:
    line = file.readline()
    while line:
        print(line.strip())
        line = file.readline()

何时适合使用 readline()

readline() 适用于需要有条件或严格控制读取的情况,例如:

  • 交互式程序
  • 一旦满足条件就立刻停止
  • 在复杂逻辑中穿插文件读取

例如:

with open("log.txt", encoding="utf-8") as file:
    while True:
        line = file.readline()
        if not line or "ERROR" in line:
            break
        print(line.strip())

为什么它通常不如默认循环

在大多数情况下,这样写仍然更好:

for line in file:
    process(line)

它更短、更清晰、同样高效,也更不容易被误用。将 readline() 视为特殊场景下的工具,而非标准循环的替代品。

为什么 readlines() 通常不适用于大文件

readlines() 方法会将整个文件读入内存,并返回一个由各行组成的列表:

with open("example.txt", encoding="utf-8") as file:
    lines = file.readlines()

这对小文件可行——但无法扩展。

因为会一次性载入整个文件:

  • 内存占用随文件大小增长
  • 大文件可能拖慢程序,甚至导致崩溃
  • 问题往往只在生产环境中暴露

何时可以接受使用 readlines()

在以下情况下是合理的:

  • 文件体积可确保很小
  • 确实需要一次性拿到所有行
  • 大小可预测且有上限

除此之外,逐行迭代几乎总是更好的选择。

逐行读取文件时的常见错误

大多数问题源自少数疏忽:

  • 忘记关闭文件 → 使用 with

  • 无意中读入整个文件 → 避免使用 read() 与 readlines()

  • 忽略编码 → 显式指定 UTF-8

  • 混淆文本与二进制模式 → 文本用 "r",二进制用 "rb"

了解这些之后,避免起来就很容易。

在 Python 中逐行读取文件的最佳实践

在结尾之前,这里有一份简短清单,帮助您的代码保持整洁可靠:

  • 默认将文件对象作为迭代器使用
  • 有意地去除空白,而非自动去除
  • 非必要时避免将整份文件读入内存
  • 处理文本时显式指定编码

这些习惯从小脚本到生产流水线都适用。

结语

在 Python 中逐行读取文件,有一个明确的默认做法:直接迭代文件对象。它简单、内存高效,并且足以应对从小型文本到海量日志的各种场景。

Python 的设计让这一模式显得自然。您无需特殊工具或复杂逻辑,因为语言本身已经为您处理了难点。大多数问题并非源自 Python,而是把本就有清晰解法的任务过度复杂化。

坚持基于迭代器的方法,谨慎处理空白与编码,您的文件读取代码就会保持可读、易于理解。

常见问题

逐行读取文件总是比一次性读取更慢吗?

不会。事实上,对于大文件,逐行读取往往整体更快,因为它避免了内存压力。将整个文件载入内存可能会拖慢程序,甚至导致崩溃,而逐行迭代能让内存占用保持恒定。

为什么 Python 在每一行中包含换行符 (\n)?

因为 Python 会按磁盘上的实际内容原样保留文件。换行符是数据的一部分,而不是后期添加的格式。这使得读取文件可预测且灵活——由您来决定何时以及如何清理文本,而不是让 Python 替您猜测。

读取行时我是否应始终使用 strip()?

并非总是需要。处理数值、比较字符串或清理输入时使用 strip()。当空白具有含义(如日志、格式化文本、定宽文件或代码片段)时请避免使用。

在哪些情况下 readline() 比 for line in file 更好?

很少但有时需要。readline() 适用于需要有条件或交互式停止读取的情况。对于大多数批处理任务,标准循环更清晰、更安全,且同样高效。

将 readline() 视为边缘场景的工具,而非通用替代品。

人们在逐行读取文件时最大的错误是什么?

把问题复杂化。多数错误来自不必要地读入整个文件、错误处理编码,或在 Python 已提供简洁方案的地方手动添加计数器与状态。

主题