Znění hlášky v jednotlivých parserech
Java hlásí Invalid byte 1 of 1-byte UTF-8 sequence (nebo byte 2 of 2-byte, byte 2 of 3-byte …), libxml2 podle verze Invalid bytes in character encoding nebo Input is not proper UTF-8, indicate encoding !, Python obecné not well-formed (invalid token) a .NET Invalid character in the given encoding:
Tabulka se týká parserů čtoucích původní bajty. Pokud soubor nejdřív přečteš jako text
s nahrazováním chybných bajtů znakem � a teprve potom ho předáš DOMParseru,
původní chybu kódování už tímto parserem nezjistíš.
Proč to vzniká
Ve Windows-1250 zabírá každý znak jeden bajt, v UTF-8 mají znaky s diakritikou dva nebo tři
bajty a musí dodržet přesný vzor. Bajt F8 (ř ve Windows-1250) v UTF-8 nesmí
stát nikde, proto Java hlásí hned první bajt. Bajt C8 (Č) sice smí zahájit
dvoubajtovou sekvenci, ale následující 65 (e) do ní nepatří — Java pak hlásí
Invalid byte 2 of 2-byte UTF-8 sequence.
| Znak | Windows-1250 | UTF-8 |
|---|---|---|
| ř | F8 | C5 99 |
| í | ED | C3 AD |
| Č | C8 | C4 8C |
| á | E1 | C3 A1 |
Soubory ve Windows-1250 dodnes vytvářejí starší účetní a skladové programy, exporty z Excelu
nebo ruční úpravy v editoru, který soubor uloží „v kódování systému“. Opačný případ — UTF-8
s deklarací windows-1250 — chybu nehlásí, jen místo „Jiří“ uvidíš „JiřÓ.
Ukázky
Soubor ve Windows-1250 s deklarací UTF-8
<?xml version="1.0" encoding="UTF-8"?>
<zakaznik>Jiří Černý</zakaznik> <?xml version="1.0" encoding="UTF-8"?>
<zakaznik>Jiří Černý</zakaznik> Obě verze vypadají v editoru stejně, liší se bajty. Opravená je uložená v UTF-8. Ukázka se nedá otevřít ve validátoru, protože do textového pole se vždy vloží už převedený text.
Znak, který v UTF-8 vypadá jako začátek sekvence
<?xml version="1.0" encoding="UTF-8"?>
<mesto>Česká Lípa</mesto> <?xml version="1.0" encoding="UTF-8"?>
<mesto>Česká Lípa</mesto> Jak kódování zjistit
-
Příkaz
file vstup.xml(Linux, macOS) vypíše odhad kódování. Soubor ve Windows-1250 obvykle označí jako ISO-8859 text, protože obě sady rozlišit neumí. -
Podívej se na bajty kolem pozice z hlášky (
xxd vstup.xmlnebo hexdump v prohlížeči). Jednotlivé bajtyE8,F8,9A,9Emezi písmeny ukazují na Windows-1250, dvojice začínajícíC3,C4neboC5na UTF-8. - VS Code ukazuje kódování vpravo dole ve stavovém řádku. Přes Reopen with Encoding soubor otevřeš jako Windows-1250 a přes Save with Encoding uložíš jako UTF-8.
Oprava: převést do UTF-8, nebo načíst se správným kódováním
Nejlepší oprava je převést soubor do UTF-8, které přečte každý parser. Pokud deklarace už
uvádí encoding="UTF-8", po převodu začne odpovídat skutečným bajtům.
Pokud uvádí Windows-1250, změň ji na UTF-8: samotný iconv text deklarace nepřepisuje.
iconv -f WINDOWS-1250 -t UTF-8 vstup.xml > vystup.xml Když soubor převést nemůžeš (přichází pravidelně od dodavatele), dekóduj ho v programu sám. Rozlišuj bajty a už dekódované znaky. Java Reader předává parseru znaky a má přednost před encoding v deklaraci. PHP string ale obsahuje bajty: ukázka je převádí do UTF-8 a předpokládá, že deklarace uvádí UTF-8 nebo chybí. Pokud uvádí jiné kódování, uprav ji také.
$raw = file_get_contents('vstup.xml');
$xml = simplexml_load_string(iconv('WINDOWS-1250', 'UTF-8', $raw));
// mb_convert_encoding() Windows-1250 nezná (ValueError), proto iconv # Soubor ve Windows-1250 s chybnou deklarací UTF-8: dekóduj ho sám
with open("vstup.xml", encoding="cp1250") as f:
root = ET.fromstring(f.read()) // Reader s daným kódováním má přednost před deklarací v souboru
try (Reader reader = new InputStreamReader(Files.newInputStream(path), "windows-1250")) {
Document doc = builder.parse(new InputSource(reader));
} // .NET (Core, 5 a novější) zná bez registrace jen Unicode, ASCII a Latin-1;
// ostatní kódové stránky zpřístupní CodePagesEncodingProvider
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
using var reader = new StreamReader("vstup.xml", Encoding.GetEncoding(1250));
var doc = XDocument.Load(reader);
Třetí možnost je opravit deklaraci na encoding="windows-1250". Ne každý parser
ale Windows-1250 umí:
| Parser | encoding="windows-1250" |
|---|---|
| Java (Xerces) | funguje |
| Python (expat) | funguje, převod zajistí Python |
| PHP, xmllint (libxml2 s iconv) | funguje |
| .NET (ověřeno na .NET 10) | System does not support 'windows-1250' encoding — dokud se nezaregistruje CodePagesEncodingProvider |
| Validátor na XML.cz (libxml2 ve WebAssembly) | Unsupported encoding: windows-1250 — soubor převeď do UTF-8 |
Související
- Kódování znaků a entity v kapitole Jak funguje XML.
- Content is not allowed in prolog — BOM na začátku souboru.
- XML import do Pohody — kódování importních souborů.
- Přehled všech chybových hlášek.