Problem mit Umlauten aus Datei
-
Hallo,
ich muss eine DBase Datei auslesen, welche im DOS Format 850 encodiert ist.
Mein Programm hat die "normale" encodierung, dadurch werden Umlaute und Sonderzeichen nicht korrekt dargestellt.Einlesen tue ich mit einem ifstream:
if(del != 26)// 26 == EOF { try{ //std::setlocale(LC_ALL,"cp850"); typedef std::codecvt_byname<char, char, std::mbstate_t> Cvt; in.imbue(std::locale("C")); char *data = new char[length]; in.read(data,length); if(del != '*')//only save undeleted data { /*std::stringstream ss; std::string temp; ss.imbue(std::locale("iso_8859_1")); ss << data; temp = ss.str(); ss.clear();*/ tabledata.InsertDataField(std::string(subrecords[j].name),data); } delete[] data; } catch(std::exception& e) { wxMessageBox(e.what()); return; } }Wie muss ich das jetzt korrekt anwenden?
Bei jedem std::locale("CodePageName") fliegt jeweils eine exception.
Wie lässt sich das korrekt convertieren?phlox
-
Hallo phlox,
da bleiben nur zwei Möglichkeiten - entweder Code-Converter-Facette kaufen oder selber schreiben. Ich habe sowas schon mal für die Ausgabe gemacht, damit mir in der DOS-Konsole nicht ständig die Sonderzeichen um die Ohren fliegen.
Den Input für die Konvertierung von der MS-Codepage nach Unicode habe ich hier gefunden.Die Google-Code-Suche brachte leider nichts.
Gruß
Werner
-
Hm, interessant. Wie müsste ich das nun implementieren?
Hab da ehrlich keine Ahnung, was ich wo überschreiben muss, um es möglichst konform zu machen.
Ich brauchs ja gerade nicht nach unicode, obwohl die CodePage natürlich schon hilfreich ist.Ich finde diese Seiten hier:
http://www.cantrip.org/locale.html
http://stdcxx.apache.org/doc/stdlibug/25-6.htmlaber da steht auch nix, wo ich eine conversion implementieren müsste.
Hm, gehts vielleicht mit boost::iostream?
http://www.boost.org/doc/libs/1_37_0/libs/iostreams/doc/index.htmlda jemand Erfahrung?
phlox
-
Hallo phlox,
hinter den beiden ersten von Dir genannten Links wird gezeigt, wie man eine eigene Facette konstruiert. Was man hier benötigt ist eine Facette, die dem streambuf bereits bekannt ist.
Das ist die std::codecvt. Sie ist für Codeconvertierung zuständig, genauer wird mit Ihrer Hilfe festgelegt, wie ein Zeichen als Folge von Bytes - z.B. in einer Datei - abgelegt wird. Im einfachsten Fall ist ein Byte aus der Datei ein Zeichen im streambuf - z.B. in Deinem Fall wenn Du von einem Zeichencode 850 nach einem 8Bit-Zeichen in Unicode (hier begrenzt auf die Code Charts Basic Latin und Latin-1 Code Chart wegen der 8Bit) konvertieren möchtest.
Eine andere bekannte Konvertierung ist die von UTF8 bei der zwischen einem und vier Byte ein beliebiges Unicode-Zeichen repräsentieren.Zur Implementierung - man leite von std::codecvt ab und überlade die Methode do_in, um aus Bytes (extern_type) Zeichen (intern_type) zu machen, also vom Device in den Streambuf (Input) bzw. do_out für die Konvertierung von Zeichen (intern_type) in eine Bytefolge (extern_type -> Output).
#include <locale> template< typename T > class CodePage850 : public std::codecvt< T, char, std::mbstate_t > { protected: virtual result do_in( state_type& , const extern_type* from, const extern_type* from_end, const extern_type*& from_next, intern_type* to, intern_type* to_limit, intern_type*& to_next) const { for( ; to != to_limit && from != from_end; ++to, ++from ) { // hier ggf. *from (aus CodePage) in *to (UniCode meistens Latin-1) konvertieren } from_next = from; to_next = to; return ok; } virtual bool do_always_noconv() const { return false; } virtual int do_encoding() const { return 1; } };mit dem Überladen von do_always_noconv = false teilt man dem streambuf mit, dass eine Konvertierug stattfindet. Das do_encoding = 1 heißt, dass eine 1:1-Konvertierung stattfindet, das heißt der State (1.Parameter) wird nicht benötigt.
Anwendung:
ifstream in( "input.txt" ); in.imbue( locale( in.getloc(), new CodePage850< char > ) );Der Zeichentyp (hier char) muss mit dem Zeichentyp des Streams übereinstimmen, das ist der 'intern_type' in den beim Input konvertiert wird.
Bleibt noch zu erwähnen, dass zumindest die MS-Implementierungen der STL an dieser Stelle so buggy sind, dass es da nicht korrekt funktioniert. In den aktuellen Versionen VC8 und VC9 (express) funktioniert der Input nur mit wchar_t.
Gruß
Werner
-
Vielen dank. Ich verwende MinGW, und es läuft jetzt.
Meine implementation sieht so aus:
template< typename T > class CodePage850 : public std::codecvt< T, char, std::mbstate_t > { typedef std::mbstate_t state_type; typedef T intern_type; typedef char extern_type; protected: virtual std::codecvt_base::result do_in( state_type& , const extern_type* from, const extern_type* from_end, const extern_type*& from_next, intern_type* to, intern_type* to_limit, intern_type*& to_next) const { for( ; to != to_limit && from != from_end; ++to, ++from ) { // hier ggf. *from (aus CodePage) in *to (UniCode meistens Latin-1) konvertieren switch(*from) { case 0x84: *to = 'ä'; break; case 0x8e: *to = 'Ä'; break; case 0x94: *to = 'ö'; break; case 0x99: *to = 'Ö'; break; case 0x81: *to = 'ü'; case 0x9a: *to = 'Ü'; break; case 0xe1: *to = 'ß'; break; default: *to =*from; } } from_next = from; to_next = to; return std::codecvt_base::ok; } virtual bool do_always_noconv() const throw() { return false; } virtual int do_encoding() const throw() { return 1; } };Alle anderen Zeichen wurden ja schon vorher korrekt dargestellt, daher das *to = *from.
phlox