How to get rid of these HTML-entities in Files

It has been written here that HTML-entities (these ä etc) should be avoided with the exception of those that we need due to the HTML-syntax like <, >, & and maybe " and  . They were already mostly obsolete more than 20 years ago, but in those days we still did not automatically use UTF-8 or UTF-16, but often an 8-bit character encoding that could express only up to 256 characters, in reality around 200 due to control characters. At least these 200 could be used. That was enough for web pages in those days and texts in German, French, Russian, Greek, Hebrew, Arabic and many other language could well be written, as long as only one language or a few similar languages were used. For the rare occasions that required some characters that were not in this character set, it was an option to rely on these HTML-entities. Or for typing HTML-pages on an US-keyboard without any good tool support.

But now Unicode has been around for more than 25 years and more than 90% of the web pages use UTF-8.

Now some people think that these HTML-entities are kind of necessary or at least „safer“ and I see people still writing HTML-code with them in these days. Or tools by relatively well known companies, that produced such output not so long ago… It is a good thing to have some courage and to change something like this to readable and natural format. Or more generally to try out if a simpler or better solution works. Reasonable courage is good for this, too much of something good can go bad, as so often…

So, please teach your collegues not to use these ugly HTML-entities, where UTF-8-characters are the better option.

And here is a perl script that converts the HTML-entities with the exceptions mentioned above to UTF-8. In the project conversion-utils some more such scripts might be added. The script is a bit too long to be pasted inline in a code block, so it is better to find the current version on github.

Then you can do something like this:

git commit
for file in *.html ; do
echo $file
mv $file ${file}~entities~
html2utf8 < ${file}~entities~ > $file
echo /$file
done
git diff

to convert all files in a directory. I assume that you are using Linux or at least have bash like for example in cygwin.
There are other tools to do the same thing, I am sure. Just use anything that works for you to get away from this unreadable crap.

Share Button

Unicode and C

It is a common practice in C to use arrays of char as strings. The 0 is used as end marker.

The whole thing was created like that in the 1970s and at that time it was kind of cool to get away with one less language feature and to express it in terms of others instead. And people did not think enough about the necessity to express more than ISO 646 IRV (commonly called ASCII) as string content.

This extended out of the box to 8 bit character sets like ISO 8859-1 or KOI-8, that are identical to ISO 646 in the lower 128 characters and contain an extension in the upper 128 characters. But fortunately we have moved ahead and now Unicode with its encodings UTF-8, UTF-16 and UTF-32.

How can we deal with this in C?

UTF-8 just works out of the box, because the byte 0 is only used to encode the code point U+0000. So the null termination can be kept as it is and a lot of functionality remains valid. Some issues arise, because in UTF-8 things like finding the logical length of a string, not its memory consumption or finding the nth code point, not the nth byte, require UTF-8-logic to be applied and to parse the whole string at least from the beginning to the desired position or the usage of an indexing facility. So a lot of non-trivial string functionality of the standard library will just not be as easy as people thought it would be in the 1970s and subsequently not work as needed. Libraries for better UTF-8-support in C can be found, leaving the „native“ C strings with UTF-8 content only for usage in interfaces that require them. I have not yet explored such libraries, but it would be interesting to find out how powerful and useful they are.

At the time when Unicode came out, it seemed to be sufficient to have 16 bits per character instead of 8. Java was built on this assumption. C added a wchar_t to allow for this and just required it to be „long enough“. So Linux uses 32 bits and MS-Windows 16 bits. This is not too bad, because programming in C for MS-Windows and for Linux is anyway quite different, unless we abstract the differences into a library, which would then also include a common string definition and string handling functionality. While the Linux wchar_t is sufficient, it really wastes a lot of memory, which is often undesirable, if we go the extra effort to program in C in order to gain performance. The Windows-wchar_t is „kind of sufficient“, as are the Java-Strings, because we can really do a lot with assuming that Unicode is only 16 bit or with UTF-16 and ignoring the complexities of that, that are in principal the same as for UTF-8, but can be ignored with less disadvantages most of the time. The good news is, that wchar_t is well supported by standard library functions.

Another way is to use char16_t and char32_t, that have a clear definition of their length, but much less library support.

Probably these facilities are sufficient for software whose string handling is relatively trivial. For more ambitious string handling in terms of functionality and performance, it will be necessary to find third party libraries or to write them.

Links

Share Button

&auml; &ouml; … in HTML

In the old days of the web, more than 20 years ago, we found a possibility to write German Umlaut letters and a lot of other letters and symbols using pure ASCII. These are called „entities„, btw.

Many people, including myself, started writing web pages using these transcriptions, in the assumption that they were required. Actually in the early days of the web there were some rumors that some browsers actually did not understand the character encodings that contained these letters properly, which was kind of plausible, because the late 80s and the 90s were the transition period where people discovered that computers are useful outside of the United States and at least for non-IT-guys it was or it should have been a natural requirement that computers understand their language or at least can process, store and transmit texts using the proper characters of the language. In case of German language this was not so terrible, because there were transcriptions for the special characters (ä->ae, ö->oe, ü->ue, ß->ss) that were somewhat ugly, but widely understandable to native German speakers. Other languages like Russian, Greek, Arabic or East-Asian languages were in more trouble, because they consist of „special characters“ only.

Anyway, this „&auml;“-transcription for web pages, which is actually superior to the „ae“, because the reader of the web page will read the correct „ä“, was part of the HTML-standard to support writing characters not on the keyboard. This was a useful feature in those days, but today we can find web pages that help use with the transliteration or just look up the word with the special characters in the web in order to write it correctly. Then we can as well copy it into our HTML-code, including all the special characters.

There could be some argument about UTF-8 vs. UTF-16 vs. ISO-8859-x as possible encodings of the web page. But in the area of the web this was never really an issue, because the web pages have headers that should be present and inform the browser about the encoding. Now I recommend to use UTF-8 as the default, because that includes all the potential characters that we might want to use sporadically. And then the Umlaut kann just directly be part of the HTML content. I converted all my web pages to use Umlaut-letters properly, where needed, without using entities in the mid 90s.

Some entities are still useful:

  • „&lt;“ for „<“, because „<“ is used as part of the HTML-syntax
  • „&amp;“ for „&“ for the same reason
  • „&gt;“ for „>“ for consistency with „<„
  • „&nbsp;“ for no-break-space, to emphasize it, since most editors make it hard to tell the difference between regular space and no-break-space.
Share Button

Unicode, UTF-8, UTF-16, ISO-8859-1: Why is it so difficult?

Deutsch

Since about 20 years we have been kept busy with the change to Unicode.

Why is it so difficult?

The most important problem is that it is hard to tell how the content of a file is to be interpreted. We do have some hacks that often allow recognizing this:
The suffix is helpful for common and well defined file types, for example .jpg or .png. In other cases the content of the file is analyzed and something like the following is found in the beginning of the file:

#!/usr/bin/ruby

From this it can be deduced that the file should be executed with ruby, more precisely with the ruby implementation that is found under /usr/bin/ruby. If it should be the ruby that comes first in the path, something like

#!/usr/bin/env ruby

could be used instead. When using MS-Windows, this works as well, when using cygwin and cygwin’s Ruby, but not with native Win32-Ruby or Win64-Ruby.

The next thing is quite annoying. Which encoding is used for the file? It can be a useful agreement to assume UTF-8 or ISO-8859-1 or ISO-8859-15, but as soon as one team member forgets to configure the editor appropriately, a mess can be expected, because files appear that mix UTF-8 and ISO-8859-1 or other encodings, leading to obscure errors that are often hard to find and hard to fix.

Maybe it was a mistake when C and Unix and libc were defined and developed to understand files just as byte sequences without any meta information about the content. This more or less defined how current operating systems like Linux and MS-Windows deal with it.

They did not really have a choice, although they did both provide facilities that could theoretically be used to carry such information, but since almost no software uses them and there are no well known and established standards, this is not helpful today. In the internet mime headers have proved to be useful for email and web pages and some other content. This allows the recipient of the communication to know how to interpret the content.

It would have been good to have such meta-information also for files, allowing files to be renamed to anything with any suffix without loosing the readability. But in the seventies, when Unix and C and libc where initially created, such requirements were much less obvious and it was part of the beauty to have a very simple concept of an I/O-stream universally applicable to devices, files, keyboard input and some other ways of I/O. Also MS-Windows has probably been developed in C and has inherited this flaw. It has been tried to keep MS-Windows runnable on FAT-file-systems, which made it hard to benefit from the feature of NTFS of having multiple streams in a file, so the second stream could be used for the meta information. But as a matter of fact suffixes are still used and text files are analyzed for guessing the encoding and magic bytes in the beginning of binary files are used to assume a certain type.

Off course some text formats like XML have ways of writing the encoding within the content. That requires iterating through several assumptions in order to read up to that encoding information, which is not as bad as it sounds, because usually only a few encodings have to be tried in order to find that out. It is a little bit annoying to deal with this when reading XML from a network connection and not from a file, which requires some smart caching mechanism.

This is most dangerous with UTF-8 and ISO-8859-x (x=1,2,3,….), which are easy to mix up. The lower 128 characters are the same and the vast majority of the content consists of these characters for many languages. So it is easy to combine two files with different encodings and not recognizing that until the file is already somewhat in use and has undergone several conversion attempts to „fix“ the problem. Eventually this can lead to byte sequences that are not allowed in the encoding. Since spoken languages are usually quite redundant, it usually is possible to really fix such mistakes, but it can become quite expensive for large amounts of text. For UTF-16 this is easier because files have to start with FFFE or FEFF (two bytes in hex-notation), so it is relatively reliable to tell that a file is utf-16 with a certain endianness. There is even such a magic sequence of three bytes to mark utf-8, but it is not known by many people, not supported by the majority of the software and not at all commonly used. Often we have to explicitly remove these three marker bytes in order to avoid confusing software processing the file.

In the MS-Windows-world things are even more annoying because the whole system is working with modern encodings, but this black CMD-windows is still using CP-850 or CP-437, which contain almost the same characters as ISO-8859-1, but in different positions. So an „ä“ might be displayed as a sigma-character, for example. This incompatibility within the same system does have its disadvantages. In theory there should be ways to fix this by changing some settings in the registry, but actually almost nobody has done that and messing with the registry is not exactly risk-less.

Links

Share Button

GNU-Emacs und Unicode

Heute sollte man Text-Dateien bevorzugt in Unicode erstellen und speichern. Natürlich braucht man nur englische Texte, deshalb reicht ISO-646 (ASCII) aus, aber ein paar Umlaute kommen doch noch rein, allein wegen Eigennamen und so kann man ISO-8859-1 oder ISO-8859-15 nehmen und hat die Umlaute auch dabei. Praktisch mit demselben Aufwand kann man stattdessen UTF-8 verwenden. Das erlaubt es, die Umlaute zu haben, aber auch alle anderen Unicode-Zeichen. Und die seltenen Umlaute sind zwei Bytes statt einem groß, was in aller Regel vernachlässigbar ist. Eine andere Frage ist es aber, wenn man z.B. Russisch, Griechisch, Japanisch oder Chinesisch schreibt. utf-8 führt dazu, dass die griechischen und russischen Buchstaben mindestens zwei Bytes brauchen und die chinesischen und japanischen noch mehr. Bei diesen Sprachen ist zugegebenermaßen der Anreiz, eine Codierung zu verwenden, die auf diese Sprache zugeschnitten ist, größer, weil man so die Textdateien um Faktor 1.5 oder 2 kleiner machen kann. Weiß jemand, wie das heute praktiziert wird? In welcher Codierung speichert man üblicherweise Textdateien bei Sprache, die nicht auf lateinischen Buchstaben basieren?

Nun hat GNU-Emacs leider vor vielen Jahren seine eigene Idee statt Unicode entwickeln wollen, wie man die Zeichen der verschiedenen Sprachen codiert. Wer das noch weiß, wird vielleicht dem ganzen Werkzeug misstrauen, aber das ist nicht nötig.
Man kann eine einzelne Datei als Textdatei in utf-8 markieren, indem man in der ersten Zeilen einen Kommentar hat, der etwa so aussieht:

// -*- coding: utf-8-with-signature-unix -*-
// -*- coding: utf-8-with-signature -*-
// -*- coding: utf-8-unix -*-
// -*- coding: utf-8 -*-

Ab dann wird die Datei als Unicode gespeichert und auch beim nächsten öffnen interpretiert.
Das „-unix“ erzwingt einen Zeilenwechsel nur aus „ctrl-J“ (LF), statt „ctrl-M ctrl-J“ (CR LF), was in der Regel besser funktioniert, wenn man sowohl auf Win64/Win32 als auch auf Linux/Unix-Plattformen unterwegs ist.
Das -with-signature führt dazu, dass am Anfang der Datei eine „Signatur“ aus drei Bytes eingefügt wird, die ausdrückt, dass die Datei utf-8 ist. Leider wird diese Signatur nicht von allen Werkzeugen verstanden, aber wenn sie verstanden wird, ist das eigentlich der richtige Ansatz, weil man so wirklich utf-8 erkennen kann, ohne die ganze Datei vorher zu lesen.

Das läßt sich auch pro Endung festlegen, wenn man in .emacs so etwas schreibt:

(defvar utf8-suffix-regex nil "describes suffixes of files that are always utf8")

(setq utf8-suffix-regex "\\.\\(cs\\|scala\\|java\\|groovy\\)$")

(defun choose-encoding-by-suffix ()
  "Choose the encoding based on suffix"
  (interactive)
  (when (and (stringp buffer-file-name)
             (string-match utf8-suffix-regex buffer-file-name))
    (set-buffer-file-coding-system 'utf-8-unix nil t)))

(add-hook 'find-file-hook 'choose-encoding-by-suffix)

Wenn bei diesen impliziten Konvertierungen etwas schiefgeht, z.B. weil iso-8859-1 nach utf-8-Konvertierung zweimal statt einmal gelaufen ist, dann hilft recode. Man kann im Emacs die geöffnete Datei mit recode konvertieren:
Ctrl-X h ESC 1 ESC | recode utf8..latin1
Es empfiehlt sich, vorher eine Sicherungskopie anzulegen.

Dieselben Fragestellungen tauchen mit anderen Editoren und Entwicklungsumgebungen auch auf. In Eclipse und Netbeans kann man die Codierung von Dateien nach Endung, Projekt und auch für einzelne Dateien festlegen. Sicher wissen die vi-Spezialisten gut Bescheid, wie es mit vi geht.

Entscheidend ist, dass man sich bewusst für eine Codierung entscheidet. In Sprachen mit lateinischer Schrift dürfte das fast immer utf-8 sein, wenn man nicht „Altlasten“ hat. Das lässt sich mit geeigneten Editoren in den Griff bekommen.

Links

Share Button

Unicode, UTF-8, UTF-16, ISO-8859-1: Warum ist das so schwierig?

English

Seit etwa 20 Jahren schlagen wir uns mit der Umstellung auf Unicode herum.

Warum ist das so schwierig?

Das größte Problem ist, dass man Dateien nur sehr begrenzt ansieht, wie ihr Inhalt zu interpretieren ist. Wir haben letztlich ein paar Tricks, mit denen man es oft erkennen kann:
Die Endungen funktionieren für häufige und gut definierte Dateitypen, z.B. .jpg oder .png recht gut. In anderen Fällen wird der Inhalt der Datei untersucht und zum Beispiel am Anfang der Datei so etwas wie

#!/usr/bin/ruby

gefunden, woraus geschlossen werden kann, dass das mit ruby ausgeführt werden soll und zwar mit dem Ruby, das unter /usr/bin/ruby steht. Wenn man sich lieber nicht festlegen will und das Ruby haben will, das zuerst im Pfad ( $PATH ) kommt, dann kann man stattdessen

#!/usr/bin/env ruby

verwenden. Das geht leider unter MS-Windows nur unter cygwin, wenn man das cygwin-Ruby verwendet, nicht aber mit dem nativen Win32-Ruby (oder Win64-Ruby).

Nun kommt aber der nächste Schritt und der ist einfach ärgerlich. Welches „encoding“ wird für diese Datei verwendet? Man kann sich auf UTF-8 oder ISO-8859-1 einigen, aber sobald einer im Team vergisst, seinen Editor entsprechend zu konfigurieren, ist Durcheinander abzusehen, weil dann Dateien entstehen, die UTF-8 und ISO-8859-1 (oder noch andere Encodings) miteinander mischen, was dann irgendwann zu obskuren Fehlern führt.

Es war ein großer Fehler, dass bei der Entwicklung von C, Unix und vor allem der libc ein Verständnis von Dateien definiert wurde, das keine Typ-Information für den Dateiinhalt erlaubt. Im Internet haben wir Mime-Header für EMail und WWW-Seiten und alles mögliche andere. Dadurch weiß der Empfänger der Kommunikation stets, wie die empfangen Daten zu interpretieren sind. Ich denke, dass Dateien solche Metainformationen haben sollte, die etwa dem Mime-Header entsprechen. Dann könnte man Dateien beliebig umbenennen, sogar die Endung, ohne dass die Datei dadurch unlesbar würde. Aber als Unix und C entwickelt wurde, wurde auch die libc und die Filesystemkonzepte definiert. Daran haben sich alle Unixe seither gehalten und auch Linux folgt diesen Vorgaben. Aber auch in der MS-Windows-Welt hat man die Betriebssysteme wahrscheinlich in C entwickelt und dabei diese Eigenschaften oder deren Fehlen geerbt. Ich weiß nicht, bis wann man versucht hat, MS-Windows-NT/2000/XP/Vista/7/8… noch auf FAT-Dateisystemen lauffähig zu halten, dabei hätte NTFS mit den multiplen Streams pro Datei eine Werkzeug geschaffen, mit dem man so einen Mime-Typ im zweiten Stream und den eigentlichen Inhalt im ersten Stream speichern könnte. Was aber fehlt ist ein allgemein anerkanntes Regelwerk, das die Nutzung des zweiten Streams für Typinformationen festlegt Aber man verwendet weiterhin Endungen, hofft auf gutes Glück bei Textdateien und analysiert magic-bytes innerhalb der Dateien, um den Typ der Datei und das Encoding zu raten. Linux hat Attribute, in denen man solche Information ablegen kann, aber das bringt nur etwas, wenn es ein Standard ist, den „alle“ kennen und der von jeder Software, die davon betroffen ist, eingehalten wird.

Natürlich haben XML und HTML die Möglichkeit, das Encoding innerhalb der Datei zu definieren. Dummerweise muss man aber das Encoding der Datei schon kennen, um die Zeilen zu lesen, wo drinsteht, welches Encoding die Datei hat. Das ist nicht so schlimm, denn letztlich steht diese Information jeweils am Anfang der Datei und man kann ein paar Encodings durchprobieren, und jeweils unter dieser Annahme anfangen, die Datei zu lesen, bis man es richtig weiß. UTF-16 erkennt man an den beiden Markerbytes am Anfang und dann kann man mit der Annahme UTF-16 lesen, bis man das Encoding, das nun UTF-16 sein muss findet. Andernfalls kann man mit der Annahme, dass es UTF-8 ist, anfangen und muss dann auf das, was man gefunden hat, umstellen. Wenn kein Encoding angegeben ist, ist UTF-8 sowieso der Defaultwert.

Am gefährlichsten ist es, UTF-8 und ISO-8859-1 (oder ähnliche Encodings) zu verwechseln. Da die unteren 128 Zeichen bei beiden gleich sind und zumindest im deutschen Sprachraum doch die überwältigende Mehrheit von Text-Inhalten darstellen, sticht das Problem nicht gleich ins Auge, sondern schleicht sich eher ein, wenn man nicht sauber arbeitet und Dateien mit verschiedenen Encodings zusammenkopiert oder die Dateien den falschen Konversionen aussetzt. Nun werden aber die Umlaute in UTF-8 durch zwei Zeichen codiert, in ISO-8859-1 durch eines. Beim Lesen der Datei unter der falschen Annahme bekommt man also irgendwann mal Zeichenfolgen, die in dem Encoding eigentlich gar nicht vorkommen dürften. Bei UTF-16 ist das einfacher, weil die Dateien dort jeweils mit FFFE oder FEFF anfangen, so dass man einigermaßen sicher UTF-16 an sich und die Bytefolge (niedriges zuerst oder hohes zuerst) erkennen kann. Es gäbe auch eine drei Byte lange Markierung für UTF-8. Obwohl das jede Software verstehen sollte, ist man in der Praxis meistens gezwungen, diese Byte-Sequenz zu entfernen, weil sie die meiste Software verwirrt, zu fehlerhaften Verhalten bringt oder gar zum Absturz bringt.

In der MS-Windows-Welt kommt noch als weiteres Ärgernis hinzu, dass zwar das ganze System mit modernen Encoding arbeiten kann, aber diese schwarzen CMD-Fenster kommen immer noch mit CP-850 oder CP-437 hoch, enthalten also in etwa dieselben Zeichen wie ISO-8859-1, aber an anderen Positionen. Da bekommt man dann schon einmal ein Sigma-Zeichen statt einem „ä“ zu sehen. Diese Incompatibilität innerhalb desselben Systems bringt natürlich Nachteile mit sich.

Links

Share Button