Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

How can I read a UTF-8 text file that may start with a byte-order mark?

Quick answer

For a local file that may begin with a UTF-8 byte-order mark (BOM), open it with encoding="utf-8-sig" if you want Python to skip that optional marker at the start. Ordinary encoding="utf-8" leaves an initial BOM as the first character, U+FEFF. To check for it after ordinary decoding, use text.startswith("\ufeff"). [S1]

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

I’m reading a local text file in Python, and I suspect its first character is an unexpected marker. What are my options?

Geez

A UTF-8 byte-order mark (BOM) can appear at the start of a file. Choose between two opening options based on what you want your text to contain:

  1. Use encoding="utf-8" to decode normally. If a BOM is at the start, it remains in the decoded text as U+FEFF.
  2. Use encoding="utf-8-sig" to skip an optional UTF-8 BOM at the start.

For a file you want to read without that leading marker, utf-8-sig is often the convenient choice. The two options are shown here with a placeholder path:

with open("[sample-file.txt]", "r", encoding="utf-8-sig") as file:
    text = file.read()

Python’s utf-8-sig handling applies to the signature at the beginning of the data; it is not a general-purpose way to remove U+FEFF everywhere in a document. [S1]

Follow-up question

How can I verify whether ordinary UTF-8 decoding left the marker at the beginning?

Geez

Read the file with ordinary UTF-8, then check whether the resulting string starts with U+FEFF. In Python, write that character as \ufeff:

with open("[sample-file.txt]", "r", encoding="utf-8") as file:
    text = file.read()

has_starting_bom = text.startswith("\ufeff")
print(has_starting_bom)

If the check prints True, the first character in text is the marker. If it prints False, that particular check did not find U+FEFF as the first character. It does not tell you that the file has no marker elsewhere or establish the file’s encoding. [S1]

Follow-up question

Can I compare both approaches on the same sample file so I can see what changes?

Geez

Yes. Read the file once with each codec and inspect the start of each resulting string. The following is a small comparison; [sample-file.txt] is a placeholder that you should replace with your local file’s path.

path = "[sample-file.txt]"

with open(path, "r", encoding="utf-8") as file:
    ordinary_text = file.read()

with open(path, "r", encoding="utf-8-sig") as file:
    signature_text = file.read()

print(ordinary_text.startswith("\ufeff"))
print(signature_text.startswith("\ufeff"))

For example, if the file has a BOM at its start, the expected comparison is True for the ordinary UTF-8 read and False for the utf-8-sig read. Treat those as expected results for that stated assumption, not as a result measured from your file. The codec skips the optional signature at the start; this comparison does not check for markers at other positions. [S1]

Follow-up question

What should I conclude if neither read starts with U+FEFF, and which option should I keep in my code?

Geez

If the ordinary UTF-8 text does not start with U+FEFF, you have not found a BOM at the beginning using this check. That alone does not establish the file’s encoding or confirm that every byte is valid for the encoding you intended. [S1]

Keep encoding="utf-8" if you want ordinary decoding and will handle a possible leading U+FEFF yourself. Keep encoding="utf-8-sig" if an optional starting UTF-8 BOM should be skipped when present. Either way, make the choice explicit in the open() call so the behavior is clear to someone reading your code.

A practical checklist

  • Replace [sample-file.txt] with the path to your local file.

  • Choose utf-8 to retain a starting BOM as U+FEFF, or utf-8-sig to skip an optional BOM at the start.

  • If checking with ordinary UTF-8, test text.startswith("\ufeff").

  • Interpret the check narrowly: it tests the first character, not the file’s encoding or every character in the text.

A few more useful answers

Does `utf-8-sig` remove U+FEFF wherever it appears in the file?

No. It skips the optional UTF-8 signature when it occurs at the start of the data; it is not a general cleanup operation for other positions. [S1]

Does a `False` result from `text.startswith("\ufeff")` prove the file is UTF-8?

No. It only means the decoded text does not begin with U+FEFF. This check does not establish the file’s encoding or detect arbitrary encoding errors. [S1]

Which option should I use if I want to check for the marker rather than skip it?

Read with encoding="utf-8", then test text.startswith("\ufeff"). Ordinary UTF-8 decoding leaves an initial BOM as U+FEFF. [S1]

Sources

References used for this guide.

  1. [S1] Python codecs: UTF-8 and UTF-8-with-signature
    docs.python.org · Checked

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work