🔤 Data Cleaning · 7 min read
CSV File Encoding: UTF-8, UTF-16 and Windows-1252 Explained
A CSV file's encoding is the map between the bytes in the file and the characters you see on screen. UTF-8 is the default you should produce; Windows-1252 is the legacy encoding that causes most of the mangled text you will meet. You do not have to guess which one a file uses: read its opening bytes for a marker called a BOM, check it in an editor that displays the encoding, or read it in Python with an explicit encoding argument. Once you know the pair — what it is and what it was read as — the conversion is a single save.
What "encoding" means for a CSV file
A file is bytes. A character is a code point in the Unicode standard. An encoding is the rule that turns one into the other. UTF-8 writes most characters as a single byte and anything outside plain ASCII as two to four bytes. Windows-1252 writes one byte per character from a much smaller set, which is why a character it cannot represent is lost outright rather than stored differently.
The CSV format itself says very little about this. RFC 4180 registers the text/csv media type with an optional charset parameter, and notes that common usage of CSV is US-ASCII but that other character sets defined by IANA for the text tree may be used with it. In other words, a CSV is simply a text file, and the encoding is whatever the program that wrote it decided.
The encodings a CSV actually arrives in
| Encoding | Bytes per character | Opening marker (BOM) | Where you meet it |
|---|---|---|---|
| UTF-8 | 1 to 4 | EF BB BF (optional) | Modern databases, APIs, data platforms, most web exports |
| UTF-8 with BOM | 1 to 4 | EF BB BF | Excel's UTF-8 CSV export, files meant to be double-clicked |
| UTF-16, little-endian | 2 or 4 | FF FE | Windows "Unicode Text" exports, some ERP and payroll systems |
| UTF-16, big-endian | 2 or 4 | FE FF | Rarer; some Java and database exports |
| Windows-1252 (cp1252) | 1 | None | Older Excel "CSV" saves, Windows desktop apps |
| US-ASCII | 1, 7-bit | None | Machine pipelines; breaks the moment a name has an accent |
The marker column comes from the Unicode FAQ: a BOM is the character U+FEFF at the start of a data stream, and the bytes that appear on disk are whatever that character converts into in the chosen encoding form — EF BB BF for UTF-8, FF FE for UTF-16 little-endian, FE FF for UTF-16 big-endian, and 00 00 FE FF or FF FE 00 00 for the two UTF-32 forms.
How to check a CSV file's encoding
1. Read the first bytes
A BOM, if present, is the fastest answer because it is a signature rather than an inference. From a terminal:
head -c 3 data.csv | xxd
# efbbbf -> UTF-8 with BOM
# fffe -> UTF-16 little-endian
# feff -> UTF-16 big-endianNo marker all means the file has to be inferred from its content. That is where the wrong guess comes from.
2. Ask a tool that reports it
file -i data.csv prints the detected charset. In an editor, Visual Studio Code and Notepad++ both show the encoding in a status bar; the same control lets you reopen the file with a different one to see whether the text becomes readable, which is the quickest way to confirm a theory without changing the file on disk.
3. Read it in Python
Two statements settle it. The first shows whether a BOM is present; the second forces an encoding and fails loudly if the guess is wrong:
with open("data.csv", "rb") as f:
print(f.read(4))
# b'\xef\xbb\xbf' -> UTF-8 with BOM
# b'\xff\xfe' -> UTF-16 little-endian
# no BOM -> try utf-8, then cp1252import csv
with open("data.csv", newline="", encoding="utf-8") as f:
print(next(csv.reader(f)))
A UnicodeDecodeError from encoding="utf-8" is useful information rather than a dead end: it means the file is not UTF-8, and cp1252 or utf-16 is usually the answer. The wider Python case, with pandas and chunked reads, is in how to open a CSV in Python.
The symptom tells you the pair
When you cannot inspect the file — a screenshot, a report from a colleague, a file already on a server — the shape of the mangled text names both encodings at once.
| What you see | File is encoded as | Something read it as |
|---|---|---|
| é as é, € as €, Chinese as a run of symbols | UTF-8 | Windows-1252 |
| é as a replacement character | Windows-1252 | UTF-8 |
| Every character separated by a gap, or by NUL bytes | UTF-16 | An 8-bit encoding |
| Wrong characters in the first field only | UTF-8 with BOM | A parser that keeps the BOM as text |
None of these are corruption. The bytes are unchanged; the reader used the wrong rule. That distinction matters, because if the file was saved in an encoding that could not represent the characters in the first place, the characters are gone and no conversion brings them back.
If the job is to get the text readable in a spreadsheet, the step-by-step repair is a separate guide: fix garbled CSV in Excel covers the import dialog, the BOM, and the re-encode options.
Which encoding to choose when you export
- UTF-8, no BOM — for anything read by software: database loads, API imports, scripts, data platforms. This is the default worth standardising on.
- UTF-8 with BOM — for a file that has to survive a double-click in Excel on a machine you do not control, because the marker saves the reader from guessing.
- UTF-16 — only when a target system demands it. It doubles the size of the file for text that is mostly ASCII and gives no benefit in return.
- Windows-1252 — expect to receive it from older Windows tools; do not create new files in it. Any character outside its set is replaced, and the replacement is permanent.
When the destination is Excel, the safest habit is to set the file origin in the import dialog rather than accepting the default, so the encoding is a decision instead of a guess. Microsoft documents the row and column ceiling for that import and export at 1,048,576 rows by 16,384 columns, which is worth knowing before you blame the encoding for a truncated file.
Converting a CSV file to UTF-8
Every method is the same two steps: read with the encoding the file is in, write with the encoding you want. The first half is the part that is easy to get wrong, and it is why you check before you convert.
import csv
with open("in.csv", newline="", encoding="cp1252") as f:
rows = list(csv.reader(f))
with open("out.csv", "w", newline="", encoding="utf-8") as f:
csv.writer(f).writerows(rows)
# add a BOM instead of plain utf-8 if the file must
# open correctly on a double-click in Excel
- In an editor: open the file, then use the encoding control to reopen it with the correct one, and save with UTF-8 (or UTF-8 with BOM).
- In Google Sheets: import the file so Google decodes it, then download as a CSV, which comes back as UTF-8.
- On the command line:
iconv -f cp1252 -t utf-8 in.csvwrites the converted stream, and adding-f utf-8 -t utf-8lets you check whether a file is already valid UTF-8 before touching it.
Converting changes the encoding, not the structure. If a converted file arrives back with the columns fused into one, the problem was never the text — that is a delimiter question, and CSV opens in one column covers it. The CSV analyzer reads UTF-8 files directly, which is a quick way to confirm a converted file before you hand it to anything else.
Frequently asked questions
What encoding should a CSV file use?
UTF-8 is the safe default. RFC 4180, which registers the text/csv media type, lists an optional charset parameter and notes that common usage is US-ASCII while any character set defined by IANA for the text tree may be used. Nothing in the format requires UTF-8, which is why two exports labelled CSV can disagree. Modern databases, APIs and data tools expect UTF-8, so producing it keeps the file portable. Add a BOM only when the file has to open correctly on a double-click in Excel.
How do I check a CSV file's encoding?
Three checks, in order of effort. Read the first bytes: EF BB BF means UTF-8 with a BOM, FF FE means UTF-16 little-endian, FE FF means UTF-16 big-endian, and no marker means the encoding has to be inferred. In a terminal, file -i data.csv prints the detected charset and head -c 3 data.csv prints the opening bytes. In an editor, Visual Studio Code and Notepad++ both show the current encoding in a status bar you can click to change it. In Python, open the file in binary mode and read four bytes.
Why does my CSV show characters like é instead of é?
The file is UTF-8 and something read it as Windows-1252. The two encodings map the same bytes to different characters, so a two-byte UTF-8 character is rendered as two Windows-1252 characters: é becomes é, € becomes €, and Chinese text becomes a run of symbols. The bytes in the file are intact. The mirror-image case is Windows-1252 data read as UTF-8, which produces the replacement character. Both are reading errors rather than corruption, and the repair steps in Excel are covered in the guide on fixing a garbled CSV.
What is a UTF-8 BOM?
A byte order mark is the character U+FEFF written at the start of a data stream, where it acts as a signature identifying the encoding. In UTF-8 that character is written as the three bytes EF BB BF, per the Unicode FAQ. It is not visible text. Some tools treat a BOM as a marker to switch to UTF-8 automatically, which is why a BOM-prefixed CSV opens correctly in Excel, and some tools write it out as an unwanted character at the start of the first field, which is the argument for leaving it off when the file is going to a script.
How do I convert a CSV file to UTF-8?
Read the file with the encoding it currently uses and write it out as UTF-8, so no bytes are guessed. In Python, open the source with the encoding it is in, parse it with the csv module, and write the result with encoding="utf-8". In an editor, use Save with Encoding and pick UTF-8, or UTF-8 with BOM if the file is headed for Excel. LibreOffice Calc and Google Sheets both re-export as UTF-8 through their own save and download dialogs. Converting does not recover characters that were already replaced by the wrong encoding, so keep the original file.
NoCodeCSV Team
Updated October 12, 2026 · Practical guides by the NoCodeCSV team.
Tools mentioned in this guide
Encoding work is a check-then-convert loop, so the useful tools are the ones that take the repetition out of it:
- OpenCode Go — a folder of exports that all need detecting and converting to UTF-8 is a short script you run once and keep. Try OpenCode Go
- Stack AI — when a file arrives on a schedule, a workflow can convert and load it without anyone opening an import dialog. Try Stack AI
- Softr — if the CSV exists to feed a lookup table, holding the records in a no-code app removes the export step and the encoding question with it. Try Softr
Some links above are affiliate links — if you buy through them we may earn a commission at no extra cost to you. OpenCode Go uses our referral link; the other two point to each vendor's official page until our tracking links are approved.
Check the File Before You Convert It
Open the CSV in the analyzer and read the text as it is stored, so you know what you are converting rather than what a spreadsheet decided.
Sources for this article: the Unicode FAQ "UTF-8, UTF-16, UTF-32 & BOM" for the BOM character U+FEFF and the byte sequences EF BB BF, FF FE, FE FF and the two UTF-32 forms; RFC 4180 for the text/csv media type, its optional charset parameter and the CRLF line rule; and Microsoft Support "Import or export text (.txt or .csv) files" for the 1,048,576 by 16,384 import and export ceiling. The commands shown are from the Python, GNU file and iconv documentation. All were checked on the day this page was published.
Related reading
Data Cleaning — other guides that pair well with this one.
- Remove Blank Rows from CSV
- Fix Garbled CSV in Excel
- How to Open a CSV File on Android (Google Sheets, Text Editors, File Managers)
- What Is CSV File Format? (RFC 4180 Explained)
Browse all guides in the NoCodeCSV blog.