HOWTO · Python

在 Python 中从文件中删除特定行

本教程讨论如何在 Python 中从文件中删除特定行。

本页内容

Python 没有内置操作可以从文本文件中删除任意一行。你可以先读取各行,跳过要删除的行,再将其余内容写回文件。下面的示例使用临时文件,均可独立运行。

在 Python 文件中按行号删除行

Python 的 enumerate() 会为每一行提供从零开始的索引:第一行的索引是 0。请先读取文件,再以写入模式打开它,因为模式 w 会立即清空文件内容。需要删除多个索引对应的行时,可以使用集合;索引在集合中的行都会被跳过。

运行此示例:

from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as directory:
    path = Path(directory) / "notes.txt"
    path.write_text("red\ngreen\nblue\nyellow\n")
    indexes_to_remove = {1, 3}
    lines = path.read_text().splitlines(keepends=True)
    path.write_text("".join(
        line for index, line in enumerate(lines)
        if index not in indexes_to_remove
    ))
    print(path.read_text(), end="")
red
blue

此示例仅使用 LF 换行符,并以 LF 结尾写入保留的行。Path.read_text() 使用通用换行处理,因此 CRLF 输入在写入前会转换为 \n。如果索引超出文件范围,就不会匹配任何行,也不会产生影响。处理大文件时,可以逐行写入临时文件;两个文件都关闭后,再用临时文件替换原文件。

方法一

即使代码块提前结束,上下文管理器也会关闭文件。先读取再写回的做法简单直接,适用于任意指定的索引,但会将所有行保存在内存中。处理大文件时,可以在同一目录中使用临时文件,并在写入成功后替换原文件。

方法二

对于可以放入内存的小文件,r+ 模式允许通过同一个文件句柄读取和写入。此处的 newline="" 会停用换行符转换:readlines() 会保留每行的行结束符,包括 CRLF,而 writelines() 会原样写出保留的行结束符。读取完成后,先定位到文件开头,再截断旧内容,然后写入保留的行。这种做法不必两次打开原文件,但它不是原子更新:重写期间发生故障可能导致文件只写入部分内容。它本身也不一定比第一种做法更快。

from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as directory:
    path = Path(directory) / "notes.txt"
    path.write_text("red\ngreen\nblue\nyellow\n")
    indexes_to_remove = {1, 3}
    with path.open("r+", newline="") as file:
        lines = file.readlines()
        file.seek(0)
        file.truncate()
        file.writelines(
            line for index, line in enumerate(lines)
            if index not in indexes_to_remove
        )
    print(path.read_text(), end="")
red
blue

在 Python 文件中删除第一行或最后一行

列表切片可以删除指定位置,同时保留其余内容。lines[1:] 会省略第一行;lines[:-1] 会省略最后一行。写入切片结果前,先检查列表是否包含行,这样同一段代码也能处理空文件。

from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as directory:
    path = Path(directory) / "notes.txt"
    path.write_text("first\nmiddle\nlast\n")
    lines = path.read_text().splitlines(keepends=True)
    without_first = lines[1:]
    path.write_text("".join(without_first))
    lines = path.read_text().splitlines(keepends=True)
    without_last = lines[:-1] if lines else []
    path.write_text("".join(without_last))
    print(path.read_text(), end="")
middle

此示例先删除第一行,再从剩余内容中删除最后一行。如果只需执行其中一种操作,保留相应的切片并写入一次即可。对空列表进行切片仍会得到空列表。

删除与指定文本完全匹配的行

比较行内容与目标字符串时,先去掉行结束符。这是区分大小写的精确匹配:空格和其他字符也会影响比较结果。下面的循环会删除所有匹配的行。此示例仅使用 LF 换行符,并保留 LF 行结束符;使用 Path.read_text() 时,CRLF 输入会在写入前转换为 \n。

from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as directory:
    path = Path(directory) / "notes.txt"
    path.write_text("keep this\nremove this\nremove this\nkeep that\n")
    target = "remove this"
    lines = path.read_text().splitlines(keepends=True)
    retained = [
        line for line in lines
        if line.rstrip("\r\n") != target
    ]
    path.write_text("".join(retained))
    print(path.read_text(), end="")
keep this
keep that

如果要比较的文本包含前导或末尾空格,请使用 line.rstrip("\r\n"),不要使用 strip()。如果只想删除第一个匹配项,可以先找到它的索引,再只省略该位置的行。

删除包含指定单词的行

要删除包含某个单词或子字符串的行,可以用 in 进行检查,而不是比较整行。此示例会删除所有包含 blue 的行;匹配区分大小写,包含 bluebird 的行也会匹配。使用临时文件可以让原文件保持不变,直到筛选后的内容准备就绪。

import os
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as directory:
    path = Path(directory) / "notes.txt"
    temporary_path = Path(directory) / "filtered.txt"
    path.write_text("red\nblue bird\ngreen\nbluebird\n")
    with path.open("r") as source, temporary_path.open("w") as target:
        for line in source:
            if "blue" not in line:
                target.write(line)
    os.replace(temporary_path, path)
    print(path.read_text(), end="")
red
green

请在原文件所在的目录中创建临时文件,以便替换操作仍在同一文件系统中进行。调用 os.replace() 前,请先关闭两个文件句柄。如果子字符串匹配范围太广,可以改用更严格的条件,例如用正则表达式检查完整单词。

在 Python 文件中删除最短的一行

去掉行结束符后再比较各行长度。min() 可以选出最短行的索引,无须猜测最大行长。如果有多行并列最短,会选择第一行;按索引删除可以保留后续内容相同的行。此示例仅使用 LF 换行符,并以 LF 结尾写入保留的行;Path.read_text() 会在写入前将 CRLF 输入转换为 \n。如果文件没有任何行,就没有内容需要删除。

from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as directory:
    path = Path(directory) / "notes.txt"
    path.write_text("long line\nx\ncat\ny\n")
    lines = path.read_text().splitlines(keepends=True)
    if lines:
        shortest_index = min(
            range(len(lines)),
            key=lambda index: len(lines[index].rstrip("\r\n")),
        )
        del lines[shortest_index]
    path.write_text("".join(lines))
    print(path.read_text(), end="")
long line
cat
y

这里 x 和 y 并列最短,因此只会删除排在前面的 x。如果空行不应参与比较,可以在选择索引时将其排除,并在输出中保留空行。

从 Python 文件中删除所有行

以写入模式打开现有文件时,文件会立即被截断。只有在确实要丢弃全部内容时才使用这种方式。截断文件需要可写的文件句柄;以只读模式打开的文件不能被截断。

from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as directory:
    path = Path(directory) / "notes.txt"
    path.write_text("first\nsecond\n")
    with path.open("w"):
        pass
    print(repr(path.read_text()))
''