aboutsummaryrefslogtreecommitdiff
path: root/articles/2013-10-06_notizen_zu_cpp_und_unicode.md
diff options
context:
space:
mode:
authorAdrian Kummerlaender2014-08-10 13:09:42 +0200
committerAdrian Kummerlaender2014-08-10 13:09:42 +0200
commitf4043ec1e9f02efc7f653274b9e4e22ef2782a51 (patch)
tree7094d038dd30eef441b6895a2ac99daf5720e7d4 /articles/2013-10-06_notizen_zu_cpp_und_unicode.md
parent8d7f2c536f3c48d17b1de244fe6713f4d4499fd5 (diff)
downloadblog_content-f4043ec1e9f02efc7f653274b9e4e22ef2782a51.tar
blog_content-f4043ec1e9f02efc7f653274b9e4e22ef2782a51.tar.gz
blog_content-f4043ec1e9f02efc7f653274b9e4e22ef2782a51.tar.bz2
blog_content-f4043ec1e9f02efc7f653274b9e4e22ef2782a51.tar.lz
blog_content-f4043ec1e9f02efc7f653274b9e4e22ef2782a51.tar.xz
blog_content-f4043ec1e9f02efc7f653274b9e4e22ef2782a51.tar.zst
blog_content-f4043ec1e9f02efc7f653274b9e4e22ef2782a51.zip
Switched content formatter to kramdown
* implemented language selection for automatic syntax highlighting ** language selection requires the language to be used to be passed as a class of the code element ** kramdown enables easy definition of this class attribute * kramdown offers more functionality such as table and class attribute support * updated all articles accordingly
Diffstat (limited to 'articles/2013-10-06_notizen_zu_cpp_und_unicode.md')
-rw-r--r--articles/2013-10-06_notizen_zu_cpp_und_unicode.md82
1 files changed, 34 insertions, 48 deletions
diff --git a/articles/2013-10-06_notizen_zu_cpp_und_unicode.md b/articles/2013-10-06_notizen_zu_cpp_und_unicode.md
index 6685687..fbc6d63 100644
--- a/articles/2013-10-06_notizen_zu_cpp_und_unicode.md
+++ b/articles/2013-10-06_notizen_zu_cpp_und_unicode.md
@@ -11,7 +11,10 @@ Getreu der auf [UTF-8 Everywhere](http://www.utf8everywhere.org/) hervorgebracht
Grundsätzlich stellt es auf der Plattform meiner Wahl - Linux mit Lokalen auf _en\_US.UTF-8_ - kein Problem dar, UTF-8 enkodierte Strings in C++ Programmen zu verarbeiten.
Den Klassen der C++ Standard Library ist es, solange wir nur über das reine Speichern und Bewegen von Strings sprechen, egal ob dieser in UTF-8, ASCII oder einem ganz anderen Zeichensatz kodiert ist. Möchten wir sicher gehen, dass ein in einer Instanz von _std::string_ enthaltener Text tatsächlich in UTF-8 enkodiert wird und dies nicht vom Zeichensatz der Quelldatei abhängig ist, reicht es dies durch voranstellen von _u8_ zu definieren:
- std::string test(u8"Hellø Uni¢ød€!");
+~~~
+std::string test(u8"Hellø Uni¢ød€!");
+~~~
+{: .language-cpp}
Der C++ Standard garantiert uns, dass ein solcher String in UTF-8 enkodiert wird. Auch die Ausgabe von in dieser Form enkodierten Strings funktioniert nach meiner Erfahrung - z.T. erst nach setzen der Lokale mittels _std::setlocale_ - einwandfrei. Probleme gibt es dann, wenn wir den Text als solchen näher untersuchen oder sogar verändern wollen bzw. die Ein- und Ausgabe des Textes in anderen Formaten erfolgen soll. Für letzteres gibt es eigentlich die _std::codecvt_ Facetten, welche aber in der aktuellen Version der GNU libstdc++ noch [nicht implementiert](http://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html#status.iso.2011) sind.
Wir müssen in diesem Fall also auf externe Bibliotheken wie beispielweise [iconv](https://www.gnu.org/software/libiconv/) oder [ICU](http://site.icu-project.org/) zurückgreifen. Auch die in der C++ Standard Library enthaltenen Templates zur String-Verarbeitung helfen uns bei Multibyte-Enkodierungen, zu denen auch UTF-8 zählt, nicht viel, da diese mit dem _char_ Datentyp und nicht mit Code-Points arbeiten. So liefert _std::string_ beispielsweise für einen UTF-8 enkodierten String, welcher nicht nur von dem in einer Code-Unit abbildbaren ASCII-Subset Gebrauch macht, nicht die korrekte Zeichenanzahl. Auch eine String-Iteration ist mit den Standard-Klassen nur Byte- und nicht Code-Point-Weise umsetzbar. Wir stehen also vor der Entscheidung eine weitere externe Bibliothek zu verwenden oder Programm-Intern vollständig auf UTF-32 zu setzen.
@@ -23,34 +26,11 @@ Um zumindest für rein lesende Zugriffe auf UTF-8 Strings nicht gleich eine Bibl
UTF-8 enkodiert die aktuell maximal 21 Bit eines Unicode Code-Points in bis zu vier Code-Units mit einer Länge von je einem Byte. Die verbleibenden
maximal 11 Bit werden dazu verwendet, Anfangs- und Fortsetzungs-Bytes eines Code-Points zu kennzeichnen und schon in der ersten Code-Unit zu definieren, in wie vielen Code-Units das aktuelle Symbol enkodiert ist.
-<p>
-<table>
- <thead>
- <tr>
- <th scope="col">Payload</th>
- <th scope="col">Struktur</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>7</td>
- <td><tt>0xxxxxxx</tt></td>
- </tr>
- <tr>
- <td>11</td>
- <td><tt>110xxxxx 10xxxxxx</tt></td>
- </tr>
- <tr>
- <td>17</td>
- <td><tt>1110xxxx 10xxxxxx 10xxxxxx</tt></td>
- </tr>
- <tr>
- <td>21</td>
- <td><tt>11110xxx 10xxxxxx 10xxxxxx 10xxxxxx</tt></td>
- </tr>
- </tbody>
-</table>
-</p>
+| Payload | Struktur |
+| 7 | `0xxxxxxx` |
+| 11 | `110xxxxx 10xxxxxx` |
+| 17 | `1110xxxx 10xxxxxx 10xxxxxx` |
+| 21 | `11110xxx 10xxxxxx 10xxxxxx 10xxxxxx` |
In obenstehender Tabelle ist die in [RFC3629](http://tools.ietf.org/html/rfc3629) definierte Struktur der einzelnen Code-Units zusammen mit der jeweiligen Anzahl der Payload-Bits dargestellt.
Anhand der Tabelle können wir erkennen, dass die Rückwärtskompatibilität zu ASCII dadurch gewährleistet wird, dass alle Code-Points bis
@@ -58,30 +38,36 @@ einschließlich 127 im ersten Byte dargestellt werden können. Sobald in der ers
Zur Erkennung und Umformung der UTF-8 Code-Units verwenden wir in der _UTF8::CodepointIterator_-Klasse die folgenden, in stark typisierten Enums definierten, Bitmasken:
- enum class CodeUnitType : uint8_t {
- CONTINUATION = 128, // 10000000
- LEADING = 64, // 01000000
- THREE = 32, // 00100000
- FOUR = 16, // 00010000
- };
-
- enum class CodePoint : uint8_t {
- CONTINUATION = 63, // 00111111
- TWO = 31, // 00011111
- THREE = 15, // 00001111
- FOUR = 7, // 00000111
- };
+~~~
+enum class CodeUnitType : uint8_t {
+ CONTINUATION = 128, // 10000000
+ LEADING = 64, // 01000000
+ THREE = 32, // 00100000
+ FOUR = 16, // 00010000
+};
+
+enum class CodePoint : uint8_t {
+ CONTINUATION = 63, // 00111111
+ TWO = 31, // 00011111
+ THREE = 15, // 00001111
+ FOUR = 7, // 00000111
+};
+~~~
+{: .language-cpp}
Bei tieferem Interesse lässt sich die Implementierung der UTF-8 Logik in der Quelldatei [codepoint_iterator.cc](https://github.com/KnairdA/CodepointIterator/blob/master/src/codepoint_iterator.cc) nachlesen.
Zusätzlich zu den in GoogleTest geschriebenen [Unit-Tests](https://github.com/KnairdA/CodepointIterator/blob/master/test.cc) sehen wir im folgenden noch ein einfaches Beispiel zur Verwendung des `UTF8::CodepointIterator` mit einem [Beispieltext](http://www.columbia.edu/~fdc/utf8/) in altnordischen Runen:
- std::string test(u8"ᛖᚴ ᚷᛖᛏ ᛖᛏᛁ ᚧ ᚷᛚᛖᚱ ᛘᚾ ᚦᛖᛋᛋ ᚨᚧ ᚡᛖ ᚱᚧᚨ ᛋᚨᚱ");
+~~~
+std::string test(u8"ᛖᚴ ᚷᛖᛏ ᛖᛏᛁ ᚧ ᚷᛚᛖᚱ ᛘᚾ ᚦᛖᛋᛋ ᚨᚧ ᚡᛖ ᚱᚧᚨ ᛋᚨᚱ");
- for ( UTF8::CodepointIterator iter(test.cbegin());
- iter != test.cend();
- ++iter ) {
- std::wcout << static_cast<wchar_t>(*iter);
- }
+for ( UTF8::CodepointIterator iter(test.cbegin());
+ iter != test.cend();
+ ++iter ) {
+ std::wcout << static_cast<wchar_t>(*iter);
+}
+~~~
+{: .language-cpp}
Die Dereferenzierung einer Instanz des Iterators produziert den aktuellen Code-Point als _char32\_t_, da dieser Datentyp garantiert vier Byte lang ist. Die Ausgabe eines solchen UTF-32 enkodierten Code-Points ist mir allerdings leider nur nach dem Cast in _wchar\_t_ gelungen. Dieser wird trotzdem nicht als Dereferenzierungs-Typ verwendet, da die Länge nicht fest definiert ist, sondern abhängig von der jeweiligen C++ Implementierung unterschiedlich sein kann. Dies stellt jedoch kein größeres Problem dar, da der Iterator für die interne Betrachtung von Strings und nicht zur Konvertierung für die Ausgabe gedacht ist.