HOWTO · Python

Python 在檔案中查詢字串

本教程演示瞭如何在 Python 中搜尋文字檔案中的字串。

本頁內容

在文字檔中尋找字面子字串時,若只需要布林答案,可搭配 in 使用 read();若要檢視符合的行,可使用 readlines();若需要字元位置,則使用 find()。檔案很大或資料以位元組為單位時,可用 mmap 進行檔案映射搜尋。以下範例使用相同的小型文字,方便比較結果與取捨。每個範例都可執行;下方輸出與提供的執行結果相符。

使用 read() 和 in 搜尋檔案內容

read() 方法會將檔案剩餘的內容以單一字串傳回。使用 Python 的 in 運算子,檢查這個字串是否包含要找的字面文字。運算結果為 True 或 False;如果只想知道子字串是否出現,這種方式就很實用。

這個方法直接簡單,但會先將整個檔案讀入記憶體,再進行搜尋。適合小型或中型文字檔,以及還需要完整檔案內容的情況。如果檔案太大,無法舒適地放入記憶體,可以改用逐行處理或檔案映射的方式。

執行以下範例:以下程式碼與輸出區塊保留原有內容,其中的程式識別字與輸出文字仍為英文。

from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as directory:
    path = Path(directory) / "notes.txt"
    path.write_text("status=ready\nowner=Sam\n", encoding="utf-8")
    with path.open(encoding="utf-8") as file:
        found = "ready" in file.read()
    print(found)
True

使用 readlines() 逐行搜尋

readlines() 方法會傳回一個清單,內含檔案各行;若行尾有換行字元,也會一併保留。你可以用 in 檢查每一行,並在找到第一個符合項目時停止。當你需要使用符合的那一行,例如決定要顯示或處理哪筆記錄時,這種方式很方便。

即使迴圈能提早停止,readlines() 也已將所有行載入清單,因此記憶體用量會隨檔案大小增加。若要逐步處理大型檔案,可以直接對開啟的檔案使用 for line in file 迭代;這樣不用建立包含全部行的清單,同時仍可逐行比對。

執行以下範例:以下程式碼與輸出區塊保留原有內容,其中的程式識別字與輸出文字仍為英文。

from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as directory:
    path = Path(directory) / "notes.txt"
    path.write_text("status=ready\nowner=Sam\n", encoding="utf-8")
    with path.open(encoding="utf-8") as file:
        for line in file.readlines():
            if "owner" in line:
                print(line.rstrip())
                break
owner=Sam

使用 find() 取得符合項目的位置

若需要子字串起始的字元索引,而不只是是或否的答案,請對 read() 傳回的內容使用字串 find() 方法。索引從零開始:符合項目位於字串開頭時會傳回 0,位於後方時則會傳回它在字串中的位置。找不到時,find() 會傳回 -1。請明確檢查這個值,不要把所有結果都當成布林值,因為在 Python 中 0 會被視為假值。

傳回的位置是解碼後 Python 字串中的字元數,不是檔案中的位元組數。若檔案含有非 ASCII 文字,請使用適當的編碼開啟文字檔。若需要位元組位移,或內容是二進位資料,可使用 mmap 搭配 bytes 樣式進行位元組搜尋。

執行以下範例:以下程式碼與輸出區塊保留原有內容,其中的程式識別字與輸出文字仍為英文。

from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as directory:
    path = Path(directory) / "notes.txt"
    path.write_text("status=ready\nowner=Sam\n", encoding="utf-8")
    with path.open(encoding="utf-8") as file:
        contents = file.read()
    position = contents.find("ready")
    print(position)
    print(contents.find("missing"))
7
-1

使用 mmap 搜尋大型檔案

mmap 模組會將檔案映射成類似 bytes 的物件,可直接搜尋,而不必先建立包含整個檔案的 Python 字串。處理大型檔案時可能適合使用,但不保證搜尋速度一定更快;效能取決於檔案、儲存裝置、作業系統及存取方式。映射也會使用作業系統資源,因此完成後請將它關閉。

請使用 bytes 樣式搜尋映射,例如 b"ready"。與文字範例不同,這裡搜尋的是編碼後的位元組,並傳回位元組位移。樣式必須是 bytes;一般字串如 "ready" 是不同的型別。此範例使用相同 ASCII 文字的 UTF-8 位元組,並以二進位模式開啟檔案。

執行以下範例:以下程式碼與輸出區塊保留原有內容,其中的程式識別字與輸出文字仍為英文。

import mmap
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as directory:
    path = Path(directory) / "notes.txt"
    path.write_bytes(b"status=ready\nowner=Sam\n")
    with path.open("rb") as file:
        with mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ) as mapped:
            print(mapped.find(b"ready"))
7

選擇適合的方法

若只需要布林答案,而且完整文字可輕鬆放入記憶體,請搭配 in 使用 read()。若需要取得行清單並檢查或保留各行,可使用 readlines();請記得清單會保存整個檔案。對於可逐行獨立處理的大型文字檔,直接迭代檔案物件即可,不必儲存所有行。

若需要第一個字面符合項目的字元位置,請使用 find()。用 -1 判斷是否不存在,並將 0 視為有效位置。若適合以檔案映射方式搜尋位元組,而且需要位元組位移,可使用 mmap;請將目標編碼為 bytes,也不要假設映射一定比較快。這些方法搜尋的是字面文字。若需求是比對樣式、忽略大小寫,或找出每一個符合項目,請選擇適合該需求的方法。