BOM UTF-8: Perbaiki Error JSON.parse dan CSV Excel
Error parse JSON akibat BOM UTF-8 adalah tiga byte yang tidak bisa Anda lihat. Berkas (file) itu terbuka bersih di editor, cat mencetak persis seperti yang Anda harapkan, linter Anda tidak mengeluh, dan JSON.parse tetap melempar error tepat di karakter paling pertama.
Diukur pada node v25.8.2, error yang muncul terlihat seperti ini:
SyntaxError: Unexpected token '', "{"a":1}" is not valid JSON
Apa pun yang digambar terminal Anda di dalam tanda kutip itu adalah satu karakter: U+FEFF, yang tersimpan sebagai byte EF BB BF. JSON yang ketat tidak menyediakan tempat untuknya. Parser di posisi 0 mengharapkan {, [, sebuah digit, tanda kutip, atau spasi kosong (whitespace), dan U+FEFF bukan salah satu di antaranya.
Kalau Anda sudah yakin itu BOM, pilih sisi yang bisa Anda kendalikan:
| Di mana Anda bisa mengubah sesuatu | Perbaikannya |
|---|---|
| Node, saat membaca file | JSON.parse(raw.replace(/^/, '')) |
| Python, saat membaca file | open(path, encoding='utf-8-sig') |
| File yang ada di disk | tail -c +4 data.json > clean.json |
Sisa halaman ini menangani kasus saat cara itu tidak mempan: error yang tampak seperti BOM padahal bukan, sumber yang terus-menerus memasangnya kembali, dan satu format yang justru rusak kalau BOM-nya dihapus. Untuk memahami apa itu BOM dan apakah file baru sebaiknya memilikinya, panduan encoding UTF-8 dan UTF-16 sudah membahas wilayah itu. Halaman ini berangkat dari asumsi bahwa BOM milik Anda sudah terlanjur merusak sesuatu.
Semua pengukuran di bawah dijalankan pada node v25.8.2 dan Python 3.14.5.
1. Yang sudah dicoret pesan error sebelum Anda menyalahkan BOM
Kebanyakan orang yang mencari error JSON di posisi 0 sebenarnya tidak punya BOM. Empat masalah yang sama sekali berbeda menghasilkan pesan dengan bentuk yang sama, dan melirik karakter di dalam tanda kutip sudah cukup untuk memisahkannya. Berikut string harfiah yang dikeluarkan V8:
| Teks error | Sebenarnya masalah apa | Langkah berikutnya |
|---|---|---|
Unexpected token '', "{"a":1}" is not valid JSON | BOM UTF-8 di byte 0 | Bagian 2 |
Unexpected token '<', "<!DOCTYPE "... is not valid JSON | Responsnya ternyata HTML: halaman error, pengalihan ke login, atau pemberitahuan dari proxy | Catat body mentah beserta kode statusnya |
Unexpected end of JSON input | Body-nya kosong | Periksa kode status dan Content-Length |
"undefined" is not valid JSON | Anda menyerahkan variabel yang tidak pernah diisi ke JSON.parse | Perbaiki sisi pemanggilnya |
Aturannya cukup pendek untuk dihafal. Bacalah karakter di dalam tanda kutip tunggal. < berarti yang Anda terima adalah HTML. Sebuah kotak, ruang kosong, atau tanda tanya yang tidak bisa Anda seleksi berarti U+FEFF. Tidak ada apa pun di dalam kutip berarti memang sejak awal tidak ada input.
Kalimat versi lama dan versi baru
Hasil pencarian untuk json parse unexpected token position 0 sebagian besar ditulis untuk pesan V8 yang lebih lama:
SyntaxError: Unexpected token in JSON at position 0
Kalimat itu menyebut offset dan justru menyembunyikan karakternya. Kalimat yang sekarang melakukan kebalikannya: ia menampilkan karakter beserta potongan input, jauh lebih berguna. Konsekuensinya, halaman yang Anda buka bisa saja membahas runtime yang tidak Anda pakai. Kalau error Anda masih menyebut posisi alih-alih karakter, Anda ada di engine yang lebih tua, dan diagnosis di bawah tetap berlaku.
2. Pastikan itu memang BOM dalam sepuluh detik
Empat pemeriksaan, kira-kira urut dari yang paling cepat. Satu saja di antaranya sudah cukup untuk memutuskan.
Lihat tiga byte pertamanya.
$ hexdump -C data.json | head -1
00000000 ef bb bf 7b 22 61 22 3a 31 7d |...{"a":1}|
ef bb bf sebelum 7b ({) itulah BOM-nya. Tanda ... di kolom ASCII sebelah kanan adalah cara hexdump memberi tahu bahwa tidak ada karakter yang bisa dicetak di sana.
Tanyakan pada file. Perintah ini menyebutkannya secara langsung, dan bahkan berubah pikiran soal jenis file-nya:
$ file data.json
data.json: Unicode text, UTF-8 (with BOM) text, with no line terminators
$ file clean.json
clean.json: JSON data
Periksa code point pertama di Node.
const fs = require('fs');
const raw = fs.readFileSync('data.json', 'utf8');
console.log(raw.charCodeAt(0) === 0xFEFF); // true
Baca status bar editor Anda. VS Code menampilkan UTF-8 with BOM di sudut kanan bawah, dan mengekliknya akan menawarkan Save with encoding. Label itulah alasan utama file tadi terlihat baik-baik saja: editor Anda sudah tahu, hanya saja ia menyampaikannya terlalu pelan.
Untuk melihat isi byte dari sesuatu yang tidak bisa Anda dump secara lokal, tempelkan saja ke encoder dan decoder Base64. BOM UTF-8 di awal sebuah payload selalu ter-encode menjadi string yang diawali 77u/, dan pola itu gampang dikenali di baris log.
3. Dari mana BOM Anda berasal
Menghapus BOM dari file yang toh dibuat ulang oleh build step setiap jam adalah perbaikan dengan masa hidup satu jam. Penghasil BOM yang paling sering muncul:
- Menu Save As → CSV UTF-8 di Excel. Yang satu ini disengaja, bukan bug, dan bagian 7 menjelaskan alasannya.
- Notepad dan editor Windows lain yang menyediakan UTF-8 with BOM sebagai opsi penyimpanan tersendiri, kadang malah sebagai opsi bawaan.
- VS Code, ketika
files.encodingdisetel keutf8bom, entah di pengaturan pengguna Anda atau ikut ter-commit di.vscode/settings.jsonyang tidak pernah dilirik siapa pun. - Pengalihan (redirection) di PowerShell.
>danOut-Filemenulis BOM secara bawaan di sebagian versi PowerShell, dan nilai bawaannya berbeda antara jalur 5.x khusus Windows dan jalur 6/7 yang lintas platform. Jangan mengandalkan ingatan untuk yang ini: tulis satu file lalu periksa tiga byte pertamanya dengan perintah di bagian 2. - Kode ekspor buatan sendiri. Penulis file yang merakit encoder UTF-8 tanpa menyatakan apakah tanda tangan (signature) perlu ditulis akan mewarisi nilai bawaan framework-nya, dan tiap framework memilih nilai bawaan yang berbeda. Jalur ekspor .NET lawas dan Java lawas adalah tersangka langganannya.
- Perkakas ekspor database dan BI, yang sering menyertakan BOM karena konsumen utamanya memang spreadsheet.
Kalau file-nya datang dari mitra atau vendor dan Anda tidak bisa mengubah sisi produsennya, langsung saja ke bagian 4 dan hapus BOM-nya saat dibaca. Kalau ia berasal dari repositori Anda sendiri, bagian 9 adalah jawaban yang tahan lama.
4. Memperbaikinya di JavaScript dan Node
Di sinilah kebingungannya menumpuk, karena ekosistem JavaScript tidak punya satu kebijakan BOM. Ia punya beberapa, dan semuanya saling bertentangan. File yang sama, runtime yang sama, diukur pada node v25.8.2:
| API | Perilaku terhadap BOM | JSON.parse sesudahnya |
|---|---|---|
fetch → res.json() | dihapus | berhasil |
fs.readFileSync(f, 'utf8') | dipertahankan | gagal |
new TextDecoder() (bawaan) | dihapus | berhasil |
new TextDecoder('utf-8', { ignoreBOM: true }) | dipertahankan | gagal |
require('./data.json') | dihapus | tidak relevan, sudah ter-parse |
import(..., { with: { type: 'json' } }) | dihapus | tidak relevan, sudah ter-parse |
Ada dua hal yang tidak muat di tabel itu, dan keduanya rutin menghabiskan waktu berjam-jam.
ignoreBOM melakukan kebalikan dari bunyi namanya
ignoreBOM: true tidak berarti “abaikan BOM-nya”. Artinya “abaikan makna khusus BOM dan pertahankan ia sebagai karakter biasa”. Nilai bawaannya, false, justru itulah yang menghapusnya. Nama tersebut menggambarkan apa yang diabaikan decoder, bukan apa yang Anda terima, dan membacanya secara alami justru memberi Anda decoder yang mempertahankan byte yang tadi ingin Anda hapus.
Kenapa berhasil di browser tetapi gagal di Node
Inilah versi masalah yang paling sering dilaporkan: URL JSON yang sama ter-parse mulus di kode front-end, lalu melempar error begitu sebuah skrip Node membaca file-nya dari disk. Tidak ada yang berubah pada file itu. res.json() melakukan decoding lewat mesin yang sama dengan TextDecoder dan membuang BOM-nya di tengah jalan; fs.readFileSync(path, 'utf8') melakukan decoding apa adanya dan menyerahkan setiap karakter yang ada di file kepada Anda, termasuk U+FEFF.
Asimetri yang sama menjelaskan kenapa require('./config.json') berhasil sementara JSON.parse(fs.readFileSync('./config.json', 'utf8')) tidak. Pemuat modul JSON milik Node menghapus BOM; jalur manual tidak.
Cara menghapusnya
const fs = require('fs');
const raw = fs.readFileSync('data.json', 'utf8');
const data = JSON.parse(raw.replace(/^/, ''));
Jangkarkan polanya dengan ^. Penggantian global tanpa jangkar juga akan menghapus karakter U+FEFF yang sah dari dalam nilai string, dan itu kehilangan data, bukan perbaikan.
Ada jalan pintas yang kebetulan berhasil: JSON.parse(raw.trim()) juga sukses, karena ECMAScript menggolongkan U+FEFF sebagai whitespace dan String.prototype.trim menghapusnya. Perilaku itu nyata dan sudah diverifikasi di atas, tetapi ia hanyalah kebetulan dari spesifikasi JavaScript dan tidak berlaku di bahasa lain. str.strip() milik Python meninggalkan U+FEFF persis di tempat ia menemukannya.
Kalau Anda ingin memastikan hasil setelah BOM dihapus benar-benar valid dan bukan sekadar tidak melempar error, tempelkan ke formatter dan validator JSON. Begitu BOM-nya hilang, sisa kandidat masalah di posisi 0 adalah persoalan escaping biasa yang dibahas di panduan escape string JSON.
5. Memperbaikinya di Python: utf-8-sig
Python adalah satu-satunya runtime yang menyebutkan masalahnya langsung di pesan error. Buka file berawalan BOM sebagai UTF-8 biasa, dan json akan memberi tahu Anda jawabannya sekaligus perbaikannya dalam satu tarikan napas:
JSONDecodeError: Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0)
Kalau Anda mencari unexpected utf-8 bom lalu mendarat di sini, string itulah asal-usulnya. Codec yang ditunjuknya membaca BOM sebagai tanda tangan lalu membuangnya:
import json
with open('data.json', encoding='utf-8-sig') as f:
data = json.load(f)
utf-8-sig aman dipakai pada file yang tidak punya BOM. Ia menghapus satu BOM kalau ada, dan berperilaku seperti UTF-8 biasa kalau tidak ada, sehingga ia jadi pilihan bawaan yang tepat untuk file apa pun yang tidak Anda buat sendiri.
Byte dan teks berperilaku berbeda
Ada asimetri yang layak diketahui, karena inilah yang membuat bug-nya tampak muncul dan hilang sendiri:
import json
json.loads(open('data.json', 'rb').read()) # {'a': 1} berhasil
json.loads(open('data.json', encoding='utf-8').read()) # memunculkan error di atas
json.loads yang menerima bytes menjalankan langkah deteksi penyandian (encoding) lebih dulu, mengenali BOM-nya, lalu melakukan decoding dengan utf-8-sig untuk Anda. Berikan str yang sudah ter-decode dan tidak ada lagi yang bisa dideteksi, sehingga U+FEFF sampai ke parser. Dua jalur kode yang terlihat setara, dan salah satunya diam-diam menangani kasus ini.
Menulis BOM dengan sengaja
Codec yang sama berjalan ke arah sebaliknya, dan begitulah cara Anda menghasilkan file untuk Excel:
with open('report.csv', 'w', encoding='utf-8-sig', newline='') as f:
f.write('name\n')
File itu diawali ef bb bf. Bagian 7 membahas kapan Anda memang menginginkannya.
Jebakan CSV
csv.DictReader pada teks berawalan BOM melakukan persis apa yang seharusnya dilakukan parser CSV yang benar, dan hasilnya adalah sebuah key yang tidak bisa dicocokkan siapa pun:
import csv, io
data = 'name,age\nAlice,30\n'
print(list(next(csv.DictReader(io.StringIO(data))).keys()))
# ['name', 'age']
Kolom pertama Anda bukan name. Ia adalah U+FEFF yang diikuti name, dan setiap pencarian row['name'] akan memunculkan KeyError sementara header-nya tercetak dengan benar di semua debugger yang Anda punya. Membuka file dengan encoding='utf-8-sig' menghapusnya sebelum reader sempat melihatnya.
6. Menghapus BOM di Java, Go, PHP, dan shell
Semua perbaikan di bawah sebenarnya adalah perbaikan yang sama pada ketinggian berbeda: hapus tiga byte (EF BB BF) atau hapus satu karakter (U+FEFF), tergantung Anda sedang memegang byte atau teks. Kalau bahasa Anda tidak punya codec yang mengenali BOM, kerjakan manual.
Java melakukan decoding BOM menjadi satu karakter di awal:
String text = Files.readString(path, StandardCharsets.UTF_8);
if (!text.isEmpty() && text.charAt(0) == '') {
text = text.substring(1);
}
Go bekerja di level byte, sebelum unmarshalling:
raw, err := os.ReadFile("data.json")
if err != nil {
return err
}
raw = bytes.TrimPrefix(raw, []byte{0xEF, 0xBB, 0xBF})
var v map[string]any
err = json.Unmarshal(raw, &v)
PHP memakai pola yang berjangkar pada byte:
$raw = file_get_contents('data.json');
$raw = preg_replace('/^\xEF\xBB\xBF/', '', $raw);
$data = json_decode($raw, true);
Empat perintah berikut menghapus BOM dari sebuah file, bukan dari sebuah variabel. Semuanya sudah teruji pada file yang diawali ef bb bf:
# Langsung di tempat, GNU sed (Linux). Escape-nya diekspansi oleh shell, bukan oleh sed.
sed -i $'1s/^\xEF\xBB\xBF//' data.json
# Langsung di tempat, BSD sed (macOS)
sed -i '' $'1s/^\xEF\xBB\xBF//' data.json
# Langsung di tempat, di mana pun Perl tersedia. Baris pertama saja.
perl -i -pe 's/^\x{ef}\x{bb}\x{bf}// if $. == 1' data.json
# Salin tanpa tiga byte pertama. Hanya aman kalau Anda yakin BOM-nya memang ada.
tail -c +4 data.json > clean.json
Bentuk tail adalah yang paling kasar: ia membuang tiga byte tanpa peduli apakah byte-byte itu memang BOM. Pastikan dulu lewat bagian 2.
7. Pengecualian CSV: ketika Excel justru butuh BOM-nya dipertahankan
Semua yang di atas memperlakukan BOM sebagai kerusakan. Di satu tempat, ia justru memikul fungsi penting, dan menghapusnya malah merusak file yang tadinya bekerja.
Pencarian untuk csv bom excel terbelah jadi dua keluhan yang berlawanan, dan itu pertanda kuat bahwa satu aturan sedang dipakai ke arah yang keliru:
- “CSV saya terbuka di Excel dengan
édanæ¥æ¬èªalih-alih karakter aslinya.” BOM-nya hilang. - “Kolom pertama saya bernama
namedan skrip saya tidak bisa menemukannya.” BOM-nya ada.
Kenapa Excel menginginkannya
Excel di Windows tidak punya cara yang andal untuk tahu bahwa sebuah CSV berformat UTF-8. Tidak ada header, tidak ada deklarasi encoding: file .csv hanyalah byte. Tanpa sinyal, Excel jatuh kembali ke locale sistem, yaitu Windows-1252 di Amerika dan Eropa Barat, Windows-1251 di Rusia. Akibatnya setiap karakter non-ASCII keluar salah. BOM adalah sinyal itu. Tiga byte di bagian depan, dan Excel membaca UTF-8 dengan benar.
Itu membuat BOM pada CSV menjadi fitur alih-alih cacat, dan dari situ lahir aturan yang muat dalam satu baris:
Kalau file-nya akan dibaca mesin, hapus BOM-nya. Kalau akan diklik ganda orang di Excel, pertahankan.
Kegagalan di sisi sebaliknya
Serahkan file yang sama ke sebuah parser, dan BOM-nya akan melebur ke dalam sel header pertama Anda. Di Node:
const header = 'name,age'.split(',');
console.log(JSON.stringify(header)); // ["name","age"]
const row = { 'name': 'Alice', age: 30 };
console.log(row.name); // undefined
row.name bernilai undefined sementara key-nya tercetak sebagai name di log, di debugger, dan di console.table. Bentuk bug-nya sama persis dengan KeyError Python di bagian 5, dan itulah sebabnya gejala “nama field-nya cocok tetapi nilainya hilang” layak langsung Anda curigai sebagai BOM.
Konverter kami sendiri sengaja berdiri di kedua sisi ini. Konverter CSV ke JSON menghapus BOM di awal input sebelum parsing, sehingga file yang langsung keluar dari Excel menghasilkan name dan bukan name. Ke arah sebaliknya, konverter JSON ke CSV menjadikan BOM sebagai sakelar eksplisit, dan preset Excel-nya menyalakan sakelar itu bersama pemisah titik koma dan akhir baris CRLF, yaitu kombinasi yang memang dibutuhkan locale Excel di Eropa. Untuk keputusan konversi lain seputar pemisah, pengutipan, dan penyimpulan tipe, panduan konversi CSV dan JSON memuat pembahasan lengkapnya.
8. Di luar JSON: di mana lagi BOM muncul
JSON berisik soal ini. Format lain tidak.
Skrip shell. BOM duduk di antara awal file dan #!, sehingga kernel tidak pernah melihat shebang dan tidak pernah menjalankan interpreter Anda. Di macOS, hasil pengukuran menunjukkan shell jatuh kembali ke sh lalu melaporkan baris shebang sebagai file yang tidak ada:
./bom.sh: line 1: #!/bin/sh: No such file or directory
Skripnya lalu tetap berjalan di bawah interpreter yang salah, dan itu lebih buruk daripada gagal. Sistem lain melaporkannya dengan kalimat berbeda, yang paling terkenal sebagai error bad interpreter. Kalau sebuah skrip dengan shebang #!/usr/bin/env python3 yang jelas benar tetap bersikeras bahwa path itu tidak ada, periksa byte-nya.
PHP. Apa pun di luar <?php ... ?> adalah output, dan BOM sebelum tag pembuka berarti tiga byte output yang terkirim sebelum kode Anda berjalan. Panggilan header(), session_start(), atau setcookie() yang pertama kemudian gagal dengan peringatan klasik headers already sent, sambil menunjuk ke baris 1 sebuah file yang baris 1-nya terlihat kosong.
File .env dan format key-value apa pun. Mekanismenya identik dengan kasus CSV: variabel pertama Anda bukan DATABASE_URL, melainkan U+FEFF yang diikuti DATABASE_URL, jadi pencariannya meleset padahal file-nya terbaca normal oleh manusia. Semua variabel sesudahnya bekerja, dan itu membuatnya tampak seperti masalah pada satu pengaturan tertentu saja.
XML adalah pengecualian ke arah yang berlawanan. Spesifikasi XML secara eksplisit mengizinkan BOM UTF-8 di awal dokumen sebagai bagian dari deteksi otomatis encoding, dan parser wajib menanganinya. xml.etree.ElementTree milik Python menerima dokumen berawalan BOM tanpa protes dalam pengujian. Kalau XML Anda gagal, BOM kemungkinan besar bukan penyebabnya.
9. Hentikan dari sumbernya
Begitu Anda paham mekanismenya, menghapus BOM dari satu file jadi pekerjaan sepele. Yang tersisa adalah mencegah file itu mendapatkannya kembali.
Kunci encoding-nya di .editorconfig. Properti charset menerima utf-8 dan utf-8-bom sebagai dua nilai terpisah, jadi menyebutkan mana yang Anda mau tidak menyisakan ambiguitas:
[*]
charset = utf-8
Periksa pengaturan editor yang bisa menimpanya. Di VS Code itu adalah "files.encoding": "utf8", dan nilai yang perlu Anda waspadai adalah utf8bom. Periksa .vscode/settings.json di workspace selain pengaturan pengguna Anda, karena pengaturan workspace yang ikut ter-commit akan diam-diam berlaku bagi semua orang di tim.
Pindai di CI atau di pre-commit hook. Skrip ini portabel, tanpa dependensi, dan keluar dengan status bukan nol ketika menemukan sesuatu:
#!/bin/sh
# Gagalkan kalau ada file terlacak yang diawali EF BB BF
found=0
for f in $(git ls-files '*.json' '*.md' '*.sh'); do
if [ "$(head -c3 "$f" | od -An -tx1 | tr -d '[:space:]')" = "efbbbf" ]; then
echo "BOM: $f"
found=1
fi
done
exit $found
Skrip ini sudah teruji ke dua arah: ia mendaftar path yang bermasalah dan keluar dengan 1 ketika ada file berawalan BOM yang terlacak, lalu keluar dengan 0 begitu file-file itu bersih.
Tuliskan satu-satunya pengecualian yang diizinkan. Aturan “tidak ada BOM di mana pun” akan patah pertama kali seseorang butuh ekspor spreadsheet, dan sesudah itu tidak ada lagi yang menganggapnya serius. Nyatakan saja pengecualiannya: BOM diizinkan pada file CSV yang dihasilkan untuk Excel, tidak di tempat lain. Kecualikan direktori ekspor dari pemindai, dan aturan itu akan selamat saat bersentuhan dengan kenyataan.
10. Alur biseksi enam puluh detik
Jalankan berurutan. Setiap langkah entah mengakhiri investigasi atau menyerahkan masalah yang lebih kecil ke langkah berikutnya.
- Baca karakternya, bukan posisinya. Bagian 1.
<berarti HTML dan urusan Anda selesai di sini. Tidak ada apa pun di dalam kutip berarti body kosong. Kotak yang tidak terbaca berarti lanjutkan. - Pastikan byte-nya.
hexdump -C file | head -1. Kalau tiga byte pertamanya bukanef bb bf, berhentilah: ini bukan BOM dan tidak ada satu pun di bawah yang akan menolong. - Cari di mana ia masuk. Apakah file-nya sudah berawalan BOM di disk, atau bersih di disk lalu jadi berawalan BOM pada saat kode Anda memegangnya? File yang bersih di disk berarti ada sesuatu di pipeline Anda yang menambahkannya.
- Pilih satu sisi untuk diperbaiki. Hapus saat membaca kalau produsennya adalah vendor, sebuah unggahan, atau build step yang bukan milik Anda. Perbaiki produsennya kalau ia milik Anda, karena perbaikan di sisi pembaca harus diulang di setiap pembaca.
- Terapkan perbaikannya di batas decoding, jangan lebih dalam.
encoding='utf-8-sig'di panggilanopen(), bukan.lstrip()pada sebuah string tiga fungsi setelahnya. Memperbaikinya jauh di dalam tumpukan berarti jalur kode berikutnya yang membaca file itu akan menemukan bug yang sama dari nol. - Verifikasi bahwa byte-nya berubah. Ulangi langkah 2. Perbaikan yang bekerja di satu jalur kode tetapi meninggalkan file-nya utuh akan gagal di jalur berikutnya.
- Pasang pemindainya. Bagian 9. Kalau tidak, Anda akan mengulang semua ini kuartal depan.
FAQ
Apakah BOM UTF-8 itu wajib?
Tidak. UTF-8 hanya punya satu urutan byte, jadi tidak ada yang perlu diperjelas penanda apa pun. Unicode mengizinkan BOM UTF-8 sebagai tanda tangan encoding tetapi tidak menganjurkannya, dan JSON melarangnya terang-terangan: RFC 8259 menyatakan bahwa implementasi tidak boleh menambahkan byte order mark ke sebuah teks JSON.
Kenapa file-nya terlihat baik-baik saja di editor saya tetapi gagal di-parse?
Karena U+FEFF sama sekali tidak tergambar. Editor yang mengenalinya menyembunyikan karakter itu dan sebagai gantinya menyebut UTF-8 with BOM di status bar. Editor yang tidak mengenalinya hanya menggambar nol piksel. cat, less, dan diff pada code review juga terlihat identik. Hanya tampilan di level byte yang bisa membongkarnya.
Apakah JSON.parse pernah menghapus BOM secara otomatis?
Tidak pernah. JSON.parse menerima sebuah string dan memperlakukan U+FEFF sebagai karakter tak terduga di mana pun ia muncul. Yang menghapusnya adalah lapisan di atasnya: res.json() sesudah fetch, require() milik Node untuk file .json, dan TextDecoder pada pengaturan bawaannya sama-sama menghapusnya sebelum parser melihat apa pun.
Haruskah saya menghapus BOM dari file CSV?
Tergantung siapa yang membuka file-nya. Parser mana pun akan melipat BOM ke dalam nama kolom pertama Anda, sehingga name menjadi name dan setiap pencarian meleset. Di sana, hapuslah. Excel di Windows memakai BOM untuk mendeteksi UTF-8 dan mengacaukan karakter beraksen serta CJK tanpanya, jadi di sana, pertahankanlah.
Apakah BOM sama dengan zero-width space?
Code point-nya sama, tugasnya berbeda. U+FEFF di offset 0 adalah byte order mark. Di posisi lain mana pun dalam dokumen, ia adalah ZERO WIDTH NO-BREAK SPACE, penggunaan yang sudah ditinggalkan Unicode dan digantikan U+2060 WORD JOINER. Teks lama masih memuatnya, dan itulah sebabnya U+FEFF muncul di tengah-tengah file.
Apakah BOM memengaruhi diff git dan ukuran file?
Tiga byte di disk, dan satu baris berisik di setiap diff yang menyentuhnya. Git membandingkan byte, jadi menambah atau menghapus BOM akan menulis ulang baris 1 sekalipun teks yang dirender identik. Dari sanalah datang perubahan satu baris yang tidak bisa dijelaskan siapa pun saat review.