{"id":394,"date":"2014-02-21T17:17:39","date_gmt":"2014-02-21T16:17:39","guid":{"rendered":"http:\/\/www.instruyete.org\/?p=394"},"modified":"2014-02-21T17:17:39","modified_gmt":"2014-02-21T16:17:39","slug":"punkt-oder-komma","status":"publish","type":"post","link":"https:\/\/www.instruyete.org\/?p=394","title":{"rendered":"Punkt oder Komma"},"content":{"rendered":"<p>Wenn ein Program dass Gleitkommazahlen von einer oder in eine Textdatei liest, bzw. schreibt nicht mehr funktioniert, dann sollte man sich genauer mit <em>LC_NUMERIC<\/em> befassen. Dieser Artikel behandelt die Auswirkungen auf Qt- und reine C-Programme.<\/p>\n<p><em>LC_NUMERIC<\/em> ist Bestandteil der sogenannte <strong>locales<\/strong>, der Lokalisierungen (oder auch Regionalisierungen) auf einem System. Diese Lokalisierungen lassen sich sehr fein einstellen &#8211; bspw. <em>LC_MONETARY<\/em> f\u00fcr die W\u00e4hrung oder eben <em>LC_NUMERIC<\/em> f\u00fcr das Zahlenformat &#8211; aber auch zusammenfassend mittels <em>LC_ALL<\/em>. Setzt man bspw. <code>LC_ALL=de_CH.UTF-8<\/code>, dann wird diese Einstellung auf alle <strong>sublocales<\/strong> \u00fcbertragen und f\u00fcr <em>LC_MONETARY<\/em> ist der Franken gesetzt, wenn auch das meiste andere ziemlich deutsch ist.<\/p>\n<p>Ok, nun aber zum Zahlenformat &#8211; und um die Sache ein bischen zu beschleunigen, soll eine Textdatei mit folgendem Inhalt ausgelesen werden.<br \/>\n<code><br \/>\n#Hall\u00f6le<br \/>\n33.456,78<br \/>\n33456.78<br \/>\n33,456.78<br \/>\n33,99<br \/>\n33.98<br \/>\n33.777<\/code><\/p>\n<p>Um die Zahlen besser zu verstehen muss noch einmal ausgeholt werden. <em>LC_NUMERIC<\/em> legt zwei Parameter fest.<br \/>\nDen <strong>Dezimaltrenner<\/strong> und den Tausender-Trenner. Er wird folgendermassen gesetzt:<\/p>\n<table border=\"1\">\n<tr>\n<th> Locale <\/th>\n<th> Dezimaltrenner<\/th>\n<th>Tausender-Trenner<\/th>\n<\/tr>\n<tr>\n<td>en_US<\/td>\n<td align=\"center\">.<\/td>\n<td align=\"center\">,<\/td>\n<\/tr>\n<tr>\n<td>de_DE<\/td>\n<td align=\"center\">,<\/td>\n<td align=\"center\">.<\/td>\n<\/tr>\n<tr>\n<td>C<\/td>\n<td align=\"center\">.<\/td>\n<td align=\"center\"> <\/td>\n<\/tr>\n<\/table>\n<p>Das bedeutet, dass die deutsche Lokalisiserung genau andersherum als die US-Amerikanische ist. Die sog.<strong> C-Locale<\/strong> (auch POSIX-Locale genannt) kennt nur den Dezimaltrenner. Diese Locale ist als R\u00fcckfallebene gedacht.<\/p>\n<p>Wie verh\u00e4lt sich nun ein Qt-Programm (Qt4), dass die o.g. Textdatei zeilenweise einliest und versucht den Text in Zahlen zu konvertieren? Zuerst einmal wird ein Testprogram unter der Umgebung <strong>LC_NUMERIC=de_DE.UTF-8<\/strong> gestartet.<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/www.instruyete.org\/wp-content\/uploads\/2014\/02\/localeTest_de.png\" alt=\"localeTest_de\" class=\"aligncenter\"\/><\/p>\n<p>Die Erkl\u00e4rung ist \u00fcberrasch komplex. Zuerst einmal muss festgestellt werden, dass Qt Programme generell LC_NUMERIC-aware sind. Dies wird dadurch erreicht, dass <em>QCoreApplication<\/em><\/p>\n<p><code>setlocale(LC_ALL,\"\");<\/code><\/p>\n<p>aufruft und damit locales aus dem Environment dem Programm zur Verf\u00fcgung gestellt werden (dies wird durch die doppelten Anf\u00fchrungszeichen erreicht). Dennoch werten nicht alle Qt-Methoden <em>LC_NUMERIC <\/em>aus.<\/p>\n<p><code>QByteArray::toDouble()<\/code><br \/>\nignoriert die gesetzte locale und benutzt immer die C-Locale.<\/p>\n<p><code>QString::toDouble()<\/code><br \/>\nverh\u00e4lt sich am kompliziertesten. Es wird der Dezimaltrenner der gesetzten locale ausgewertet, nicht aber der Tausender-Trenner (die Gr\u00fcnde liegen in der Kompatibilit\u00e4t zu C, siehe sp\u00e4ter). Gleichzeitig wird die C-Locale immer als Fallback mitausgewertet. Bei Qt5 verh\u00e4lt sich \u00fcbrigens QString::toDouble wie QByteArray::toDouble<\/p>\n<p><code>QLocale::toDouble()<\/code><br \/>\nhingegen orientiert sich ausschliesslich an der gesetzten locale.<\/p>\n<p>Zum Vergleich nun die Ausgabe unter dem Environment <strong>LC_NUMERIC=en_US.UTF-8<\/strong><\/p>\n<p><img decoding=\"async\" src=\"http:\/\/www.instruyete.org\/wp-content\/uploads\/2014\/02\/localeTest_en.png\" alt=\"localeTest_en\" class=\"aligncenter\" \/><\/p>\n<p>Interessant ist nun der Vergleich zu reinen C-Programme. Denn reine C-Programme mit ihren typischen Funktionen<\/p>\n<p><code>printf\/fprintf<br \/>\nscanf\/fscanf<br \/>\nstrtof<br \/>\n<\/code><\/p>\n<p>..usw. ignorieren standardm\u00e4ssig die gesetzte locale und st\u00fctzen sich immer auf die C-Locale. Erst durch den schon oben genannten Aufruf von <em>setlocale<\/em> werden C-Programme locale-aware.<br \/>\nWerden allerdings C-Funktionen &#8211; bspw. aus einer Bibliothek &#8211; von einem Qt-Programm verwendet, sind sie automatisch locale-aware, da ja wie schon erw\u00e4hnt <em>QCoreApplication<\/em> <em>setlocale<\/em> aufruft.<\/p>\n<p><strong>Dies ist eine gro\u00dfe potentielle Fehlerquelle!<\/strong><\/p>\n<p>Um den Bogen zu QString::toDouble() nochmals zu spannen, sei erw\u00e4hnt, dass C-Funktionen den Tausender-Trenner standardm\u00e4ssig ignorieren. Um z.B. printf zur Ausgabe des Tausender-Trenners bei Gleitkommazahlen zu zwingen, muss man<\/p>\n<p><code>printf(\"Gezwungen zu %'f\",myfloat);<\/code><\/p>\n<p>einen Abostroph vor dem Formatierungszeichen einf\u00fchren.<br \/>\nZum Ausprobieren liegt das Qt Beispielprogramm sowie zwei plain C-Programme im <a href=\"http:\/\/www.instruyete.org\/wp-content\/uploads\/2014\/02\/locale.tar\">diesem Archiv bei<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wenn ein Program dass Gleitkommazahlen von einer oder in eine Textdatei liest, bzw. schreibt nicht mehr funktioniert, dann sollte man sich genauer mit LC_NUMERIC befassen. Dieser Artikel behandelt die Auswirkungen auf Qt- und reine C-Programme. LC_NUMERIC ist Bestandteil der sogenannte locales, der Lokalisierungen (oder auch Regionalisierungen) auf einem System. Diese Lokalisierungen lassen sich sehr fein &hellip; <a href=\"https:\/\/www.instruyete.org\/?p=394\" class=\"more-link\"><span class=\"screen-reader-text\">Punkt oder Komma<\/span> weiterlesen<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9,10,11,7],"tags":[16,18],"class_list":["post-394","post","type-post","status-publish","format-standard","hentry","category-linux","category-macos","category-solaris","category-unix","tag-locale","tag-qt"],"_links":{"self":[{"href":"https:\/\/www.instruyete.org\/index.php?rest_route=\/wp\/v2\/posts\/394","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.instruyete.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.instruyete.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.instruyete.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.instruyete.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=394"}],"version-history":[{"count":0,"href":"https:\/\/www.instruyete.org\/index.php?rest_route=\/wp\/v2\/posts\/394\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.instruyete.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=394"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.instruyete.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=394"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.instruyete.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=394"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}