2010-06-28 25 views
12

ya hay algunas preguntas relacionadas con este problema. Creo que mi pregunta es un poco diferente porque no tengo un problema real, solo estoy pidiendo por interés académico. Sé que la implementación de Windows de UTF-16 a veces es contradictoria con el estándar Unicode (por ejemplo, intercalación) o más cercano al viejo UCS-2 que a UTF-16, pero mantendré la terminología "UTF-16" aquí por razones de sencillez.Cómo generar cadenas Unicode en la consola de Windows

Antecedentes: en Windows, todo es UTF-16. Independientemente de si está tratando con el kernel, el subsistema de gráficos, el sistema de archivos o lo que sea, está pasando cadenas UTF-16. No hay lugares o conjuntos de caracteres en el sentido de Unix. Para compatibilidad con las versiones medievales de Windows, hay una cosa llamada "páginas de códigos" que está obsoleta pero que sin embargo es compatible. AFAIK, solo hay una función correcta y no obsoleta para escribir cadenas en la consola, a saber, WriteConsoleW, que toma una cadena UTF-16. Además, una discusión similar se aplica a las transmisiones de entrada, que también ignoraré.

Sin embargo, creo que esto representa un defecto de diseño en la API de Windows: hay una función genérica que se puede usar para escribir en todos los objetos de flujo (archivos, tuberías, consolas ...) llamados WriteFile, pero esta función es byte- orientado y no acepta cadenas UTF-16. La documentación sugiere usar WriteConsoleW para la salida de la consola, que está orientado al texto, y WriteFile para todo lo demás, que está orientado a bytes. Dado que tanto las secuencias de consola como los objetos de archivo están representados por manejadores de objetos de kernel y las secuencias de consola pueden redirigirse, debe invocar una función para cada escritura en una secuencia de salida estándar que compruebe si el manejador representa una secuencia de consola o un archivo, rompiendo polymorphy. OTOH, creo que la separación de Windows entre cadenas de texto y bytes sin procesar (que se refleja en muchos otros sistemas como Java o Python) es conceptualmente superior al enfoque char* de Unix que ignora las codificaciones y no distingue entre cadenas y matrices de bytes.

Así que mis preguntas son: ¿Qué hacer en esta situación? ¿Y por qué no se resuelve este problema incluso en las propias bibliotecas de Microsoft? Tanto .NET Framework como las bibliotecas C y C++ parecen adherirse al modelo de página de códigos obsoleto. ¿Cómo diseñaría la API de Windows o un marco de aplicación para eludir este problema?

Creo que el problema general (que no es fácil de resolver) es que todas las bibliotecas suponen que todas las secuencias están orientadas por bytes e implementan secuencias orientadas a texto además de eso. Sin embargo, vemos que Windows tiene secuencias especiales orientadas a texto en el nivel del sistema operativo, y las bibliotecas no pueden hacer frente a esto. Entonces, en cualquier caso, debemos introducir cambios significativos en todas las bibliotecas estándar. Una manera rápida y sucia sería tratar la consola como una secuencia orientada a bytes especial que acepta solo una codificación. Esto aún requiere que se eviten las bibliotecas estándar de C y C++ porque no implementan el modificador WriteFile/WriteConsoleW. ¿Es eso correcto?

+2

sentimos, este "problema" suena como una entrada de blog en el encubrimiento ;-) – Philipp

+0

esto podría estar relacionado con mi pregunta: http://superuser.com/questions/157225/even-on-windows-7- can-you-do-a-dir-and-be-able-to-see-filenames-that-has-unico –

Respuesta

5

La estrategia general que utilizo/usamos en la mayoría de las aplicaciones/proyectos (multiplataforma) es: simplemente utilizamos UTF-8 (me refiero al estándar real) en todas partes. Usamos std :: string como contenedor y simplemente interpretamos todo como UTF8. Y también manejamos todos los archivos IO de esta forma, es decir, esperamos UTF8 y guardamos UTF8. En el caso de que obtengamos una cadena de algún lugar y sepamos que no es UTF8, la convertiremos en UTF8.

El caso más común en el que tropezamos con WinUTF16 es para los nombres de archivo. Entonces, para cada manejo de nombre de archivo, siempre convertiremos la cadena UTF8 en WinUTF16. Y también a la inversa si buscamos archivos en un directorio.

La consola no se usa realmente en nuestra compilación de Windows (en la compilación de Windows, todos los resultados de la consola se envuelven en un archivo). Como tenemos UTF8 en todas partes, también nuestra salida de consola es UTF8, lo que está bien para la mayoría de los sistemas modernos.Y también el archivo de registro de la consola de Windows tiene su contenido en UTF8 y la mayoría de los editores de texto en Windows pueden leer eso sin problemas.

Si queremos utilizar el WinConsole más y si nos preocupan mucho de que todos los caracteres especiales se muestran correctamente, que tal vez sería escribir algún manejador de tubería automático que instalamos en el medio fileno=0 y lo real stdout que utilizará WriteConsoleW como usted han sugerido (si realmente no hay una manera más fácil).

Si se pregunta cómo realizar este manipulador automático de tuberías: ya lo hemos implementado para todos los sistemas similares a POSIX. Es probable que el código no funcione en Windows tal como está, pero creo que debería ser posible portarlo. Nuestro controlador de tubería actual es similar a lo que hace tee. Es decir. si hace un cout << "Hello" << endl, ambos se imprimirán en stdout y en algún archivo de registro. Mire the code si le interesa cómo se hace esto.

4

varios puntos:

  1. Una diferencia importante entre Windows "WriteConsoleW" y printf es que WriteConsoleW mira a la consola como interfaz gráfica de usuario que más bien los flujos de texto. Por ejemplo, si lo usa y usa tubería, no capturaría la salida.
  2. Nunca diría que las páginas de códigos están obsoletas. Tal vez los desarrolladores de Windows desearían que fuera así, pero nunca lo serían. Todo el mundo, pero api de Windows, utiliza secuencias orientadas a bytes para representar datos: XML, HTML, HTTP, Unix, etc., etc. usan codificaciones y la más popular y poderosa es UTF-8. Por lo tanto, puede usar cadenas anchas internamente, pero en el mundo externo necesitará algo más.

    Incluso cuando imprime wcout << L"Hello World" << endl es convertido bajo el capó de flujo orientado a bytes, en la mayoría de los sistemas de ventanas (pero) a UTF-8.

  3. Mi opinión personal es que Microsoft cometió un error al cambiar su API en todos los lugares a ancho en lugar de admitir UTF-8 en todas partes. Por supuesto, puedes discutir al respecto. Pero, de hecho, debe separar el texto y las secuencias orientadas a bytes y convertir entre ellas.

+0

1. Microsoft sugiere verificar si la secuencia de salida estándar va a una consola u otra cosa antes de usar WriteConsole. Esto es engorroso, pero parece ser la única opción posible y portátil. 2. Las páginas de códigos y las codificaciones no son lo mismo. Con las páginas de códigos me refiero a las páginas de códigos de la consola de Windows. Dado que la consola de Windows está orientada al texto y usa UTF-16, las páginas de códigos están obsoletas: cada cadena que utiliza una página de códigos se convertirá de inmediato a UTF-16 de todos modos. El problema de 'wostream' es desafortunado, pero lo exige el estándar de C++. 3. No creo que la decisión de usar UTF-16 sea desafortunada ... – Philipp

+0

... comió, pero la API está mal diseñada. Por ejemplo, podría pensar en algo como 'GetStdHandle (STD_UTF16LE_OUTPUT_HANDLE)' que devolvería un manejador de flujo orientado a bytes que espera cadenas codificadas en UTF-16-LE. Entonces podrías usar 'WriteFile' en todas partes. OTOH, creo que el problema de que C y C++ no tienen flujos de texto reales es más importante. – Philipp

+0

Creo que "Todo el mundo, pero api de Windows, utiliza secuencias orientadas a bytes para representar datos" lo está exagerando un poco. Java, C# y JavaScript también hacen todo su manejo de caracteres y cadenas como secuencias orientadas a palabras, UTF-16. – hippietrail

3

para responder a su primera pregunta, puede generar cadenas Unicode a la consola de Windows usando _setmode. Los detalles específicos con respecto a esto se pueden encontrar en Michael Kaplan's blog. Por defecto, la consola no es Unicode (UCS-2/UTF-16). Funciona de manera Ansi (configuración regional/página de códigos) y debe configurarse específicamente para usar Unicode.

Además, debe cambiar la fuente de la consola, ya que la fuente predeterminada solo admite caracteres Ansi. Aquí hay algunas excepciones menores, como caracteres ASCII de extensión cero, pero la impresión de caracteres Unicode reales requiere el uso de _setmode.

En Windows, todo es UTF-16. Independientemente de si está tratando con el kernel, el subsistema de gráficos, el sistema de archivos o lo que sea, está pasando cadenas UTF-16. No hay lugares o conjuntos de caracteres en el sentido de Unix.

Esto no es del todo cierto. Si bien el núcleo subyacente de Windows sí utiliza Unicode, existe una enorme cantidad de interoperabilidad que permite que Windows interactúe con una gran variedad de software.

Considere el bloc de notas (sí, el bloc de notas está lejos de ser un componente central, pero aclara mi punto).Notepad tiene la capacidad de leer archivos que contienen Ansi (su página de códigos actual), Unicode o UTF-8. Puede considerar el bloc de notas como una aplicación Unicode, pero eso no es del todo exacto.

Un mejor ejemplo son los controladores. Drivers puede escribirse en Unicode o Ansi. Realmente depende de la naturaleza de la interfaz. Para avanzar en este punto, Microsoft proporciona la biblioteca StrSafe, que se escribió específicamente con Kernel-mode drivers en mente e incluye both Unicode and Ansi versions. Si bien los controladores son Ansi o Unicode, el kernel de Windows debe interactuar con ellos, correctamente, independientemente de la forma que adopten.

Cuanto más se aleje del núcleo de Windows, más interoperabilidad entrará en juego. Esto incluye code pages and locales. Debe recordar que no todo el software está escrito con Unicode en mente. Visual C++ 2010 todavía tiene el ability para compilar utilizando Ansi, Multi-Byte o Unicode. Esto incluye el uso de code pages y locales, que son parte del estándar C/C++.

Sin embargo, creo que esto representa un defecto de diseño en la API de Windows

los dos artículos siguientes discuten esta bastante bien.

Así que mis preguntas son: ¿Qué hacer en esta situación? ¿Y por qué no se resuelve este problema incluso en las propias bibliotecas de Microsoft? Tanto .NET Framework como las bibliotecas C y C++ parecen adherirse al modelo de página de códigos obsoleto. ¿Cómo diseñaría la API de Windows o un marco de aplicación para eludir este problema?

En este punto, creo que está buscando en Windows en hindsight. Unicode no fue el primero, ASCII lo hizo. Después de ASCII, vino code pages. Después de las páginas de códigos, vino DBCS. Después de DBCS vino MBCS (y eventualmente UTF-8). Después de UTF-8, vino Unicode (UTF-16/UCS-2).

Cada una de estas tecnologías se incorporó al sistema operativo Windows en los últimos años. Cada edificio en el último, pero sin romperse. El software fue escrito con cada uno de estos en mente. Si bien puede no parecerlo a veces, Microsoft pone un huge amount of effort en no rompiendo el software que no escribió. Incluso ahora, puede escribir un nuevo software que aproveche cualquiera de estas tecnologías y funcionará.

La verdadera respuesta aquí es "compatibilidad". Microsoft todavía usa estas tecnologías y también muchas otras compañías. Hay un número incalculable de programas, componentes y bibliotecas que no se han actualizado (o que nunca se actualizarán) para usar Unicode. Incluso cuando surgen tecnologías más nuevas, como .NET, las tecnologías más antiguas deben permanecer. Por lo menos para la interoperabilidad.

Por ejemplo, supongamos que tiene una DLL con la que necesita interactuar desde .NET, pero esta DLL se escribió utilizando Ansi (página de códigos de un solo byte localizada). Para empeorar las cosas, no tienes el origen de la DLL. La única respuesta aquí es usar esas características obsoletas.

0

Cómo registrado correctamente el trabajo es el siguiente:

  • Uso UTF-16 y wchar_t internamente, esto funciona muy bien con los nombres de archivo y las API de Windows en general.
  • Establezca la página de códigos en 65001, que es UTF-8. Esto garantiza que cuando lee archivos de texto sin formato, Windows los comprueba en busca de UTF-16 y BOM, ("el estándar de Windows"), y si no hay BOM, el texto será tratado como UTF-8 ("el estándar mundial") y traducido a UTF-16 para su uso.
Cuestiones relacionadas