Mittwoch, 20. Dezember 2017

Encoding & Charset in HTML/PHP/... mit notepad++

Das Thema Encoding und Charset bei HTML und PHP ist ein etwas kompliziertes, da die Definition der beiden Begriffe in der Verwendung der Begriffe nicht immer Beachtung findet und es Überschneidungen gibt. Das Thema betrifft auch Text Dateien, CSS Dateien, .htaccess Dateien, Javascript und sonstige Script Dateien sowie Computer-Dokumente im Allgemeinen.

Der Begriff Encoding meint eine Enkodierung eines Dokuments. Ein Dokument kann hier ein HTML oder PHP Dokument sein, dass die File Extension .html oder .php hat. Das Encoding wird beim Abspeichern eines Dokuments vorgenommen. Das Decoding wird beim Abrufen des Dokuments vorgenommen.

Der Begriff Charset meint den character set (Zeichensatz), der in einem HTML oder PHP Dokument verwendet wird. Das Charset dient zur Attribution (Zuschreibung von Eigenschaften) eines Dokuments. Mit der Charset Attribution wird angegeben, welches character set verwendet wird. Die Charset Attribution gibt Rückschlüsse auf das verwendete Encoding.

Die Namen der Kodierungs-Codes und der zugehörigen Charset Attributions sind grob gesehen gleich. Aus diesem Grund werden bei der Verwendung der Namen die beiden Begriffe Encoding und Charset von ihrer eigentlichen Definition gelöst und als allgemeine Begriffe verwendet.


Verwendet wurden folgende Programme:
npp.7.5.1.bin.x64 (portable)
WinSCP 5.11.2 (portable)
- Internal Editor
- npp.7.5.1.bin.minimalist.x64 (portable)
Hinweis: notepad++ 7.5.2 und 7.5.3 hat Probleme mit dem Encoding.


Reine Text-Dokumente wie HTML, PHP, TXT, CSS, JS, JSON und so weiter haben keinen File Header, in dem das Encoding angegeben ist. Deshalb wird das Encoding als Attribution im Dokument mitgeliefert und aus dieser Angabe ein File Header erzeugt.
Im folgenden einige Beispiele von File Headern anderer Dateien.
Mit notepad++ kann der File Header von Dateien angezeigt werden.

Code: Plugins - MIME Tools - Quoted-printable Encode
HEX: Plugins - Converter - ASCII -> HEX

JPEG File Interchange Format (JFIF)
Code: =FF=D8=FF=E0 =10JFIF
HEX: 3D46463D44383D46463D4530203D31304A464946

Portable Network Graphics (PNG)
Code: =89PNG
HEX: 89504E47

Portable Document Format (PDF)
Code: %PDF
HEX: 25504446

MP3 (MPEG-1 Audio Layer III / MPEG-2 Audio Layer III, Moving Picture Experts Group)
Code: ID3
HEX: 494433

Bei UTF-8-BOM, UTF-16-BOM und UTF-32-BOM wird ähnlich wie bei JPEG, PDF, PNG, MP3, ... am Anfang der Datei eine Markierung gesetzt - das sogenannte Byte Order Mark (BOM) - woran das Encoding erkannt werden kann. Allerdings zeigen einige Programme dieses Byte Order Mark mit an, zB auf der Webseite, was unerwünscht ist.

Encoding BOM in hexadecimal BOM in decimal BOM in CP1252
UTF-8 EF BB BF 239 187 191 
UTF-16 (BE) FE FF 254 255 þÿ
UTF-16 (LE) FF FE 255 254 ÿþ
UTF-32 (BE) 00 00 FE FF 0 0 254 255 ␀␀þÿ
UTF-32 (LE) FF FE 00 00 255 254 0 0 ÿþ␀␀
BE = Big Endian, LE = Little Endian

Die Kodierung eines HTML, PHP, TXT und ähnlichen Dokumentes wird durch das Charset Attribut übermittelt.

Encoding = Verwendetes Encoding beim Speichern des Dokuments.
Charset = Angabe des verwendeten Encodings.

Wenn die Kodierung eines Dokuments von dem Charset Attribut abweicht, kommt es zu Anzeigefehlern der characters (Zeichen). Einige Tools können die Kodierung eines Dokuments an den im Dokument verwendeten characters erkennen. Allerdings ist dazu eine Mindestanzahl von characters notwendig, da ansonsten falsche Ergebnisse wie "ERROR: (null)", "unknown-8bit" oder "binary" angezeigt werden können.

Nicht alle Encodings werden von einer Charset Attribution unterstützt. Welche Endcodings eine Charset Attribution bekommen legt die Internet Assigned Numbers Authority (IANA) fest. Einige Encodings haben ein gemeinsames Charset Attribute.

Encoding-codes (kleine Auswahl):
ASCII UTF-8 UTF-16 UTF-32 CSUNICODE UNICODEBIG UNICODELITTLE UCS-2LE JAVA ISO-8859-1 ISO-8859-2 ISO-8859-3 ISO-8859-4 ISO-8859-5 ISO-8859-6 ISO-8859-7 ISO-8859-8 ISO-8859-9 ISO-8859-10 ISO-8859-11 ISO-8859-13 ISO-8859-14 ISO-8859-15 ISO-8859-16 KOI8-R KOI8-U KOI8-RU WINDOWS-1250 (CP1250) WINDOWS-1251 (CP1251) WINDOWS-1252 (CP1252) WINDOWS-1253 (CP1253) WINDOWS-1254 (CP1254) WINDOWS-1255 (CP1255) WINDOWS-1256 (CP1256) WINDOWS-1257 (CP1257) WINDOWS-1258 (CP1258) CSPC862LATINHEBREW MACINTOSH MACCENTRALEUROPE MACICELAND MACCROATIAN MACROMANIA MACCYRILLIC MACUKRAINE MACGREEK MACTURKISH MACHEBREW MACARABIC MACTHAI ATARI

Charset Attributes (kleine Auswahl):
EUC-JP US-ASCII UTF-8 UTF-16 UTF-32 ISO-8859-1 ISO-8859-2 ISO-8859-3 ISO-8859-4 ISO-8859-5 ISO-8859-6 ISO-8859-7 ISO-8859-8 ISO-8859-9 ISO-8859-10 WINDOWS-1250 WINDOWS-1251 WINDOWS-1252 WINDOWS-1253 WINDOWS-1254 WINDOWS-1255 WINDOWS-1256 WINDOWS-1257 WINDOWS-1258 Windows-31J IBM-Symbols Big5 macintosh

Das Charset Attribute "US-ASCII" (ASCII) gilt für diese Encodings (Aliases):
iso-ir-6 ANSI_X3.4-1968 ANSI_X3.4-1986 ISO_646.irv:1991 ISO646-US US-ASCII us IBM367 cp367 csASCII

Die Charset Attribution eines Encodings kann an verschiedenen Stellen vorgenommen werden.

HTML header meta tag (HTML4)
<meta http-equiv="content-type" content="text/html; charset=utf-8">

HTML header meta tag (HTML5)
<meta charset="UTF-8">
- Die Angabe muss bei HTML5 innerhalb der ersten 1024 Bytes des Dokuments stehen.
- Die Angabe des Mime Types (text/html) als meta tag wurde gestrichen.
Beispiel HTML5 Header:
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
Info: Ein schließender Slash "/" (<meta charset="UTF-8" />) ist nur in XHTML notwendig.

XHTML5 header (.xhtml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<head>
<meta charset="UTF-8" />
- Ein .xhtml Dokument wird als XML Dokument gekennzeichnet.
- Die Angabe eines meta tag in <head> ist nicht notwendig.
- Die erste Zeile muss als erste Zeile im Dokument stehen.
"XML declaration allowed only at the start of the document"
- Bei XHTML müssen innerhalb des <html> tags alle HTML Tags mit einem Slash geschlossen werden "<meta ... />", <img ... />, ... , sowie wie in HTML </head>, </span>, </p>, </body>, </html>.

XHTML5 und PHP (.php)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<?php header("Content-type: text/html; charset=UTF-8"); ?>
- Bei XHTML5 mit PHP muss die file extension .php gesetzt werden.
- Die Angabe eines meta tag in <head> ist nicht notwendig.
- Bei XHTML müssen innerhalb des <html> tags alle HTML Tags mit einem Slash geschlossen werden "<meta ... />", <img ... />, ... , sowie wie in HTML </head>, </span>, </p>, </body>, </html>.

PHP header (.php)
header("Content-type: text/html; charset=UTF-8");
- erste Zeile nach <?php.
- wenn im Dokument PHP mit HTML vermischt ist, dann die file extension .php vergeben und den PHP header noch vor den HTML header setzen.
Beispiel (.php):
<?php header("Content-type: text/html; charset=UTF-8"); ?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
- Die Mime Types "application/x-httpd-php" und "application/x-httpd-php-source" veranlassen, dass der PHP Code in einem .php Dokument bei einem Aufruf im Browser nicht ausgeführt wird, sondern das Dokument zum Download angeboten wird. Bei einem .phtml Dokument wird mit diesen Mime Types der PHP Code nicht ausgeführt und im Quellcode angezeigt.

CSS header (.css)
@charset "UTF-8";
- Erste Zeile in der CSS Datei

Javascript (.js)
<script src="javascript.js" charset="UTF-8"></script>
- bei Javascripts ist eine Attribution nicht direkt im Dokument möglich.

.htaccess mod_mime
<IfModule mod_mime.c>
AddType text/html .html .phpAddType text/css .css
AddType application/javascript .js
AddType application/xhtml+xml .xhtml .xml
AddCharset UTF-8 .html .php .css .js .xhtml .xml
</IfModule>
- Der Mime Type "text/javascript" ist veraltet.
- Eine .htaccess Datei in UTF-8-BOM Encoding funktioniert nicht.

Wichtig ist, dass das Charset Attribut mit dem Encoding des Dokuments überein stimmt.

MIME TYPES Liste
Die komplette Liste aller von der IANA empfohlenen mime types:
http://www.iana.org/assignments/media-types/media-types.xhtml

Mit folgenden Linux Tools auf der Kommandozeile kann das Encoding und Charset Attribute ausgelesen werden.
Allerdings haben die Tools so ihre Eigenarten. Für das Tool file muss eine Mindestanzahl an Zeichen im Dokument vorhanden sein. Zudem zeigt file bei einem Dokument, das in einem Superset (zB UTF-8) gespeichert wurde und nur Zeichen eines Subsets (ASCII) des Supersets (UTF-8) beinhaltet, das Subset (ASCII) an.

curl
[user@server ]$ curl -I https://www.example.com/example.php
Content-Type: text/html; charset=UTF-8

HEAD
[user@server ]$ HEAD https://www.example.com/example.html
Content-Type: text/html; charset=utf-8

file
[user@server ]$ file -b --mime-type --mime-encoding /var/www/html/www.example.com/example.php
text/x-php; charset=us-ascii
- file ignoriert die Angaben von Mime Type und Charset und ermittelt beides selbst neu.
- der Mime Type "x-php" ist kein offizieller Standard und wird nur von file zur Anzeige des selbst ermittelten Mime Types genutzt. Es ist möglich, dass dieser irgendwann einmal Standard werden kann, wenn er sich durchsetzt, ansonsten nicht.

wget
[user@server ]$ wget --spider --server-response https://www.example.com/example.php
Content-Type: text/html; charset=UTF-8

Was zu Problemen, Anzeigefehlern und Missverständnissen führen kann ist, dass der beim speichern eines Dokuments verwendete Encoding-Code beim encoden (speichern) nicht in den File Header geschrieben wird. Jeder Encoding-Code hat gewisse Eigenheiten, an denen er eventuell erkannt werden kann. Allerdings ist das nicht zuverlässig. Deswegen soll ein Charset Attribut im Dokument angegeben werden. Dokumente haben so gesehen keinen echten File-Header, sondern der Dokumenten-Header wird als Plain-Text in das Dokument geschrieben, oder außerhalb des Dokuments angegeben. Bei einem Abruf des Dokuments wird ein Header erzeugt und das Charset Attribut darin aufgenommen.

Das Encoding findet immer auf Mindestebene statt. Deswegen sind Dokumente, die zwar in UTF-8 encoded wurden, aber nur ASCII Zeichen enthalten in ASCII encoded, da im Dokument keine Zeichen vorhanden sind, die ein UTF-8 Encoding erfordern. Hierbei spielen die Begriffe Subset und Superset eine Rolle. Das Dokument kann aber mit dem Charset Attribut UTF-8 versehen werden. Allerdings kann dieser Subset-Fallback Umstand zu Problemen führen. Editor-Tools wie notepad++ und ähnlich lesen keine Charset Attribute aus. Diese Tools erkennen das verwendete Encoding an der Kodierung der verwendeten Zeichen. Wenn nur ASCII Zeichen im Dokument vorkommen, dann wird das Dokument als in ASCII encoded geöffnet. Wenn aber das Charset Attribute UTF-8 ist und dies den Programmierer dazu veranlasst auch UTF-8 Zeichen im Dokument zu verwenden, das Dokument aber am Ende nur als ASCII encoded wird, wird es beim nächsten Öffnen des Dokuments einen Error geben.

Im Beispiel der Inhalt eines neuen Dokuments mit Umlauten und Eszett, das in notepad++ mit ANSI (ASCII) Encoding gespeichert wurde, danach geschlossen und wieder geöffnet wurde.
vorher: äöüß
HEX: C3A4C3B6C3BCC39F
nachher: הצ
HEX: D794D7A6EFA295EFA293

Ein wiederherstellen ist nicht möglich.

Der WinSCP interne Editor bietet ein Fallback auf "1252 (ANSI - Lateinisch I)", wenn ein Dokument als UTF-8 geöffnet werden soll, aber enthaltene Zeichen exklusive Windows-1252 characters sind. Das passiert, wenn ein Dokument mit Windows-1252 characters mit Encoding Windows-1252 gespeichert wurde und in den WinSCP Preferences für den Editor als Default UTF-8 gesetzt wurde. Der WinSCP interne Editor versucht also das Dokument im Default UTF-8 encoding zu öffnen und benachrichtigt mit "Error loading file", dass das Dokument nicht in UFT-8 encoding geöffnet werden kann, (weil Nicht-UTF-8 characters darin vorkommen, die nicht mit UTF-8 encoding gespeichert wurden).
Encoding und Charset in HTML PHP WinSCP editor

Allerdings bringt WinSCP in seinem Editor die Begriffe durcheinander. Mit "1252 (ANSI - Lateinisch I)" ist entweder Windows-1252 gemeint, oder ISO-8859-1 (Lateinisch I) und das "ANSI" wurde zur endgültigen Verwirrung noch hinzugefügt. Es werden also 2 Encoding-Codes mit einem Überbegriff mit einmal erwähnt. Dazu muss erwähnt werden, dass sogar auf seriösen Webseiten unter dem Begriff "charset" Windows-1252 als ANSI bezeichnet wird, wohingegen ISO-8859-1 als "Lateinisch I" gilt.
Vom character code point 0 bis 255 sind die characters im charset ISO-8859-1 und charset UTF-8 gleich. Windows-1252 enthält im code point range 0 bis 255 die gleichen characters wie ISO-8859-1 und UTF-8 plus zusätzliche characters (€  ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ  Ž   ‘ ’ “ ” • – — ˜ ™ š › œ  ž Ÿ). Das charset UTF-8 hat ab code point 256 noch sehr viele characters mehr.
Ein Test hat ergeben, dass im WinSCP internen Editor mit "1252 (ANSI - Lateinisch I)" das Encoding Windows-1252 (CP1252) gemeint ist. Windows-1252 ist ein Superset von den charsets ASCII und ISO-8859-1 sowie innerhalb der code points 0 bis 255 auch vom charset UTF-8. Der WinSCP interne Editor zeigt Windows-1252 control points als Leerzeichen an (Beispiel: Alt+ 0129 in Microsoft Windows).

Die Begriffe ASCII, ANSI, Windows-1252 (CodePage 1252, CP1252), ISO-8859-1 (Windows Latin 1) und UTF-8 werden auch in anderen Programmen nicht immer eindeutig verwendet. Im Editor-Tool notepad++ verbirgt sich unter dem Menü-Punkt Encoding bei "Encode in ANSI" das Windows-1252 encoding in einfacher Form. Control characters werden nicht angezeigt. Erst wenn über den Untermenü-Punkt Character set - Western Europe - Windows-1252 ausgewählt wird, erscheinen die Control characters mit ihrem Namen in einem schwarzen Kästchen (Beispiel: Alt+ 0129 in Microsoft Windows).
Nun gibt es in den WinSCP Preferences unter den Einstellungen für New Document die Möglichkeit für ein Fallback "Apply to opened ANSI files". Wenn an dieser Stelle "ANSI" das Windows-1252 encoding/charset meinen würde, dann würde dies mit einem Dokument, das die characters enthält, die Windows-1252 im code point range 0 bis 255 zusätzlich zu UTF-8 enthält, nicht funktionieren. Also ist an dieser Stelle mit ANSI maximal ISO-8859-1 gemeint. Die Angelegenheit kann in notepad++ überprüft werden.
In notepad++ kann das euro sign mit dem shortcut ALT+ 0129 in Microsoft Windows jederzeit geschrieben werden. Danach kann es in verschiedenen Encoding-Codes gespeichert werden. Notepad++ zeigt nach dem erneuten öffnen des Dokuments "Encoded in ANSI" an, was nicht gleich Windows-1252 bedeuten muss, aber zumindest hat kein "Apply to opened ANSI files" (in UTF-8) stattgefunden. In ISO-8859-1 gibt es dieses Zeichen nicht. Der hexadecimale Code verrät das Encoding.
Ein Beispiel mit dem euro sign (Euro Zeichen, Alt+ 0128 in Microsoft Windows).
Das Zeichen markieren, dann
Menüleiste: Plugins - Mime Tools - Quoted-printable Encode

UTF-8
Plain: €
HEX: =E2=82=AC

Windows-1252
Plain: €
HEX: =80

Im Test wurde "=80" angezeigt, was bedeutet, dass das "ANSI" in "Apply to opened ANSI files" nur ISO-8859-1 und kein Windows-1252 meint und das "ANSI" in "Encoded in ANSI" auch Windows-1252 meint.
Hiermit sollte klar sein, dass an vielen Stellen mit ANSI nicht gleich ANSI gemeint ist.

Mit ANSI (American National Standards Institute) als Encoding oder Charset kann gemeint sein:
ASCII -  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
abcdefghijklmnopqrstuvwxyz{|}~
Windows-1252 (CodePage 1252, CP1252) - Superset von ASCII, Teilset von ISO-8859-1.
ISO-8859-1 - Superset von ASCII, Teilset von Windows-1252.

UTF-8 ist ein Superset von ASCII. ASCII ist ein Subset von UTF-8.
Mit dem Begriff "UTF-8" kann folgendes gemeint sein:
- ein Encoding-Code
- ein Charset Attribute
- ein Charset innerhalb der Code Points 0 bis 10FFFF im UNICODE.


Mit notepad++ können Dokumente Enkodiert (Encode) und das Encoding konvertiert (Convert) werden. Im Unter-Menü "Character sets" sind weitere Encoding-Codes zu finden. 
Encoding und Charset in HTML PHP notepad++

In notepad++ kann auch ein Fallback von ASCII und ISO-8859-1 (ANSI) auf UTF-8 eingeschaltet werden. Das bedeutet, dass als ASCII oder und ISO-8859-1 encoded Dokumente als in UTF-8 encoded geöffnet werden, also das Subset ASCII und und ISO-8859-1 mit Superset UTF-8 geöffnet wird ("Apply to opened ANSI files"). Das soll verhindern, dass Dokumente, die auch UTF-8 charcters enthalten sollen dürfen und das charset Attribute UTF-8 haben, aber nur ASCII oder ISO-8859-1 charcters enthalten, nicht als "Encoded in ANSI", sondern als "Encoded in UTF-8" geöffnet werden, damit sie auch wieder in UTF-8 encoding gespeichert werden und nicht in ANSI, was das Dokument zerstören könnte. Mit dem Begriff "ANSI" ist an dieser Stelle nicht Windows-1252 gemeint.
Encoding und Charset in HTML PHP notepad++


Keine Garantie. Keine Gewähr. Kein Support.
Benutzung auf eigenes Risiko.