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 換行;若使用 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()))
''