libreria - sort algorithm c++
¿Obtener la longitud real de un std:: string codificado en UTF-8? (9)
my std :: string está codificado en utf-8, así que obviamente, str.length () devuelve el resultado incorrecto
Encontré esta información pero no estoy seguro de cómo puedo usarla para hacer esto:
Las siguientes secuencias de bytes se utilizan para representar un carácter. La secuencia que se utilizará depende del número de código UCS del carácter:
0x00000000 - 0x0000007F: 0xxxxxxx 0x00000080 - 0x000007FF: 110xxxxx 10xxxxxx 0x00000800 - 0x0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx 0x00010000 - 0x001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
¿Cómo puedo encontrar la longitud real de un std :: string codificado en UTF-8? Gracias
C ++ no sabe nada acerca de las codificaciones, por lo que no puede esperar usar una función estándar para hacer esto.
Esto (así como la mayoría de las respuestas) parecía faltar, pero desafortunadamente como nuevo usuario no puedo desestimarlos. La biblioteca estándar reconoce la existencia de codificaciones de caracteres, en forma de locales. Si su sistema admite una configuración regional, es muy fácil utilizar la biblioteca estándar para calcular la longitud de una cadena. En el código de ejemplo a continuación, asumo que su sistema admite la configuración regional en_EN.UTF-8. Si compilo el código y lo ejecuto como "./a.out ー ニ Sony", la salida es que había 13 valores de carácter y 7 caracteres. Y todo sin ninguna referencia a la representación interna de los códigos de caracteres UTF-8 o tener que usar bibliotecas de terceros.
#include <clocale>
#include <cstdlib>
#include <iostream>
#include <string>
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;
}
Cuente todos los primeros bytes (los que no coinciden con 10xxxxxx).
int len = 0;
while (*s) len += (*s++ & 0xc0) != 0x80;
Esta es una implementación ingenua, pero debería ser útil para que veas cómo se hace esto:
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;
}
Este código que estoy transfiriendo de php-iconv a c ++, debe usar iconv primero, espero que sea útil:
// 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");
}
La biblioteca CPP UTF-8 tiene una función que hace precisamente eso. Puede incluir la biblioteca en su proyecto (es pequeño) o simplemente mirar la función. UTF8-CPP
char* twochars = "/xe6/x97/xa5/xd1/x88";
size_t dist = utf8::distance(twochars, twochars + 5);
assert (dist == 2);
Probablemente debería tomar el consejo de Omry y buscar una biblioteca especializada para esto. Dicho esto, si solo quieres entender el algoritmo para hacer esto, lo publicaré a continuación.
Básicamente, puede convertir su cadena en un formato de elemento más amplio, como wchar_t
. Tenga en cuenta que wchar_t
tiene algunos problemas de portabilidad, ya que wchar_t
es de tamaño variable dependiendo de su plataforma. En Windows, wchar_t
es de 2 bytes, y por lo tanto es ideal para representar UTF-16. Pero en UNIX / Linux, tiene cuatro bytes y, por lo tanto, se utiliza para representar a UTF-32. Por lo tanto, para Windows esto solo funcionará si no incluye ningún punto de código Unicode por encima de 0xFFFF. Para Linux puede incluir toda la gama de puntos de código en un wchar_t
. (Afortunadamente, este problema se mitigará con los tipos de caracteres C ++ 0x Unicode).
Con esa advertencia anotada, puedes crear una función de conversión usando el siguiente algoritmo:
template <class OutputIterator>
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<const unsigned char*> (s.c_str()),
reinterpret_cast<const unsigned char*>(s.c_str()) + s.length(), std::back_inserter(output));
cout << output.length() << endl; // Actual length
}
El algoritmo no es totalmente genérico, porque el InputIterator debe ser un carácter sin signo, por lo que puede interpretar que cada byte tiene un valor entre 0 y 0xFF. El OutputIterator es genérico, (solo para que pueda usar std :: back_inserter y no se preocupe por la asignación de memoria), pero su uso como parámetro genérico es limitado: básicamente, debe enviarse a una serie de elementos lo suficientemente grandes como para representar una Caracteres UTF-16 o UTF-32, como wchar_t
, uint32_t
o los tipos C ++ 0x char32_t
. Además, no incluí el código para convertir secuencias de bytes de caracteres de más de 4 bytes, pero debería obtener el punto de cómo funciona el algoritmo a partir de lo publicado.
Además, si solo desea contar el número de caracteres, en lugar de enviar a un nuevo búfer de caracteres anchos, puede modificar el algoritmo para incluir un contador en lugar de un OutputIterator. O mejor aún, simplemente use la respuesta de Marcelo Cantos para contar los primeros bytes.
Te recomiendo usar UTF8-CPP . Es una biblioteca de solo encabezado para trabajar con UTF-8 en C ++. Con esta lib, se vería algo así:
int LenghtOfUtf8String( const std::string &utf8_string )
{
return utf8::distance( utf8_string.begin(), utf8_string.end() );
}
(El código es de la parte superior de mi cabeza.)
Uno de los proyectos a los que contribuyo tiene una pequeña función que hace eso:
Busque Utf8StringSize
. Depende de otra pequeña función en el mismo archivo de encabezado.
intenta usar una biblioteca de codificación como iconv . probablemente tiene la api que quieres.
una alternativa es implementar su propio utf8strlen que determina la longitud de cada punto de código y los puntos de código de iteración en lugar de los caracteres.