Domanda

Ho una stringa multibyte contenente una combinazione di caratteri giapponesi e latini. Sto provando a copiare parti di questa stringa in una posizione di memoria separata. Dal momento che è una stringa multibyte, alcuni caratteri usano un byte e altri caratteri ne usano due. Quando copio parti della stringa, non devo copiare " metà " personaggi giapponesi. Per essere in grado di farlo correttamente, devo essere in grado di determinare dove iniziano e finiscono i caratteri della stringa multi-byte.

Ad esempio, se la stringa contiene 3 caratteri che richiedono [2 byte] [2 byte] [1 byte], devo copiare 2, 4 o 5 byte nell'altra posizione e non 3, poiché se fossi copiando 3 Vorrei copiare solo metà del secondo carattere.

Per capire dove iniziano e finiscono i caratteri della stringa multi-byte, sto cercando di utilizzare la funzione API Windows CharNext e CharNextExA, ma senza fortuna. Quando utilizzo queste funzioni, navigano nella mia stringa un byte alla volta, anziché un carattere alla volta. Secondo MSDN, CharNext dovrebbe La funzione CharNext recupera un puntatore al carattere successivo in una stringa. .

Ecco un po 'di codice per illustrare questo problema:

#include <windows.h>
#include <stdio.h>
#include <wchar.h>
#include <string.h>

/* string consisting of six "asian" characters */
wchar_t wcsString[] = L"\u9580\u961c\u9640\u963f\u963b\u9644";

int main() 
{
   // Convert the asian string from wide char to multi-byte.
   LPSTR mbString = new char[1000];
   WideCharToMultiByte( CP_UTF8, 0, wcsString, -1, mbString, 100,  NULL, NULL);

   // Count the number of characters in the string.
   int characterCount = 0;
   LPSTR currentCharacter = mbString;
   while (*currentCharacter)
   {
      characterCount++;

     currentCharacter = CharNextExA(CP_UTF8, currentCharacter, 0);
   }
}

(ignora la perdita di memoria e la mancata verifica degli errori.)

Ora, nell'esempio sopra mi aspetterei che characterCount diventi 6, poiché questo è il numero di caratteri nella stringa asiatica. Invece, characterCount diventa 18 perché mbString contiene 18 caratteri:

門阜陀阿阻附

Non capisco come dovrebbe funzionare. Come dovrebbe CharNext sapere se " & # 233; & # 8211; & # 8364; & # 233; " nella stringa è una versione codificata di un carattere giapponese, o in effetti i caratteri & # 233; & # 8211; & # 8364; e & # 233 ;?

Alcune note:

  • Ho letto il post sul blog di Joels su ciò che ogni sviluppatore deve sapere su Unicode. Potrei aver frainteso qualcosa, però.
  • Se tutto ciò che volevo fare era contare i caratteri, avrei potuto contare direttamente i caratteri nella stringa asiatica. Tieni presente che il mio vero obiettivo è copiare parti della stringa multi-byte in una posizione separata. La posizione separata supporta solo multi-byte, non widechar.
  • Se riconvertisco il contenuto di mbString in caratteri larghi usando MultiByteToWideChar, ottengo la stringa corretta (& # 38272; & # 38428; & # 38464; & # 38463; & # 38459; & # 38468;), che indica che non c'è niente di sbagliato in mbString.

EDIT: Apparentemente le funzioni CharNext non supportano UTF-8 ma Microsoft ha dimenticato di documentarlo. Ho gettato / copiato insieme la mia routine, che non userò e che deve essere migliorata. Immagino che sia facilmente deformabile.

  LPSTR CharMoveNext(LPSTR szString)
  {
     if (szString == 0 || *szString == 0)
        return 0;

     if ( (szString[0] & 0x80) == 0x00)
        return szString + 1;
     else if ( (szString[0] & 0xE0) == 0xC0)
        return szString + 2;
     else if ( (szString[0] & 0xF0) == 0xE0)
        return szString + 3;
     else if ( (szString[0] & 0xF8) == 0xF0)
        return szString + 4;
     else
        return szString +1;
  }
È stato utile?

Soluzione

Ecco una spiegazione davvero valida di ciò che sta accadendo qui nel Ordinamento del blog All Out : CharNextExA è rotto? . In breve, CharNext non è progettato per funzionare con stringhe UTF8.

Altri suggerimenti

Per quanto posso determinare (google e sperimentazione), CharNextExA in realtà non funziona con UTF-8, supporta solo codifiche multibyte che utilizzano coppie di byte lead / trail più brevi o caratteri a byte singolo.

UTF-8 è una codifica abbastanza regolare, ci sono molte librerie che faranno quello che vuoi, ma è anche abbastanza facile farne una tua.

Dai un'occhiata qui unicode.org , in particolare tabella 3-7 per moduli sequenza validi.

const char* NextUtf8( const char* in )
{
    if( in == NULL || *in == '\0' )
        return in;

    unsigned char uc = static_cast<unsigned char>(*in);

    if( uc < 0x80 )
    {
        return in + 1;
    }
    else if( uc < 0xc2 )
    {
         // throw error? invalid lead byte
    }
    else if( uc < 0xe0 )
    {
        // check in[1] for validity( 0x80 .. 0xBF )
        return in + 2;
    }
    else if( uc < 0xe1 )
    {
        // check in[1] for validity( 0xA0 .. 0xBF )
        // check in[2] for validity( 0x80 .. 0xBF )
        return in + 3;
    }
    else // ... etc.
    // ...
}

Dato che CharNextExA no ' lavorare con UTF-8 , puoi analizzarlo da solo. Basta saltare i personaggi che hanno 10 nei primi due bit. Puoi vedere lo schema nella definizione di UTF-8: http://en.wikipedia.org / wiki / UTF-8

LPSTR CharMoveNext(LPSTR szString)
{
    ++szString;
    while ((*szString & 0xc0) == 0x80)
        ++szString;
    return szString;
}

Questa non è una risposta diretta alla tua domanda, ma potresti trovare utile il seguente tutorial, l'ho sicuramente fatto. In effetti le informazioni fornite qui sono sufficienti per poter attraversare facilmente la stringa multi-byte:

Tutorial completo sulle stringhe

Prova a usare 932 per la tabella codici. Non credo che CP_UTF8 sia una vera tabella codici e potrebbe funzionare solo per WideCharToMultibyte () e ritorno. Puoi anche provare isleadByte (), ma ciò richiede l'impostazione corretta della locale o l'impostazione corretta della tabella codici predefinita. Ho usato con successo IsDBCSLeadByteEx (), ma mai con CP_UTF8.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top