Skip to content
Powrót do bloga
Poradniki

BOM UTF-8: napraw błędy JSON.parse i problemy z CSV

BOM UTF-8 psuje JSON.parse w pliku, który wygląda idealnie. Rozpoznaj niewidoczne bajty EF BB BF, usuń je w dowolnym języku i sprawdź, kiedy Excel ich potrzebuje.

14 min czytania

BOM UTF-8: napraw błędy JSON.parse i problemy z CSV

BOM UTF-8 to trzy bajty, których nie widać, a wystarczą, żeby przetwarzanie JSON zakończyło się błędem. Plik otwiera się w edytorze bez zarzutu, cat wypisuje dokładnie to, czego można się spodziewać, linter nie zgłasza uwag, a JSON.parse i tak rzuca wyjątek na pierwszym znaku.

W pomiarze na node v25.8.2 wyjątek wygląda tak:

SyntaxError: Unexpected token '', "{"a":1}" is not valid JSON

Cokolwiek terminal narysował wewnątrz tych cudzysłowów, to jeden znak: U+FEFF, zapisany jako bajty EF BB BF. Ścisły JSON nie ma dla niego miejsca. Parser na pozycji 0 oczekuje {, [, cyfry, cudzysłowu albo białego znaku, a U+FEFF nie jest żadną z tych rzeczy.

Jeśli wiadomo już, że chodzi o BOM, wystarczy wybrać stronę, na którą można wpłynąć:

Gdzie da się coś zmienićPoprawka
Node, odczyt plikuJSON.parse(raw.replace(/^/, ''))
Python, odczyt plikuopen(path, encoding='utf-8-sig')
Plik na dyskutail -c +4 data.json > clean.json

Reszta tej strony jest na wypadek, gdy to nie wystarcza: kiedy błąd tylko wygląda na BOM albo kiedy coś wstawia znacznik z powrotem po każdym buildzie. Jest też jeden format, w którym usunięcie BOM-u psuje działający plik, zamiast go naprawiać. Czym BOM w ogóle jest i czy nowy plik powinien go mieć, opisuje przewodnik po UTF-8, UTF-16 i Unicode. Ta strona zakłada, że BOM już coś zepsuł.

Wszystkie pomiary poniżej wykonano na node v25.8.2 i Python 3.14.5.

1. Co komunikat błędu wyklucza, zanim padnie podejrzenie na BOM

Większość osób, które szukają błędu JSON na pozycji 0, wcale nie ma BOM-u. Cztery różne problemy dają niemal identyczny komunikat, a rozdziela je jedno spojrzenie na znak w cudzysłowie. To są dosłowne ciągi, które wypisuje V8:

Treść błęduCo to naprawdę jestNastępny krok
Unexpected token '', "{"a":1}" is not valid JSONBOM UTF-8 na bajcie 0Sekcja 2
Unexpected token '<', "<!DOCTYPE "... is not valid JSONOdpowiedź była HTML-em: strona błędu, przekierowanie do logowania, komunikat proxyZapisz w logu surowe ciało odpowiedzi i kod statusu
Unexpected end of JSON inputCiało odpowiedzi było pusteSprawdź kod statusu i Content-Length
"undefined" is not valid JSONDo JSON.parse trafiła zmienna, której nigdy nie przypisano wartościPopraw kod wywołujący

Reguła mieści się w jednym zdaniu. Przeczytaj znak wewnątrz apostrofów. < oznacza, że przyszedł HTML. Prostokąt, pusta przestrzeń albo znak zapytania, którego nie da się zaznaczyć, oznacza U+FEFF. Brak czegokolwiek w cudzysłowie znaczy, że wejścia w ogóle nie było.

Stare i nowe brzmienie komunikatu

Wyniki wyszukiwania dla json parse unexpected token position 0 opisują w większości starszy komunikat V8:

SyntaxError: Unexpected token in JSON at position 0

Tamto sformułowanie podawało przesunięcie i ukrywało znak. Obecne robi odwrotnie: pokazuje znak i fragment wejścia, co jest znacznie użyteczniejsze, ale oznacza też, że znaleziona strona może opisywać inne środowisko uruchomieniowe niż to, w którym błąd wystąpił. Jeśli błąd nadal podaje pozycję zamiast znaku, silnik jest starszy, ale diagnoza poniżej pozostaje bez zmian.

2. Potwierdzenie, że to BOM, w dziesięć sekund

Poniższe cztery sprawdzenia ułożone są mniej więcej od najszybszego. Każde rozstrzyga sprawę samodzielnie.

Spójrz na pierwsze trzy bajty.

$ hexdump -C data.json | head -1
00000000  ef bb bf 7b 22 61 22 3a  31 7d                    |...{"a":1}|

ef bb bf przed 7b ({) to właśnie BOM. Kropki ... w kolumnie ASCII po prawej oznaczają, że hexdump nie ma tu nic drukowalnego do pokazania.

Zapytaj file. Mówi to wprost, a przy okazji całkowicie zmienia zdanie co do typu pliku:

$ file data.json
data.json: Unicode text, UTF-8 (with BOM) text, with no line terminators

$ file clean.json
clean.json: JSON data

Sprawdź pierwszy punkt kodowy w Node.

const fs = require('fs');
const raw = fs.readFileSync('data.json', 'utf8');
console.log(raw.charCodeAt(0) === 0xFEFF);   // true

Przeczytaj pasek stanu edytora. VS Code pokazuje UTF-8 with BOM w prawym dolnym rogu, a kliknięcie tej etykiety proponuje Save with encoding. Ta etykieta była przez cały czas jedyną wskazówką: edytor wiedział, tylko nie powiedział tego głośniej.

Żeby zajrzeć na poziom bajtów w czymś, czego nie da się zrzucić lokalnie, wystarczy wkleić to do kodera i dekodera Base64. BOM UTF-8 na początku payloadu koduje się zawsze do ciągu zaczynającego się od 77u/, co widać gołym okiem w linijce logu.

3. Skąd wziął się BOM

Usunięcie BOM-u z pliku, który krok builda odtwarza co godzinę, to poprawka o godzinnej żywotności. Typowi producenci:

  • Excel i jego Zapisz jako → CSV UTF-8. To działanie celowe, nie błąd. Sekcja 7 wyjaśnia dlaczego.
  • Notatnik i inne edytory windowsowe, które oferują UTF-8 z BOM jako osobną opcję zapisu, czasem domyślną.
  • VS Code, gdy files.encoding jest ustawione na utf8bom — w ustawieniach użytkownika albo w pliku .vscode/settings.json, który leży w repozytorium i do którego nikt nie zagląda.
  • Przekierowanie strumienia w PowerShellu. > i Out-File w części wersji PowerShella zapisują BOM domyślnie, a domyślne ustawienie różni się między linią 5.x (tylko Windows) a wieloplatformową linią 6/7. Tu nie należy polegać na pamięci: zapisz jeden plik i sprawdź jego pierwsze trzy bajty poleceniami z sekcji 2.
  • Własnoręcznie napisany kod eksportu. Kod, który tworzy koder UTF-8 bez wskazania, czy emitować sygnaturę, dziedziczy to, co dany framework wybrał jako domyślne — a frameworki nie wybrały tego samego. Najczęściej chodzi o stare ścieżki eksportu w .NET i w Javie.
  • Narzędzia eksportu z baz danych i systemów BI, które często dokładają BOM, bo ich głównym odbiorcą jest arkusz kalkulacyjny.

Jeśli plik przychodzi od partnera albo dostawcy i producenta nie da się zmienić, przejdź do sekcji 4 i usuwaj BOM przy odczycie. Jeśli pochodzi z własnego repozytorium, trwałą odpowiedzią jest sekcja 9.

4. Naprawa w JavaScripcie i w Node

Tu koncentruje się całe zamieszanie, bo ekosystem JavaScriptu nie ma jednej polityki wobec BOM-u. Ma kilka i są ze sobą sprzeczne. Ten sam plik, to samo środowisko uruchomieniowe, pomiar na node v25.8.2:

APIZachowanie wobec BOMKolejny JSON.parse
fetchres.json()usuwanyudaje się
fs.readFileSync(f, 'utf8')zachowywanyzawodzi
new TextDecoder() (domyślnie)usuwanyudaje się
new TextDecoder('utf-8', { ignoreBOM: true })zachowywanyzawodzi
require('./data.json')usuwanynie dotyczy, JSON już przetworzony
import(..., { with: { type: 'json' } })usuwanynie dotyczy, JSON już przetworzony

Z tej tabeli wynikają dwie rzeczy, na których łatwo stracić popołudnie.

ignoreBOM robi coś odwrotnego, niż zapowiada

ignoreBOM: true nie znaczy „zignoruj BOM”. Znaczy „zignoruj szczególne znaczenie BOM-u i zachowaj go jako zwykły znak”. Usuwa go dopiero wartość domyślna, czyli false. Nazwa opisuje, co ignoruje dekoder, a nie to, co wychodzi na końcu. W naturalnym odczytaniu wychodzi na odwrót: dekoder zatrzymuje dokładnie ten bajt, który miał zniknąć.

Dlaczego działa w przeglądarce, a psuje się w Node

To najczęściej zgłaszany wariant problemu: ten sam URL z JSON-em przetwarza się bez kłopotu w kodzie frontendowym i rzuca wyjątek w chwili, gdy skrypt Node odczytuje plik z dysku. W samym pliku nic się nie zmieniło. res.json() dekoduje przez tę samą maszynerię co TextDecoder i po drodze gubi BOM; fs.readFileSync(path, 'utf8') to wierne dekodowanie, które oddaje każdy znak zawarty w pliku, łącznie z U+FEFF.

Ta sama asymetria tłumaczy, dlaczego require('./config.json') działa, a JSON.parse(fs.readFileSync('./config.json', 'utf8')) już nie. Loader modułów JSON w Node usuwa BOM; ręczna ścieżka tego nie robi.

Usuwanie BOM-u

const fs = require('fs');
const raw = fs.readFileSync('data.json', 'utf8');
const data = JSON.parse(raw.replace(/^/, ''));

Wzorzec należy zakotwiczyć na ^. Niezakotwiczona globalna podmiana usunęłaby także prawidłowe znaki U+FEFF ze środka wartości tekstowych, a to już utrata danych, nie poprawka.

Cichsza alternatywa, która działa przypadkiem: JSON.parse(raw.trim()) również się udaje, bo ECMAScript klasyfikuje U+FEFF jako biały znak, a String.prototype.trim go usuwa. Zachowanie jest prawdziwe i sprawdzone, ale wynika ze zbiegu okoliczności w specyfikacji JavaScriptu i nie przenosi się na inne języki. Pythonowy str.strip() zostawia U+FEFF dokładnie tam, gdzie go zastał.

Żeby potwierdzić, że oczyszczony wynik jest naprawdę poprawny, a nie tylko nie rzuca wyjątku, wystarczy wkleić go do formatowania i walidacji JSON. Kiedy BOM zniknie, na pozycji 0 zostają już tylko zwykłe problemy z escapowaniem opisane w przewodniku po escapowaniu ciągów JSON.

5. Naprawa w Pythonie: utf-8-sig

Python to jedyne środowisko uruchomieniowe, które nazywa problem po imieniu w treści błędu. Wystarczy otworzyć plik z BOM-em jako zwykły UTF-8, a moduł json w jednym komunikacie podaje i diagnozę, i lekarstwo:

JSONDecodeError: Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0)

Jeśli ktoś szukał frazy unexpected utf-8 bom i trafił tutaj, to właśnie stąd bierze się ten ciąg. Kodek, na który komunikat wskazuje, czyta BOM jako sygnaturę i ją odrzuca:

import json

with open('data.json', encoding='utf-8-sig') as f:
    data = json.load(f)

utf-8-sig jest bezpieczny także dla plików bez BOM-u. Usuwa znacznik, jeśli jest, a w przeciwnym razie zachowuje się jak zwykły UTF-8. Dzięki temu nadaje się na domyślne ustawienie dla każdego pliku przyjętego z zewnątrz.

Bajty i tekst zachowują się inaczej

Ta asymetria sprawia, że błąd wygląda na sporadyczny:

import json

json.loads(open('data.json', 'rb').read())       # {'a': 1}      działa
json.loads(open('data.json', encoding='utf-8').read())  # rzuca powyższy błąd

json.loads wywołane na bytes uruchamia najpierw krok wykrywania kodowania, dostrzega BOM i dekoduje przez utf-8-sig. Podane już zdekodowane str nie zostawia nic do wykrycia, więc U+FEFF dociera do parsera. Dwie ścieżki kodu wyglądają równoważnie, a tylko jedna po cichu obsługuje ten przypadek.

Celowe zapisanie BOM-u

Ten sam kodek działa w drugą stronę i tak właśnie powstaje plik dla Excela:

with open('report.csv', 'w', encoding='utf-8-sig', newline='') as f:
    f.write('name\n')

Taki plik zaczyna się od ef bb bf. Sekcja 7 mówi, kiedy jest to pożądane.

Pułapka CSV

csv.DictReader na tekście z BOM-em robi dokładnie to, co powinien robić poprawny parser CSV, i tworzy klucz, którego nikt nie trafi:

import csv, io

data = 'name,age\nAlice,30\n'
print(list(next(csv.DictReader(io.StringIO(data))).keys()))
# ['name', 'age']

Pierwsza kolumna nie nazywa się name. To U+FEFF, a po nim name — każde odwołanie row['name'] rzuca KeyError, choć w debuggerze nagłówek wyświetla się poprawnie. Otwarcie pliku z encoding='utf-8-sig' usuwa znacznik, zanim czytnik w ogóle go zobaczy.

6. Usuwanie BOM-u w Javie, Go, PHP i w powłoce

Każda poprawka to ta sama poprawka na innym poziomie: usunąć trzy bajty (EF BB BF) albo usunąć jeden znak (U+FEFF), zależnie od tego, czy w ręku są bajty, czy tekst. Jeśli język nie ma kodeka świadomego BOM-u, trzeba to zrobić ręcznie.

Java dekoduje BOM na wiodący znak :

String text = Files.readString(path, StandardCharsets.UTF_8);
if (!text.isEmpty() && text.charAt(0) == '') {
    text = text.substring(1);
}

Go — praca na poziomie bajtów przed json.Unmarshal:

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 — wzorzec zakotwiczony na bajtach:

$raw  = file_get_contents('data.json');
$raw  = preg_replace('/^\xEF\xBB\xBF/', '', $raw);
$data = json_decode($raw, true);

Żeby usunąć BOM z samego pliku, a nie ze zmiennej, przydaje się jedno z czterech poleceń, sprawdzonych na pliku zaczynającym się od ef bb bf:

# W miejscu, GNU sed (Linux). Sekwencje ucieczki rozwija powłoka, nie sed.
sed -i $'1s/^\xEF\xBB\xBF//' data.json

# W miejscu, BSD sed (macOS)
sed -i '' $'1s/^\xEF\xBB\xBF//' data.json

# W miejscu, wszędzie tam, gdzie jest Perl. Tylko pierwszy wiersz.
perl -i -pe 's/^\x{ef}\x{bb}\x{bf}// if $. == 1' data.json

# Kopia bez pierwszych trzech bajtów. Bezpieczne tylko wtedy, gdy wiadomo, że BOM tam jest.
tail -c +4 data.json > clean.json

Wariant z tail działa na ślepo: usuwa trzy bajty niezależnie od tego, czy były BOM-em. Najpierw warto potwierdzić to sekcją 2.

7. Wyjątek CSV: kiedy Excel potrzebuje BOM-u

Wszystko powyżej traktuje BOM jako uszkodzenie. W jednym miejscu pełni jednak realną funkcję i jego usunięcie psuje działający plik.

Wyszukiwania frazy csv bom excel dzielą się na dwie przeciwstawne skargi, co dobrze pokazuje, że jedna reguła bywa stosowana w złą stronę:

  1. „Mój plik CSV otwiera się w Excelu z é i æ¥æ¬èª zamiast prawdziwych znaków”. Brakuje BOM-u.
  2. „Pierwsza kolumna nazywa się name i skrypt nie potrafi jej znaleźć”. BOM jest obecny.

Dlaczego Excel go potrzebuje

Excel na Windowsie nie ma pewnego sposobu, żeby rozpoznać, że plik CSV jest w UTF-8. Nie ma tu nagłówka, deklaracji ani metadanych: plik .csv to bajty. Bez sygnału Excel wraca do ustawień regionalnych systemu, czyli Windows-1252 w USA i Europie Zachodniej albo Windows-1251 w Rosji, przez co każdy znak spoza ASCII wychodzi źle. BOM jest właśnie tym sygnałem. Trzy bajty na początku i Excel czyta UTF-8 poprawnie.

W pliku CSV BOM jest więc funkcją, a nie usterką, i cała decyzja mieści się w jednym zdaniu:

Plik pisany do przetworzenia przez maszynę — usuń BOM. Plik pisany do dwukliku w Excelu przez człowieka — zostaw go.

Awaria po drugiej stronie

Ten sam plik podany parserowi sprawia, że BOM zlewa się z pierwszą komórką nagłówka. W 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 to undefined, podczas gdy klucz wypisuje się jako name w logach, w debuggerze i w console.table. To ta sama awaria co pythonowy KeyError z sekcji 5, więc zdanie „nazwa pola się zgadza, ale wartości brak” warto od razu traktować jako objaw BOM-u.

Nasze konwertery celowo obsługują obie strony. Konwerter CSV na JSON usuwa wiodący BOM z wejścia przed przetworzeniem, więc plik prosto z Excela daje name, a nie name. W drugą stronę konwerter JSON na CSV robi z BOM-u jawny przełącznik, a jego preset dla Excela włącza go razem ze średnikiem jako separatorem i końcami wierszy CRLF. Dokładnie takiej kombinacji potrzebują europejskie ustawienia regionalne Excela. Szerszy zestaw decyzji wokół separatorów, cudzysłowów i wnioskowania typów opisuje przewodnik po konwersji CSV i JSON.

8. Poza JSON-em: gdzie jeszcze pojawia się BOM

JSON przynajmniej robi wokół tego hałas. Pozostałe formaty psują się ciszej.

Skrypty powłoki. BOM siedzi między początkiem pliku a #!, więc jądro nigdy nie widzi shebanga i nigdy nie uruchamia wskazanego interpretera. Na macOS zmierzony wynik to powrót do sh i zgłoszenie wiersza shebanga jako brakującego pliku:

./bom.sh: line 1: #!/bin/sh: No such file or directory

Skrypt i tak się wykonał, tylko pod złym interpreterem, co jest gorsze niż zwykła porażka. Inne systemy formułują to inaczej, najczęściej jako błąd bad interpreter. Jeśli skrypt zaczynający się od całkowicie poprawnego #!/usr/bin/env python3 upiera się, że ta ścieżka nie istnieje, sprawdź bajty.

PHP. Wszystko poza <?php ... ?> jest wyjściem, a BOM przed znacznikiem otwierającym to trzy bajty wysłane, zanim kod w ogóle ruszy. Pierwsze wywołanie header(), session_start() albo setcookie() kończy się wtedy klasycznym ostrzeżeniem headers already sent. Ostrzeżenie wskazuje na wiersz 1 pliku, którego wiersz 1 wygląda na pusty.

Pliki .env i każdy format klucz-wartość. Mechanizm jest identyczny jak przy CSV: pierwsza zmienna to nie DATABASE_URL, tylko U+FEFF, a po nim DATABASE_URL, więc wyszukanie chybia, choć dla człowieka plik czyta się poprawnie. Każda kolejna zmienna działa, więc całość wygląda na problem z jednym konkretnym ustawieniem.

XML jest wyjątkiem w drugą stronę. Specyfikacja XML wprost dopuszcza BOM UTF-8 na początku dokumentu jako element automatycznego wykrywania kodowania, a parsery mają obowiązek sobie z nim poradzić. Pythonowy xml.etree.ElementTree przyjął w teście dokument z BOM-em bez błędu. Jeśli XML zawodzi, BOM raczej nie jest przyczyną.

9. Zatrzymanie problemu u źródła

Mechanizm jest już jasny, więc zostaje ostatnia część: jak sprawić, żeby plik nie dostał BOM-u z powrotem.

Ustal kodowanie w .editorconfig. Właściwość charset przyjmuje utf-8 i utf-8-bom jako osobne wartości, więc wskazanie tej właściwej jest jednoznaczne:

[*]
charset = utf-8

Sprawdź ustawienie edytora, które to nadpisuje. W VS Code jest to "files.encoding": "utf8", a szukana wartość to utf8bom. Zajrzyj do .vscode/settings.json w przestrzeni roboczej, nie tylko do ustawień użytkownika: ustawienie zapisane w repozytorium po cichu obowiązuje cały zespół.

Skanuj w CI albo w hooku pre-commit. Poniższy skrypt jest przenośny, nie ma zależności i kończy się kodem różnym od zera, gdy coś znajdzie:

#!/bin/sh
# Zwróć błąd, jeśli którykolwiek śledzony plik zaczyna się od 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

Sprawdzone w obie strony: wypisuje ścieżki plików z problemem i kończy się kodem 1, gdy śledzony jest plik z BOM-em, a kodem 0, kiedy pliki są już czyste.

Zapisz jedyny dozwolony wyjątek. Reguła „żadnego BOM-u nigdzie” łamie się przy pierwszej osobie, która potrzebuje eksportu do arkusza, a potem przestaje być traktowana poważnie w ogóle. Lepiej nazwać wyjątek wprost: BOM jest dozwolony w plikach CSV generowanych dla Excela i nigdzie indziej. Wystarczy wyłączyć katalog eksportu ze skanera, a reguła da się utrzymać.

10. Bisekcja w sześćdziesiąt sekund

Każdy z poniższych kroków albo kończy śledztwo, albo przekazuje następnemu mniejszy problem.

  1. Przeczytaj znak, nie pozycję. Sekcja 1. < oznacza HTML i na tym koniec. Brak czegokolwiek w cudzysłowie oznacza puste ciało odpowiedzi. Nieczytelny prostokąt oznacza: dalej.
  2. Potwierdź bajty. hexdump -C file | head -1. Jeśli pierwsze trzy bajty to nie ef bb bf, stop: to nie jest BOM i nic poniżej nie pomoże.
  3. Znajdź miejsce wejścia. Czy plik ma BOM już na dysku, czy jest na dysku czysty, a BOM pojawia się dopiero wtedy, gdy trzyma go kod? Czysty plik na dysku znaczy, że dokłada go coś w pipelinie.
  4. Wybierz jedną stronę do naprawy. Usuwaj BOM przy odczycie, kiedy producentem jest dostawca, upload albo krok builda spoza naszej kontroli. Napraw producenta, kiedy jest własny, bo poprawkę po stronie odczytu trzeba powtarzać w każdym miejscu odczytu.
  5. Zastosuj poprawkę na granicy dekodowania, nie głębiej. encoding='utf-8-sig' przy wywołaniu open(), a nie .lstrip() na ciągu trzy funkcje dalej. Poprawka wciśnięta głęboko w stos oznacza, że kolejna ścieżka kodu czytająca ten plik odkryje błąd od nowa.
  6. Sprawdź, czy bajty się zmieniły. Powtórz krok 2. Poprawka, która działa w jednej ścieżce kodu, a plik zostawiła nietknięty, zawiedzie w kolejnej.
  7. Dodaj skaner. Sekcja 9. Inaczej to wszystko powtórzy się w przyszłym kwartale.

FAQ

Czy BOM UTF-8 jest wymagany?

Nie. UTF-8 ma jedną kolejność bajtów, więc znacznik nie ma czego rozstrzygać. Unicode dopuszcza BOM UTF-8 jako sygnaturę kodowania, ale go nie zaleca, a JSON zakazuje go wprost: RFC 8259 stwierdza, że implementacje nie mogą dodawać znacznika kolejności bajtów do tekstu JSON.

Dlaczego plik wygląda dobrze w edytorze, a nie daje się przetworzyć?

Bo U+FEFF nie rysuje się w ogóle. Edytory, które go rozpoznają, ukrywają znak i wspominają o UTF-8 with BOM na pasku stanu. Edytory, które go nie rozpoznają, rysują po prostu zero pikseli. cat, less i diff w code review też wyglądają identycznie. Odsłania go dopiero widok na poziomie bajtów.

Czy JSON.parse kiedykolwiek usuwa BOM automatycznie?

Nigdy. JSON.parse przyjmuje ciąg znaków i traktuje U+FEFF jako znak nieoczekiwany, gdziekolwiek się pojawi. Usuwa go warstwa wyżej: res.json() po fetch, require() z Node dla plików .json oraz TextDecoder w ustawieniach domyślnych. Wszystkie trzy pozbywają się go, zanim parser cokolwiek zobaczy.

Czy usuwać BOM z plików CSV?

To zależy, kto otwiera plik. Każdy parser wchłonie BOM do nazwy pierwszej kolumny, więc name zmieni się w name, a każde wyszukanie chybi. Tam BOM trzeba usunąć. Excel na Windowsie używa BOM-u do wykrycia UTF-8, a bez niego kaleczy znaki diakrytyczne i CJK, więc tam trzeba go zostawić.

Czy BOM to to samo co spacja o zerowej szerokości?

Ten sam punkt kodowy, inna rola. U+FEFF na pozycji 0 to znacznik kolejności bajtów. W każdym innym miejscu dokumentu jest to ZERO WIDTH NO-BREAK SPACE — zastosowanie, które Unicode uznał za przestarzałe na rzecz U+2060 WORD JOINER. Stare teksty wciąż go zawierają i dlatego U+FEFF pojawia się w środku plików.

Czy BOM wpływa na diffy w git i na rozmiar pliku?

Trzy bajty na dysku i jeden hałaśliwy wiersz w każdym diffie, który go dotyka. Git porównuje bajty, więc dodanie albo usunięcie BOM-u przepisuje wiersz 1, nawet gdy wyświetlany tekst jest identyczny. Stąd bierze się ta jednowierszowa zmiana, której nikt na review nie potrafi wyjaśnić.

Tagi: utf-8 bom json csv debugging character-encoding

Powiązane artykuły

Zobacz wszystkie artykuły