Ottenere la lunghezza effettiva di una std :: string codificata UTF-8?

my std :: string è codificato in utf-8, quindi ovviamente str.length () restituisce il risultato errato.

Ho trovato queste informazioni ma non sono sicuro di come posso usarlo per fare questo:

Le seguenti sequenze di byte sono usate per rappresentare un carattere. La sequenza da utilizzare dipende dal numero di codice UCS del personaggio:

0x00000000 - 0x0000007F: 0xxxxxxx 0x00000080 - 0x000007FF: 110xxxxx 10xxxxxx 0x00000800 - 0x0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx 0x00010000 - 0x001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 

Come posso trovare la lunghezza effettiva di una std :: string codificata UTF-8? Grazie

Uno dei progetti a cui contribuisco ha una piccola funzione che lo fa:

http://openlierox.git.sourceforge.net/git/gitweb.cgi?p=openlierox/openlierox;a=blob;f=include/Unicode.h;h=a523b464fc65a7ad875e683cd830b41c9a01934a;hb=HEAD

Cerca Utf8StringSize . Dipende da un’altra piccola funzione nello stesso file di intestazione.

Conta tutti i first-byte (quelli che non corrispondono a 10xxxxxx).

 int len = 0; while (*s) len += (*s++ & 0xc0) != 0x80; 

C ++ non sa nulla delle codifiche, quindi non puoi aspettarti di usare una funzione standard per farlo.

Questo (oltre alla maggior parte delle risposte) sembrava mancare, ma sfortunatamente come nuovo utente non posso sottovalutare. La libreria standard effettivamente riconosce l’esistenza di codifiche di caratteri, nella forma di locales. Se il tuo sistema supporta un locale, è molto facile usare la libreria standard per calcolare la lunghezza di una stringa. Nel codice di esempio qui sotto presumo che il tuo sistema supporti il ​​locale en_EN.UTF-8. Se compilo il codice e lo eseguo come “./a.out ソ ニ ー Sony”, l’output è che c’erano 13 valori di caratteri e 7 caratteri. E tutto senza alcun riferimento alla rappresentazione interna dei codici carattere UTF-8 o dover utilizzare librerie di terze parti.

 #include  #include  #include  #include  using namespace std; int main(int argc, char *argv[]) { string str(argv[1]); unsigned int strLen = str.length(); cout << "Length (char-values): " << strLen << '\n'; setlocale(LC_ALL, "en_EN.UTF-8"); unsigned int u = 0; const char *c_str = str.c_str(); unsigned int charCount = 0; while(u < strLen) { u += mblen(&c_str[u], strLen - u); charCount += 1; } cout << "Length (characters): " << charCount << endl; } 

Probabilmente dovresti prendere il consiglio di Omry e cercare in una libreria specializzata per questo. Detto questo, se vuoi solo capire l’algoritmo per farlo, lo posterò qui sotto.

Fondamentalmente, puoi convertire la tua stringa in un formato più ampio, come wchar_t . Nota che wchar_t ha alcuni problemi di portabilità, perché wchar_t dimensioni variabili a seconda della tua piattaforma. Su Windows, wchar_t è 2 byte, e quindi ideale per rappresentare UTF-16. Ma su UNIX / Linux, è quattro byte e viene quindi utilizzato per rappresentare UTF-32. Pertanto, per Windows ciò funzionerà solo se non includi alcun codice Unicode sopra 0xFFFF. Per Linux è ansible includere l’intera gamma di codepoint in un wchar_t . (Fortunatamente, questo problema verrà mitigato con i tipi di carattere Unicode di C ++ 0x.)

Con questo avvertimento annotato, è ansible creare una funzione di conversione utilizzando il seguente algoritmo:

 template  inline OutputIterator convert(const unsigned char* it, const unsigned char* end, OutputIterator out) { while (it != end) { if (*it < 192) *out++ = *it++; // single byte character else if (*it < 224 && it + 1 < end && *(it+1) > 127) { // double byte character *out++ = ((*it & 0x1F) << 6) | (*(it+1) & 0x3F); it += 2; } else if (*it < 240 && it + 2 < end && *(it+1) > 127 && *(it+2) > 127) { // triple byte character *out++ = ((*it & 0x0F) << 12) | ((*(it+1) & 0x3F) << 6) | (*(it+2) & 0x3F); it += 3; } else if (*it < 248 && it + 3 < end && *(it+1) > 127 && *(it+2) > 127 && *(it+3) > 127) { // 4-byte character *out++ = ((*it & 0x07) << 18) | ((*(it+1) & 0x3F) << 12) | ((*(it+2) & 0x3F) << 6) | (*(it+3) & 0x3F); it += 4; } else ++it; // Invalid byte sequence (throw an exception here if you want) } return out; } int main() { std::string s = "\u00EAtre"; cout << s.length() << endl; std::wstring output; convert(reinterpret_cast (s.c_str()), reinterpret_cast(s.c_str()) + s.length(), std::back_inserter(output)); cout << output.length() << endl; // Actual length } 

L'algoritmo non è completamente generico, in quanto InputIterator deve essere un char non firmato, pertanto è ansible interpretare ogni byte come avente un valore compreso tra 0 e 0xFF. OutputIterator è generico, (solo così puoi usare uno std :: back_inserter e non preoccuparti dell'allocazione di memoria), ma il suo uso come parametro generico è limitato: in pratica, deve essere prodotto in una matrice di elementi abbastanza grande da rappresentare un Caratteri UTF-16 o UTF-32, come wchar_t , uint32_t o i tipi char32_t C ++ 0x. Inoltre, non ho incluso il codice per convertire sequenze di byte di caratteri superiori a 4 byte, ma dovresti ottenere il punto di come l'algoritmo funziona da ciò che viene pubblicato.

Inoltre, se si desidera solo contare il numero di caratteri, anziché l'output in un nuovo buffer a caratteri ampi, è ansible modificare l'algoritmo in modo da includere un contatore anziché un OutputIterator. O meglio ancora, basta usare la risposta di Marcelo Cantos per contare i primi byte.

Questa è un’implementazione ingenua, ma dovrebbe essere utile per vedere come è fatto:

 std::size_t utf8_length(std::string const &s) { std::size_t len = 0; std::string::const_iterator begin = s.begin(), end = s.end(); while (begin != end) { unsigned char c = *begin; int n; if ((c & 0x80) == 0) n = 1; else if ((c & 0xE0) == 0xC0) n = 2; else if ((c & 0xF0) == 0xE0) n = 3; else if ((c & 0xF8) == 0xF0) n = 4; else throw std::runtime_error("utf8_length: invalid UTF-8"); if (end - begin < n) { throw std::runtime_error("utf8_length: string too short"); } for (int i = 1; i < n; ++i) { if ((begin[i] & 0xC0) != 0x80) { throw std::runtime_error("utf8_length: expected continuation byte"); } } len += n; begin += n; } return len; } 

Vi consiglio di usare UTF8-CPP . È una libreria di sola intestazione per lavorare con UTF-8 in C ++. Con questa libreria, sarebbe simile a questa:

 int LenghtOfUtf8String( const std::string &utf8_string ) { return utf8::distance( utf8_string.begin(), utf8_string.end() ); } 

(Il codice è dalla cima della mia testa.)

prova ad usare una libreria di codifica come iconv . probabilmente ha l’API che vuoi.

un’alternativa consiste nell’implementare il proprio utf8strlen che determina la lunghezza di ciascun punto di codice e itera i codepoint anziché i caratteri.

La libreria CPP UTF-8 ha una funzione che fa proprio questo. Puoi includere la libreria nel tuo progetto (è piccola) o semplicemente guardare la funzione. http://utfcpp.sourceforge.net/

 char* twochars = "\xe6\x97\xa5\xd1\x88"; size_t dist = utf8::distance(twochars, twochars + 5); assert (dist == 2); 

Questo codice sto eseguendo il porting da php-iconv a c ++, devi prima usare iconv, spero utile:

 // porting from PHP // http://lxr.php.net/xref/PHP_5_4/ext/iconv/iconv.c#_php_iconv_strlen #define GENERIC_SUPERSET_NBYTES 4 #define GENERIC_SUPERSET_NAME "UCS-4LE" UInt32 iconvStrlen(const char *str, size_t nbytes, const char* encode) { UInt32 retVal = (unsigned int)-1; unsigned int cnt = 0; iconv_t cd = iconv_open(GENERIC_SUPERSET_NAME, encode); if (cd == (iconv_t)(-1)) return retVal; const char* in; size_t inLeft; char *out; size_t outLeft; char buf[GENERIC_SUPERSET_NBYTES * 2] = {0}; for (in = str, inLeft = nbytes, cnt = 0; inLeft > 0; cnt += 2) { size_t prev_in_left; out = buf; outLeft = sizeof(buf); prev_in_left = inLeft; if (iconv(cd, &in, &inLeft, (char **) &out, &outLeft) == (size_t)-1) { if (prev_in_left == inLeft) { break; } } } iconv_close(cd); if (outLeft > 0) cnt -= outLeft / GENERIC_SUPERSET_NBYTES; retVal = cnt; return retVal; } UInt32 utf8StrLen(const std::string& src) { return iconvStrlen(src.c_str(), src.length(), "UTF-8"); }