Dlaczego „zażółć“ przychodzi jako krzaki

« Blog · Autor:

Wiadomość zostaje wysłana i dociera jako zażółć. Albo jako za???. Albo z małymi prostokątami w miejscach, gdzie powinny stać polskie litery. Trzy różne obrazy, trzy różne przyczyny — i tylko jedną z nich da się jeszcze naprawić, gdy wiadomość już leży u odbiorcy.

Co właściwie ustala zestaw znaków

Po łączu płyną bajty, czyli liczby od 0 do 255. To, jaki znak przedstawia dany ciąg liczb, nie wynika z niego samego — stoi w umowie, którą trzeba zakomunikować osobno.

W wiadomości pocztowej ta umowa stoi w Content-Type, i to osobno w każdej części wiadomości:

Content-Type: text/plain; charset=UTF-8

Gdy deklaracji brakuje, program odbierający zgaduje. A zgaduje różnie, zależnie od pochodzenia i ustawień domyślnych.

Skąd bierze się Å

ż zakodowane jako UTF-8 C5 BC odczytane jako UTF-8 ż odczytane jako Windows-1252 ż Te same dwa bajty. Różni się wyłącznie założenie, co one znaczą.
Z jednego znaku robią się dwa. To jest cecha rozpoznawcza: wersja zniekształcona jest zawsze dłuższa od oryginału.

UTF-8 przedstawia ż jako dwa bajty, C5 BC. Program biorący te same bajty za Windows-1252 — starszy zestaw znaków z dokładnie jednym znakiem na bajt — odczytuje z nich dwa znaki: Å i ¼.

Według tego schematu powstaje cała znajoma rodzina:

ZamierzoneBajtyOdczytane błędnie
ąC4 85Ä…
ćC4 87ć
ęC4 99Ä™
łC5 82Å‚
żC5 BCż
óC3 B3ó

Wiodące Ä i Å nie są przypadkiem: polskie znaki diakrytyczne zaczynają się w UTF-8 od bajtu C4 albo C5, a te w Windows-1252 to właśnie Ä i Å. Widok takiego ciągu od razu mówi, czego szukać.

Znaki zapytania i prostokąty to co innego

Trzy objawy z pierwszego akapitu nie są trzema odcieniami tego samego błędu.

  • ż — odczytane błędnie. Bajty są nienaruszone, błędna jest tylko interpretacja. Da się to cofnąć: po ustawieniu właściwego zestawu znaków tekst wraca.
  • ? — utracone. Tekst przekonwertowano na zestaw, który tego znaku nie zna. Z ż zrobił się znak zapytania, a ze znaku zapytania nie da się już niczego odtworzyć. To jedyny przypadek nieodwracalny.
  • Prostokąty albo romby. Zestaw znaków i bajty się zgadzają, ale użyty krój pisma nie zawiera rysunku tego znaku. Tekst jest nieuszkodzony i da się go skopiować; zawodzi wyłącznie wyświetlanie.

To rozróżnienie decyduje o tym, czy warto poprosić o oryginał jeszcze raz, czy nie.

Nagłówek ma własne reguły

Temat, nazwa wyświetlana i nazwy plików załączników stoją w nagłówku wiadomości, a ten według pierwotnego standardu może zawierać wyłącznie ASCII. Dlatego istnieje tam osobne przepisanie:

Subject: =?UTF-8?Q?Zam=C3=B3wienie?=

To poprawnie zakodowany temat „Zamówienie": deklaracja zestawu znaków, metoda, treść. Program, który to rozumie, pokazuje tekst. Jeśli odbiorca widzi taki ciąg otwartym tekstem, system wysyłający wybrał złą kolejność — zakodował tam, gdzie nie należało, albo odwrotnie.

To kodowanie jest niezależne od charset treści wiadomości. Stąd częsty przypadek, że temat jest zniekształcony, a treść w porządku, albo na odwrót. To dwa osobne place budowy. Jak wiadomość dzieli się na części w całości, opisuje artykuł o MIME.

Zakodowane podwójnie

Najbardziej uporczywy jest przypadek, w którym tekst już zniekształcony zostaje przekonwertowany po raz drugi. Z ż robi się wtedy ż — ciąg rośnie z każdym przebiegiem.

Zdarza się to zwykle w łańcuchach kilku systemów: formularz dostarcza UTF-8, baza danych jest ustawiona na latin1, program wysyłkowy zakłada UTF-8. Każda stacja robi po swojemu rzecz właściwą, a na końcu wychodzi bezsens.

Naprawić da się to wyłącznie idąc łańcuchem wstecz i znajdując miejsce, w którym po raz pierwszy zinterpretowano błędnie. Wszystko po nim jest już następstwem.

Reguły, które problem likwidują

  1. Wszędzie UTF-8, bez wyjątku. Formularz, aplikacja, baza danych, wysyłka, wiadomość. Jeden jedyny punkt z innym ustawieniem wystarcza na cały efekt.
  2. Zestaw znaków deklarować, nie zakładać. charset=UTF-8 należy do każdej części tekstowej. Bez deklaracji odbiorca zgaduje, a program, który przez lata zgadywał trafnie, kiedyś zgadnie inaczej.
  3. Nagłówki zostaw bibliotece. Składanie zakodowanych słów własnoręcznie jest podatne na błędy, a każda poważna biblioteka wysyłkowa robi to poprawnie.
  4. Przy bazie danych pilnuj kodowania połączenia, nie tylko tabeli. Kolumna utf8mb4 nic nie da, gdy połączenie stoi na latin1 — dokładnie tam rodzi się podwójne kodowanie.
  5. Testuj prawdziwymi znakami. Przebieg testowy ze słowem „test" nie dowodzi niczego. Przebieg ze zdaniem „zażółć gęślą jaźń, Grüße, Ελλάδα" dowodzi bardzo wiele.

Ostatni punkt jest najtańszy i najpewniej zapominany. Błąd ujawnia się wyłącznie na znakach, których w danych testowych zwykle nie ma — a potem u klienta, który akurat nazywa się Żółtowski.