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