txt procesamiento manejo leer guardar funciones datos crear con archivos archivo c++ file unicode wofstream

procesamiento - ¿Por qué el flujo de archivos ancho en C++ restringe los datos escritos de forma predeterminada?



manejo de archivos en c++ fstream (5)

El modelo utilizado por C ++ para juegos de caracteres se hereda de C, por lo que se remonta a al menos 1989.

Dos puntos principales:

  • IO se realiza en términos de char.
  • es el trabajo de la configuración regional determinar cómo se serializan los caracteres anchos
  • la configuración regional predeterminada (llamada "C") es muy mínima (no recuerdo las restricciones de la norma, aquí es capaz de manejar solo ASCII de 7 bits como un conjunto de caracteres estrecho y amplio).
  • hay un entorno determinado determinado como ""

Así que para obtener algo, tienes que establecer la configuración regional.

Si utilizo el programa simple

#include <locale> #include <fstream> #include <ostream> #include <iostream> int main() { wchar_t c = 0x00FF; std::locale::global(std::locale("")); std::wofstream os("test.dat"); os << c << std::endl; if (!os) { std::cout << "Output failed/n"; } }

que utilizan la configuración regional del entorno y generan el carácter ancho del código 0x00FF en un archivo. Si pido usar la configuración regional "C", obtengo

$ env LC_ALL=C ./a.out Output failed

la configuración regional no ha podido manejar el carácter ancho y se nos notifica el problema ya que el IO falló. Si ejecuto pregunto en un locale UTF-8, obtengo

$ env LC_ALL=en_US.utf8 ./a.out $ od -t x1 test.dat 0000000 c3 bf 0a 0000003

(od -t x1 acaba de volcar el archivo representado en hexadecimal), exactamente lo que espero para un archivo codificado en UTF-8.

Honestamente, no obtengo la siguiente decisión de diseño en la biblioteca estándar de C ++. Cuando se escriben caracteres anchos en un archivo, wofstream convierte wchar_t en caracteres char :

#include <fstream> #include <string> int main() { using namespace std; wstring someString = L"Hello StackOverflow!"; wofstream file(L"Test.txt"); file << someString; // the output file will consist of ASCII characters! }

Soy consciente de que esto tiene que ver con el codecvt estándar. Hay codecvt para utf8 en Boost . Además, hay un codecvt para utf16 por Martin York aquí en SO . La pregunta es ¿por qué el standard codecvt convierte caracteres anchos? ¿Por qué no escribir los personajes como son?

Además, ¿obtendremos unicode streams reales con C ++ 0x o me estoy perdiendo algo aquí?


Mira esto: Class basic_filebuf

Puede modificar el comportamiento predeterminado configurando un búfer de caracteres ancho , utilizando pubsetbuf. Una vez que hiciste eso, la salida será wchar_t y no char.

En otras palabras para tu ejemplo tendrás:

wofstream file(L"Test.txt", ios_base::binary); //binary is important to set! wchar_t buffer[128]; file.rdbuf()->pubsetbuf(buffer, 128); file.put(0xFEFF); //this is the BOM flag, UTF16 needs this, but mirosoft''s UNICODE doesn''t, so you can skip this line, if any. file << someString; // the output file will consist of unicode characters! without the call to pubsetbuf, the out file will be ANSI (current regional settings)


No sé acerca de wofstream. Pero C ++ 0x incluirá nuevos tipos de caracteres (char16_t, char32_t) de ancho y firmeza garantizados (sin signo) que se pueden usar de manera portátil para UTF-8, UTF-16 y UTF-32. Además, habrá nuevos literales de cadena (u "¡Hola!" Para un literal de cadena codificado en UTF-16, por ejemplo)

Echa un vistazo al borrador más reciente de C ++ 0x (N2960) .


Para tu primera pregunta, esta es mi suposición.

La biblioteca IOStreams se construyó bajo un par de instalaciones con respecto a las codificaciones. Para convertir entre Unicode y otras codificaciones no tan habituales, por ejemplo, se supone que.

  • Dentro de su programa, debe usar una codificación de ancho de caracteres (ancho fijo).
  • Solo el almacenamiento externo debe usar codificaciones multibyte (de ancho variable).

Creo que esa es la razón de la existencia de las dos especializaciones de plantillas de std :: codecvt. Uno que asigna entre tipos de caracteres (tal vez simplemente esté trabajando con ASCII) y otro que asigna entre wchar_t (interno a su programa) y caracteres (dispositivos externos). Por lo tanto, siempre que necesite realizar una conversión a una codificación multibyte, debe hacerlo byte por byte. Observe que puede escribir una faceta que maneja el estado de codificación cuando lee / escribe cada byte desde / a la codificación multibyte.

Pensando de esta manera, el comportamiento del estándar C ++ es comprensible. Después de todo, estás usando codificación ASCII de caracteres anchos (suponiendo que este sea el valor predeterminado en tu plataforma y no hayas cambiado las configuraciones regionales). La conversión "natural" sería convertir cada carácter ASCII de caracteres anchos a un carácter ASCII ordinario (en este caso, un carácter). (La conversión existe y es directa.)

Por cierto, no estoy seguro de saberlo, pero puede evitarlo creando una faceta que devuelva noconv para las conversiones. Entonces, tendrías tu archivo con caracteres anchos.


Una respuesta muy parcial para la primera pregunta: un archivo es una secuencia de bytes, por lo que, cuando se trata de wchar_t , al menos debe ocurrir una conversión entre wchar_t y char . Hacer esta conversión "inteligentemente" requiere el conocimiento de las codificaciones de caracteres, por lo que se permite que esta conversión sea dependiente del entorno local, en virtud del uso de una faceta en el entorno local del flujo.

Entonces, la pregunta es cómo se debe hacer esa conversión en la única configuración regional requerida por el estándar: la "clásica". No hay una respuesta "correcta" para eso, y el estándar es muy vago al respecto. Por su pregunta, entiendo que usted asume que la conversión a ciegas (o memcpy () - ing) entre wchar_t [] y char [] hubiera sido una buena manera. Esto no es irrazonable, y de hecho es lo que se hace (o al menos se hizo) en algunas implementaciones.

Otro punto de vista sería que, dado que un codecvt es una faceta de locale, es razonable esperar que la conversión se realice utilizando la "codificación de locale" (aquí tengo una mano, ya que el concepto es bastante difuso). Por ejemplo, uno esperaría que un local turco use ISO-8859-9, o un japonés que use Shift JIS. Por similitud, la configuración regional "clásica" se convertiría a esta "codificación de configuración regional". Aparentemente, Microsoft optó simplemente por recortar (lo que lleva a IS-8859-1 si asumimos que wchar_t representa a UTF-16 y nos quedamos en el plano multilingüe básico), mientras que la implementación de Linux que conozco se decidió por ASCII.

Para su segunda pregunta:

Además, ¿obtendremos transmisiones Unicode reales con C ++ 0x o me estoy perdiendo algo aquí?

En la sección [locale.codecvt] de n2857 (el último borrador de C ++ 0x que tengo a mano), se puede leer:

El codecvt<char16_t, char, mbstate_t> especialización codecvt<char16_t, char, mbstate_t> convierte entre los esquemas de codificación UTF-16 y UTF-8, y el codecvt <char32_t, char, mbstate_t> especialización codecvt <char32_t, char, mbstate_t> convierte entre los esquemas de codificación UTF-32 y UTF-8. codecvt<wchar_t,char,mbstate_t> convierte entre los juegos de caracteres nativos para caracteres estrechos y anchos.

En la sección [locale.stdcvt], encontramos:

Para la faceta codecvt_utf8 : - La faceta se convertirá entre secuencias multibyte UTF-8 y UCS2 o UCS4 (según el tamaño de Elem) dentro del programa. [...]

Para la faceta codecvt_utf16 : - La faceta se convertirá entre secuencias multibyte UTF-16 y UCS2 o UCS4 (según el tamaño de Elem) dentro del programa. [...]

Para la faceta codecvt_utf8_utf16 : - La faceta debe convertir entre secuencias multibyte UTF-8 y UTF-16 (uno o dos códigos de 16 bits) dentro del programa.

Así que supongo que esto significa "sí", pero tendrías que ser más precisos acerca de lo que quieres decir con "transmisiones reales de Unicode" para estar seguro.