UTF-8 Text decodieren



  • Hallo zusammen!

    Ich habe ein kleines Problem bezüglich UTF-8. Und zwar lese ich aus einer Datei byteweise Daten aus. Bei manchen Datensätzen handelt es sich um Text im UTF-8-Format.

    Hier mal ein konkretes Beispiel für eine solche Bytefolge:

    50 72 6F 6D C3 A9 74 68 C3 A9 65

    entspricht decodiert dem Wort "Prométhée".

    Da in diesem Wort nun aber diese komischen "Franzosen-Buchstaben" drin sind, bekomme ich beim auslesen folgenden Output, da die "é" in 2 Byte Länge gespeichert wurden, anstatt (wie alle anderen Buchstaben) in einem Byte.

    Ausgabe beim normalen Auslesen:

    "Prométhée"
    

    So, nun ist halt mein Problem, dass ich das ganze natürlich in "normalen" Buchstaben haben möchte, allerdings hier an die Grenzen meines C++-Wissens stoße.

    Hier mal der relevante Code-Block, mit dem ich das bisher realisiert habe:

    CString SC2Utils::ReadBytesBinary(int iLen)
    {
    	DWORD dwBytesRead = 0;
    	DWORD dwCount = 0;
    	char cBuffer[1] = "";
    	CString strBuffer = L"";
    	CString strTemp = L"";
    
    	// Kontroll-Byte auslesen und verarbeiten (Länge der folgenden binären Daten).
    	if ( iLen == 0 )
    		dwCount = ReadBytesVLF();
    	else
    		dwCount = iLen;
    
    	// Binary-Daten auslesen und verarbeiten.
    	for ( int i = 1 ; i <= dwCount ; i++ )
    	{
    		SFileReadFile(this->m_hHandle, &cBuffer, 1, &dwBytesRead);
    		strTemp = cBuffer[0];
    		strBuffer.Insert(strBuffer.GetLength(), strTemp);
    		strTemp = L"";
    	}
    
    	return strBuffer;
    }
    

    Ich habe natürlich bei Google und in der MSDN geschaut, auch etwas vielversprechendes gefunden (http://www.codeproject.com/KB/string/UTF8.aspx), was allerdings leider nicht funktioniert hat.

    Gibt es für dieses, sicher alltägliche Problem, eine angemessene und praktikable Lösung? Ich bin für jeden Tipp dankbar. Bitte bedenkt aber, dass ich noch nicht so erfahren im Umgang mit C++ bin. Ergo - je simpler das ganze ist, desto besser 😉

    Vielen Dank schonmal im Voraus!

    Gruß
    Skubi


  • Mod

    MultiByteToWideChar wandelt Dir UTF-8 in Unicode um
    http://msdn.microsoft.com/en-us/library/dd319072(VS.85).aspx



  • Super, vielen Dank für den Hinweis!

    Hab's nach einigen kleinen Schwierigkeiten mit dieser Funktion (richtige Datantypen für die Parameter) dann folgendermaßen hinbekommen.

    // ReadBytesBinary - Reads binary data from an open file and returns a CString.
    CString SC2Utils::ReadBytesBinary(int iLen)
    {
    	DWORD dwBytesRead = 0;
    	DWORD dwCount = 0;
    	char cBuffer[1] = "";
    	CString strBuffer = L"";
    	CString strTemp = L"";
    	char* cBuf = 0;
    	int iBufferSize = 0;
    
    	// Read and process the control-byte (length of the following binary data).
    	if ( iLen == 0 )
    		dwCount = ReadBytesVLF();
    	else
    		dwCount = iLen;a
    
    	// Read and process the binary data.
    	for ( int i = 1 ; i <= dwCount ; i++ )
    	{
    		SFileReadFile(this->m_hHandle, &cBuffer, 1, &dwBytesRead);
    		strTemp = cBuffer[0];
    		strBuffer.Insert(strBuffer.GetLength(), strTemp);
    		strTemp = L"";
    	}
    
    	// Convert the read Text from CString to char*
    	String ^ sTemp = gcnew String(strBuffer);
    	IntPtr p = Marshal::StringToHGlobalAnsi(sTemp);
    	cBuf = static_cast<char*>(p.ToPointer());
    
    	// First get the required buffer size.
    	iBufferSize = MultiByteToWideChar(CP_UTF8, 0, cBuf, -1, NULL, 0);
    
    	// Now convert from UTF-8 to Unicode.
    	MultiByteToWideChar(CP_UTF8, 0, cBuf, -1, strBuffer.GetBuffer(strlen(cBuf)), iBufferSize);
    	strBuffer.ReleaseBuffer();
    
    	// Release the pointer used to convert from CString to char*.
    	Marshal::FreeHGlobal(p);
    
    	return strBuffer;
    }
    

    Danke und Gruß
    Skubi


  • Mod

    Selten solchen üblen Code gesehen. Warmun mischt Du da noch C++/CLI mit rein?
    Und warum liest Du das byteweise ein. Und warum bastelst Du Dir den UTF8 String byteweise zusammmen?

    CStringW SC2Utils::ReadBytesBinary(int iLen)
    {
        DWORD dwBytesRead = 0;
        DWORD dwCount = 0;
        // Read and process the control-byte (length of the following binary data).
        if ( iLen == 0 )
            dwCount = ReadBytesVLF();
        else
            dwCount = iLen;
    
        // Get the UTF8 stream
        CStringA strUTF8;
        SFileReadFile(this->m_hHandle, strUTF8.GetBuffer(dwCount), dwCount, &dwBytesRead);
        strBuffer.ReleaseBuffer(dwBytesRead);
    
        // First get the required buffer size.
        int iLen = MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, NULL, 0);
    
        // Now convert from UTF-8 to Unicode.
        CStringW strUnicode;
        MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, strUnicode.GetBuffer(iLen), iLen);
        strBuffer.ReleaseBuffer(iLen);
    
        // Justthe result
        return strUnicode;
    }
    


  • Hiho!

    Hehe, vielen Dank für die konstruktive Kritik. Ich kann voll und ganz nachvollziehen, dass mein "Werk" für einen erfahrenen Programmierer in den Augen weh tun muss 😉 Aber wie gesagt: ich bin relativ neu in der Materie und weiss ehrlich gesagt nicht mal genau, was du mit "C++/CLI" meinst ...ich denke mal den Part mit "String^ ...." oder?

    Dein Code sieht auf den ersten Blick schon besser und effektiver aus. Nur leider habe ich nun das Problem, dass ich als Ergebnis immer einen 25 Zeichen langen String bekomme, der den eigentlichen Text enthält und die restlichen Stellen werden mit komischen Sonderzeichen aufgefüllt, von denen ich nicht weiss wo sie her kommen.

    Hier mal ein solcher Output (direkt aus dem Debugger kopiert mit Adresse):

    0x0093E680 "Xuno��������������������"

    Ich musste übrigens einige kleine Anpassungen vornehmen, weil da z.B. ein Variablenname schon vergeben war und die "ReleaseBuffer()"-Anweisungen haben Fehler verursacht, solange die Parameter übergeben wurden:

    Expression: nLength <= GetData()->nAllocLength()

    Meine etwas geänderte Version von deinem Code sieht nun so aus:

    // ReadBytesBinary - Reads binary data from an open file and returns a CString.
    CString SC2Utils::ReadBytesBinary(int iLen)
    {
    	CString strBuffer = L"";
        DWORD dwBytesRead = 0;
        DWORD dwCount = 0;
        // Read and process the control-byte (length of the following binary data).
        if ( iLen == 0 )
            dwCount = ReadBytesVLF();
        else
            dwCount = iLen;
    
        // Get the UTF8 stream
        CStringA strUTF8;
        SFileReadFile(this->m_hHandle, strUTF8.GetBuffer(dwCount), dwCount, &dwBytesRead);
    	strBuffer.ReleaseBuffer();
    
        // First get the required buffer size.
    	int iSize = MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, NULL, 0);
    
        // Now convert from UTF-8 to Unicode.
        CStringW strUnicode;
        MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, strUnicode.GetBuffer(iSize), iSize);
        strBuffer.ReleaseBuffer();
    
        // Just the result
        return strUnicode;
    }
    

    Kannst du oder jemand anders mir bei diesem Problem weiter helfen und sagen, wieso ich jetzt immer einen 25 Stellen langen String als Ergebnis bekomme, der mit Sonderzeichen aufgefüllt ist und am besten auch, wie ich das beheben kann 😉

    Vielen Dank schonmal!

    Gruß
    Skubi


  • Mod

    Mien Code war einfach aus der hohlen Hand ungetestet.

    Man übergibt an RelaseBuffer die Länge des Strings. Man übergibt nur dann keinen Wert, wenn man sicher ist, dass der String 0 terminiert ist. Davon kann man in Deinem Code nichtausgehen. Ichhabe die Wert eangenommen für die Länge die mir aus dem Kontext logisch erschienen!


Anmelden zum Antworten