2010-10-29 21 views
9

Si escribo una cadena al azar en un archivo en C++ que consta de algunos caracteres Unicode, mi editor de texto me dice que no he creado un archivo UTF-8 válido.Unicode y std :: string en C++

// Code example 
const std::string charset = "abcdefgàèíüŷÀ"; 
file << random_string(charset); // using std::fstream 

¿Qué puedo hacer para resolver esto? ¿Tengo que hacer mucha codificación manual adicional? Tal como lo entiendo, std :: string no se preocupa por la codificación, solo los bytes, así que cuando le paso una cadena Unicode y la escribo en un archivo, seguramente ese archivo debería contener los mismos bytes y ser reconocido como un UTF- 8 archivo codificado?

+3

¿Está buscando en std :: wstring? – Chubsdad

+0

Solo una suposición descabellada: ¿podría ser que su función 'random_string' está insertando accidentalmente valores nulos debido a un error de uno por uno con la cadena de caracteres? –

+0

@Charles: Eso sería como yo :) Pero lo dudo, ya que el constructor std :: string descarta el nulo del literal de la cadena, y la función random_string simplemente selecciona un carácter aleatorio de la cadena de caracteres. – Oystein

Respuesta

15

random_string es probable que sea el culpable; Me pregunto cómo se implementa. Si la cadena es de hecho UTF-8 codificados y random_string parece

std::string random_string(std::string const &charset) 
{ 
    const int N = 10; 
    std::string result(N); 
    for (int i=0; i<N; i++) 
     result[i] = charset[rand() % charset.size()]; 
    return result; 
} 

Seguidamente, tomará al azar char s de charset, que en UTF-8 (como otros críticos han señalado) no son puntos de código Unicode, pero simples bytes. Si selecciona un byte aleatorio del medio de un carácter multibyte UTF-8 como primer byte (o lo pone después de un carácter compatible con ASCII de 7 bits), entonces su salida no será válida UTF-8. Ver Wikipedia y RFC 3629.

La solución podría ser transform hacia y desde UTF-32 en random_string. Creo que wchar_t y std::wstring usan UTF-32 en Linux. UTF-16 también sería seguro, siempre y cuando permanezca dentro del Basic Multilingual Plane.

+1

Entonces, si una std :: cadena llamada "str" ​​contiene "àỳ", str [0] no devolverá "à"? Y str [1] no devolverá "ỳ"? – Oystein

+3

No, devolverá el primer byte en la codificación multibyte para estos caracteres. C++ es una invención de los años 80, diseñada para ser compatible con C (1970) y ASCII (1960), mientras que Unicode y UTF-8 se introdujeron a principios de los 90. UTF-8 fue diseñado para mantener funcionando * la mayoría de * los viejos programas y algoritmos, parece que usaste uno de los algoritmos que se rompen. * Si * esto es más o menos lo que 'random_string' hace. –

+0

Lo es. Supongo que esto significa que siempre que quiera manipular una cadena Unicode, debo usar un wstring. Voy a leer sobre problemas de portabilidad y tal. De todos modos, respuesta aceptada. – Oystein

0

Para escribir UTF-8, necesita utilizar una faceta codecvt como this one. Se puede ver un ejemplo de cómo usarlo here.

+1

Se usan para convertir wchar_t (UTF-16/UTF-32) en UTF-8. Como la cadena ya es UTF-8, no se requiere conversión. –

+0

@Martin: no hay garantía de que la cadena sea UTF-8. Si el archivo fuente se guardó usando la página de códigos 437, el carácter 'à' será un byte único con el valor 133. (En Unicode,' à' se representa por el punto de código U + 00E0, que UTF-8 codifica como el byte secuencia [0xc3, 0xa0].) –

1

En su ejemplo de código, std::string charset almacena lo que escribe. Es decir, si ha usado un editor de texto UTF-8 para escribir esto, lo que recibirá en la salida del archivo sería exactamente ese texto UTF-8.

UTF-8 es solo un esquema de codificación en el que diferentes caracteres utilizan diferentes tamaños de bytes. Sin embargo, si usa un editor UTF-8, codificará, dirá 'ñ' con dos bytes, y, cuando lo escriba en el archivo, tendrá esos dos bytes (siendo nuevamente compatible con UTF-8).

El problema puede ser el editor que utilizó para crear el archivo fuente C++. Puede usar latin1 o alguna otra codificación.

+0

Sí, pensé en eso, pero el editor está en modo UTF-8 – Oystein

10

¿Qué puedo hacer para solucionar esto? ¿Tengo para hacer muchas codificaciones manuales adicionales de ? De la forma en que lo entiendo, std :: string no se preocupa por la codificación , solo los bytes, así que cuando I le pasa una cadena Unicode y lo escribe en el archivo, seguramente ese archivo debe contener los mismos bytes y ser reconocido como un archivo codificado UTF-8?

Tiene la razón que std::string está codificando agnóstico. Simplemente tiene una matriz de elementos char. La forma en que se interpretan estos elementos char como texto depende del entorno. Si su configuración regional no está configurada en alguna forma de Unicode (es decir, UTF-8 o UTF-16), cuando imprima una cadena, no se mostrará/interpretará como Unicode.

¿Seguro de su cadena literal "abcdefgàèíüŷÀ" es en realidad Unicode y no, por ejemplo, Latin-1? (ISO-8859-1 o posible Windows-1252)? Debe determinar en qué configuración regional está configurada su plataforma actualmente.

----------- ----------- EDITAR

Creo que sé su problema: algunos de esos caracteres Unicode en la cadena literal charset , como el carácter acentuado "À", son caracteres de dos bytes (suponiendo una codificación UTF-8). Cuando direcciona la cadena del juego de caracteres utilizando el operador [] en su función random_string, está devolviendo la mitad de un carácter Unicode. Por lo tanto, la función random-string crea una cadena de caracteres no válida.

Por ejemplo, considere el siguiente código:

std::string s = "À"; 
std::cout << s.length() << std::endl; 

En un entorno donde la cadena literal se interpreta como UTF-8, este programa es la salida 2. Por lo tanto, el primer carácter de la cadena (s[0]) es solo medio de un carácter Unicode y, por lo tanto, no es válido. Como su función random_string está direccionando la cadena en bytes individuales usando el operador [], está creando cadenas aleatorias no válidas.

Así que sí, necesita usar std::wstring, y cree su serie de caracteres-literal usando el prefijo L.

+0

Este es probablemente el problema, ya que he podido leer una cadena Unicode desde un archivo (codificado en UTF) -8) en std :: string y lo envía a un archivo diferente.Lo miraré. – Oystein

+0

Sí, creo que esto es todo. Ver mi respuesta –

+0

Y esta es exactamente la razón por la que dije que no puedes almacenar codificaciones multibyte en 'std :: string'. Pero por alguna razón me volví downvoted al olvido. –

Cuestiones relacionadas