UTF-8 in Software, die mit VCL programmiert wurde



  • Hallo,

    ich hoffe, dass meine Anfrage an dieser Stelle richtig ist, denn ich weiß nicht genau, wo die Ursache meines Problems liegt. Wenn die Frage hier falsch sein sollte, bitte ich vielmals um Entschuldigung und um Verschiebung dieses Threads.

    Ich bastel zur Zeit an einem kleinen Tool, das XML-Dateien (UTF-8 encoded) einliest und anschließend den Inhalt (der aus Einstellungen des Users resultiert) auf Sinnhaftigkeit überprüfen soll.

    Ich lese die XML-Datei mit einem XML-Parser ein und lasse mir alle Inhalte anzeigen, was jedoch leider nicht bei Sonderzeichen klappt. Z.B. erhalte ich anstatt des XML-Eintrages "Düsseldorf" in meinem Tool "Düsseldorf".

    Das Problem liegt nicht am XML-Parser, denn wenn ich die XML-Datei (versuchshalber mal ausprobiert) über fgetc() zeichenweise einlese, wird auch kein "ü" eingelesen, sondern als erstes das Zeichen "Ã" und dann das Zeichen "¼".

    Ich habe auch bereits die Schriftart Arial Unicode MS installiert und diese im Builder für die Textfelder eingestellt. Das Resultat bleibt das gleiche.

    Von einer Umcodierung des XML-Files von UTF-8 in ISO 8859-1 vor dem Einlesen (was wahrscheinlich eine Lösung darstellen würde??) möchte ich absehen, da die Datei nach der Überpüfung als UTF-8 weiter verarbeitet werden muss.

    Hat einer eine Idee, wie ich den Borland Builder (Version 6) oder aber den Windowsrechner, auf dem das Tool laufen soll, dazu bewegt bekomme, mit UTF-8 zu arbeiten?

    Danke für Eure Ideen.



  • Bist du schon auf die Idee gekommen, dass einfach nur deine Ausgabefunktion die Daten nicht als UTF-8 interpretiert?



  • Hallo

    Wo ist das Problem? Du sagst doch selber das die Datei UTF8-codiert ist und nicht ANSI. Dein XML-Parser als auch fgetc lesen die Datei als ANSI ein, also bekommen sie die kodierten Zeichen.
    Du hast nun zwei Möglichkeiten :
    - Du arbeitest intern eben mit (Ansi-) Strings die UTF8 enthalten, und schreibst diese genauso wieder in die Zieldatei zurück. UTF8 ist ja gerade dazu da um Unicode in ANSI zu packen. Wenn du dann doch dem User die Strings korrekt mit Umlauten anzeigen must, brauchst du noch eine interne Konvertierung von UTF8 in WideString.
    - Du verwendest einen XML-Parser der gleich Unicode-fähig ist, sprich der UTF8 selber intern in Unicode umwandelt und dann auch WideString anstelle von AnsiString verwendet.

    bis bald
    akari



  • Wie meinst Du das?
    Die "Ausgabefunktion" ist doch lediglich eine Zuweisung Label->Caption = string.

    Darüber hinaus scheint das Problem (wie das fgetc() Beispiel zeigt) ja schon beim Einlesen zu bestehen.

    if ( (readfile = fopen(file,"rt")) != 0 )
    {
       if ( (writefile = fopen("D:\\xml.txt","wt")) != 0 )
       {
          while(!feof(readfile))
          {
             zeichen = fgetc(readfile);
             if(zeichen != EOF)
                fputc(zeichen,writefile);
          }
       }
    }
    

    Wenn ich hier mit dem Debugger die Variable "zeichen" überwache, dann bekomme ich das Wort Düsseldorf nicht als D-ü-s-s-e-l-d-o-r-f sondern als
    D-Ã-¼-s-s-e-l-d-o-r-f

    Dabei macht es auch keinen Unterscheid, ob ich die Quelldaten binär oder als Text einlese, bzw. die Zieldaten binär oder als Text ausgebe. Alles schon versucht zur Fehlersuche.

    Somit kann es (in meinen Augen, aber ich lasse mich da gerne korrigieren) auch nicht am XMLParser liegen, denn den benutze ich ja bei dem obigen fgetc()-Beispiel gat nicht, und auch nicht an einer späteren Zuweisung der Strings an ein Label.

    Danke für Eure weitere Unterstützung.



  • Hallo

    In deiner Datei steht ja auch tatsächlich die Bytefolge "D-Ã-¼-s-s-e-l-d-o-r-f " (Öffne mal deine Datei mit einem Hexeditor).
    Deshalb wird jede ANSI-Einlesefunktion auch diesen Text so verarbeiten, zum Beispiel alle VCL-Operation (die ja mit AnsiString arbeiten) oder auch alle C/WinAPI-Funktionen mit char*.
    Um nun aus dem UTF8-kodierten AnsiString einen dekodierten WideString zu bekommen must du die UTF8-Dekodierung noch durchführen.
    Dazu brauchst du wie schon gesagt entweder einen (externen) XML-Parser der bereits Unicode ausgibt, oder eine (externe) Konvertierung von UTF8-AnsiString in WideString.
    Dann hast du in dem Unicode auch das korrekte Sonderzeichen.

    bis bald
    akari



  • akari schrieb:

    Hallo

    [...]- Du verwendest einen XML-Parser der gleich Unicode-fähig ist, sprich der UTF8 selber intern in Unicode umwandelt und dann auch WideString anstelle von AnsiString verwendet.

    bis bald
    akari

    Sorry, hätte ich vielleicht dazu sagen sollen, der XMLParser ist unicodefähig und ich verwende ihn auch in der UNICODE-Version.

    Ich benutze folgenden XML-Parser: http://iridia.ulb.ac.be/~fvandenb/tools/xmlParser.html

    Auf der Seite steht:

    To manipulate the data contained inside the XML file, the first operation is to get an instance of the class XMLNode that is representing the XML file in memory. You can use:
    XMLNode xMainNode=XMLNode::openFileHelper("PMMLModel.xml","PMML");

    or, if you use the UNICODE windows version of the library:
    XMLNode xMainNode=XMLNode::openFileHelper("PMMLModel.xml",_T("PMML"));

    Mein Aufruf ist auch:

    XMLNode xMainNode=XMLNode::openFileHelper(file,_T("beauftragung"));

    Also arbeitet der XMLParser ja mit UTF-8.



  • Hallo

    Ja du hättest schon angeben können was für einen externen Parser du benutzt...
    Dann must du in der Dokumentation des Parsers mal genauer schauen ob der wirklich UTF8 dekodieren kann oder ob nur die Library Unicode-fähig ist, wie es der letzte zitierte Satz sagt. Das ist nämlich nicht dasselbe!

    bis bald
    akari



  • Hallo,

    darf ich zu diesem Punkt noch zwei Frage ergänzen? Ich bastel seit dem ersten Beitrag an meinem Quellcode, aber bekomme es trotzdem nicht hin. Mit dem Autor des XMLParsers habe ich gesprochen, der Parser ist UNICODE-fähig (nicht nur die Library) und mit folgenden Code sollte auch alles funktionieren:

    #include <stdio.h>
    #include <utilcls.h>                    // for TCHAR
    #include <conio.h>                      // for getch()
    #include "xmlparser.h"
    
    int main(int argc, char* argv[])
    {
       XMLNode xMainNode=XMLNode::openFileHelper("PMMLModel.xml",_T("PMML"));
       XMLNode xNode=xMainNode.getChildNode(_T("Header"));
       TCHAR *t = xNode.getChildNode(_T("Application")).getAttribute(_T("name"));
       printf("%S",t);    
    
       getch();
       return 0;
    }
    

    Daraus ergeben sich für mich aber zwei Probleme:

    Die Zeile

    TCHAR *t = xNode.getChildNode(_T("Application")).getAttribute(_T("name"));
    

    bringt mir den Error "Konertierung von 'const char *' nach 'char *' nicht möglich.
    Gibt es eine Funktion, mit der ich diese Konvertierung durchführen kann?

    Ich könnte zwar alternativ mit

    const TCHAR *t =
    

    arbeiten, aber da ich t einige Dutzend Male brauche, dürfte das nicht so sinnvoll sein, denn wenn ich t konstant setze, kann ich es doch nicht mehr ändern.

    Die printf-Zeile selber funktioniert nicht, sie bringt zwar keinen Error, das Programm läuft, aber es wird nichts ausgegeben. Die Ursache liegt am großen "%S". Der Programmierer des XMLParsers meinte, das könnte auch nur funktionieren, wenn im Projekt selber UNICODE eingestellt wäre. Ich finde aber nirgendwo in den Projekteinstellungen des C++ Builder Version 6 eine Möglichkeit, UNICODE einzustellen. Weiß einer von Euch, wo ich das machen kann?

    Nochmals vielen Dank für Eure Hilfe.



  • So, nachdem hier keiner mehr helfen konnte und ich noch tagelang gesucht habe und fast verzweifelt bin, scheint die Antwort darin zu liegen, das die Komponenten (z.B. das Label) des C++ Builder Version 6 kein Unicode unterstüzen.
    Ob das jetzt richtig ist oder nicht weiß ich nicht, aber ich habe diese Info auch im Netz gefunden und mich dann an eine eigene Lösung begeben.
    Diese möchte ich hier kurz vorstellen für den nächsten, der an dem gleichen Problem verzweifelt.

    Würde das Label unicode unterstützen, sollte es wie folgt klappen:

    Main->Label->Caption = xNode.getAttribute(_T("xmlfeldname"));
    

    Stattdessen sieht die Zuweisung jetzt wie folgt aus:

    Main->Label->Caption = unicode(xNode.getAttribute(_T("xmlfeldname")));
    

    Und dazu gibt es die passende Funktion unicode:

    char *unicode (AnsiString string_ansi)
    {
       int i = 0,
           j = 0,
           laenge = 0;
       char string_char[255] = {0};
    
       strcpy(string_char,string_ansi.c_str());
    
       laenge = strlen(string_char);
    
       for (i=0; i<laenge; i++)
       {
          if(string_char[i] < 0)                // 2-Byte-Zeichen
          {
             if(string_char[i] == -61)
             {
                if(string_char[i+1] == -68)     // ü
                   string_converted[j] = 'ü';
    
                if(string_char[i+1] == -74)     // ö
                   string_converted[j] = 'ö';
    
                if(string_char[i+1] == -92)     // ä
                   string_converted[j] = 'ä';
    
                if(string_char[i+1] == -97)     // ß
                   string_converted[j] = 'ß';
    
                if(string_char[i+1] == -100)    // Ü
                   string_converted[j] = 'Ü';
    
                if(string_char[i+1] == -106)    // Ö
                   string_converted[j] = 'Ö';
    
                if(string_char[i+1] == -124)    // Ä
                   string_converted[j] = 'Ä';
    
                j++;
             }
          }
          else
          {
             string_converted[j] = string_char[i];
             j++;
          }
       }
    
       string_converted[j] = '\0';
    
       return(string_converted);
    

    Ich denke nicht, dass es besonders elegant ist, es geht bestimmt besser und sinnvoller, aber es funktioniert und erfüllt seinen Zweck. 😃



  • Wenn ich das richtig sehe, willst Du einen UTF8-String in ASCII umwandeln richtig? Warum nimmst Du dann nicht Utf8ToAnsi()? Dann musst Du auch nicht zittern, dass der String evtl mehr als 255 Zeichen besitzt und dass das Label störende Zeichen nach den Umlauten anzeigt.
    Edit: Sorry mein Fehler, den letzten Punkt hast Du ja mit
    if(string_char[i] < 0)
    abgefangen.



  • witte schrieb:

    Wenn ich das richtig sehe, willst Du einen UTF8-String in ASCII umwandeln richtig? Warum nimmst Du dann nicht Utf8ToAnsi()? Dann musst Du auch nicht zittern, dass der String evtl mehr als 255 Zeichen besitzt und dass das Label störende Zeichen nach den Umlauten anzeigt.
    Edit: Sorry mein Fehler, den letzten Punkt hast Du ja mit
    if(string_char[i] < 0)
    abgefangen.

    ... weil selber programmieren mehr Spaß macht? 😉
    Nein, Spaß bei Seite, ich kannte die Funktion nicht.
    Ich habe tagelang das Internet nach meinem Problem durchsucht, aber anscheinend mit den falschen Suchwörtern.

    Danke für den Tipp, dann kann ich meine kleine Funktion ja wieder rausschmeißen und Utf8ToAnsi() nehmen.



  • Hallo

    Das die normalen Builder-Controls kein Unicode können ist klar, wir sind hier schon davon ausgegangen das du eine Unicode-kompatible Ausgabe benutzt...

    Deine unicode-Funktion ist wirklich nur eine Notlösung.
    - Warum gibst du char* zurück und nicht gleich AnsiString? Dann must du dich auch nicht mit der globalen Variablen string_converted herumschlagen
    - So richtig Unicode-fähig scheint die xml-lib aber doch nicht zu sein, wenn sie dir UFT8-AnsiString zurückgibt und nicht gleich decodierte WideString
    - Deine Funktion behandelt nur deutsche Sonderzeichen. Damit aber die Unicode-Unterstützung Sinn macht sollten auch alle anderen Zeichen verarbeitet werden

    Wenn Unicode dann komplett. Schau die die von Witte genannte Funktion an (ist im BCB5 jedenfalls noch nicht dabei), oder such dir gleich eine andere xml-lib, die gleich richtiges decodierte WideString bzw. äquivalentes zurückgibt.
    Zum Anzeigen kannst du dann auch gleich eine externe Lib suchen die WideString-Controls anbietet. Zum Beispiel TntUnicode, da findest du vielleicht noch die alte, kostenlose Version im Netz.

    bis bald
    akari



  • Ich weiß das das nur eine Notlösung war, aber wenn man wie ein Ochse vorm Berg steht, dann lieber eine Notlösung als ein nicht ordungsgemäß laufendes Programm.

    Da mein kleines Tool auch auf immer und ewig nur deutsche Sonderzeichen einlesen wird, hatte ich mich mit den paar Ausführungen in meiner unicode() begnügt, mehr hätte ich nie gebraucht, aber Du hast natürlich Recht, eine voll funktionsfähige Konvertierung für alle Sonderzeichen ist natürlich wesentlich besser.
    Ich hätte die Funktion ja auch von Anfang an genommen, wenn ich sie gekannt hätte. 😞

    Also ich habe gerade meine kleine Funktion unicode() rausgeschmissen und komplett durch Utf8ToAnsi() ersetzt, klappt wunderbar.

    Danke für Eure Hinweise.



  • W.Stecher schrieb:

    So, nachdem hier keiner mehr helfen konnte und ich noch tagelang gesucht habe und fast verzweifelt bin, scheint die Antwort darin zu liegen, das die Komponenten (z.B. das Label) des C++ Builder Version 6 kein Unicode unterstüzen.

    So ist es. Erst die kommende C++Builder-Version wird Unicode in der VCL unterstützen.

    akari schrieb:

    - So richtig Unicode-fähig scheint die xml-lib aber doch nicht zu sein, wenn sie dir UFT8-AnsiString zurückgibt und nicht gleich decodierte WideString

    Weshalb das? UTF-8 ist genauso ein Unicode-Zeichensatz wie WideString (UTF-16).


Anmelden zum Antworten