Estate atento ya que en esta noticia vas a encontrar la solución que buscas.Este escrito ha sido analizado por nuestros especialistas para garantizar la calidad y exactitud de nuestro post.
Solución:
Respuesta corta:
Nunca debes usar wchar_t
en C++ moderno, excepto cuando interactúa con API específicas del sistema operativo (básicamente usa wchar_t
solo para llamar a las funciones de la API de Windows).
Respuesta larga:
El diseño de la biblioteca estándar de C++ implica que solo hay una forma de manejar Unicode: almacenando cadenas codificadas en UTF-8 en matrices de caracteres, ya que casi todas las funciones existen solo en variantes de caracteres (piense en std::exception::what
).
En un programa C++ tienes dos configuraciones regionales:
- Configuración regional estándar de la biblioteca C establecida por
std::setlocale
- Configuración regional estándar de la biblioteca de C++ establecida por
std::locale::global
Desafortunadamente, ninguno de ellos define el comportamiento de las funciones estándar que abren archivos (como std::fopen
, std::fstream::open
etc.). El comportamiento difiere entre los sistemas operativos:
- Linux es independiente de la codificación, por lo que esas funciones simplemente pasan char string a la llamada del sistema subyacente
- En el carácter de Windows string se convierte en ancho string utilizando la configuración regional específica del usuario antes de realizar la llamada al sistema
Por lo general, todo funciona bien en Linux, ya que todos usan configuraciones regionales basadas en UTF-8, por lo que todas las entradas y argumentos del usuario pasan a main
Las funciones estarán codificadas en UTF-8. Pero es posible que aún deba cambiar las configuraciones regionales actuales a las variantes UTF-8 explícitamente, ya que, de manera predeterminada, el programa C ++ comienza a usar el valor predeterminado "C"
lugar. En este punto, si solo le interesa Linux y no necesita compatibilidad con Windows, puede usar matrices de caracteres y std::string
asumiendo que son secuencias UTF-8 y todo “simplemente funciona”.
Los problemas aparecen cuando desea admitir Windows, ya que allí siempre tiene una tercera configuración regional adicional: la configurada para el usuario actual que se puede configurar en algún lugar del “Panel de control”. El problema principal es que esta configuración regional nunca es una configuración regional Unicode, por lo que es imposible usar funciones como std::fopen(const char *)
y std::fstream::open(const char *)
para abrir un archivo utilizando la ruta Unicode. En Windows, tendrá que usar envoltorios personalizados que usan funciones específicas de Windows no estándar como _wfopen
, std::fstream::open(const wchar_t *)
en Windows Puede consultar Boost.Nowide (aún no incluido en Boost) para ver cómo se puede hacer esto: http://cppcms.com/files/nowide/html/
Con C++ 17 puedes usar std::filesystem::path
para almacenar la ruta del archivo de forma portátil, pero aún no funciona en Windows:
- Constructor implícito
std::filesystem::path::path(const char *)
usa la configuración regional específica del usuario en MSVC y no hay forma de hacer que use UTF-8. Funciónstd::filesystem::u8string
debe usarse para construir una ruta desde UTF-8 stringpero es demasiado fácil olvidarse de esto y usar una construcción implícita en su lugar. std::error_category::message(int)
para ambas categorías de error devuelve la descripción del error utilizando la codificación específica del usuario.
Así que lo que tenemos en Windows es:
- Las funciones de biblioteca estándar que abren archivos están rotas y nunca deben usarse.
- Argumentos pasados a
main(int, char**)
están rotas y nunca deben usarse. - Las funciones de WinAPI que terminan en *A y las macros no funcionan y nunca deben usarse.
std::filesystem::path
está parcialmente roto y nunca debe usarse directamente.- Categorías de error devueltas por
std::generic_category
ystd::system_category
están rotas y nunca deben usarse.
Si necesita una solución a largo plazo para un proyecto no trivial, le recomendaría:
- Usando Boost.Nowide o implementando una funcionalidad similar directamente: esta biblioteca estándar rota fija.
- Reimplementación de las categorías de error estándar devueltas por
std::generic_category
ystd::system_category
para que siempre devuelvan cadenas codificadas en UTF-8. - Envase
std::filesystem::path
para que la nueva clase siempre use UTF-8 al convertir la ruta a string y string camino - Envolviendo todas las funciones requeridas de
std::filesystem
para que usen su contenedor de ruta y sus categorías de error.
Desafortunadamente, esto no solucionará los problemas con otras bibliotecas que funcionan con archivos, pero el 99% de ellas están rotas de todos modos (no son compatibles con Unicode).
Así es la vida de un programador de C++. Microsoft podría solucionar esto permitiéndonos cambiar el tiempo de ejecución de Windows a la configuración regional basada en UTF-8, pero no lo harán debido a la compatibilidad con versiones anteriores.
Puede consultar este enlace para obtener más explicaciones: http://utf8everywhere.org/
Fundamentalmente, utiliza wchar_t
cuando la codificación tiene más símbolos que un char
puede contener
Antecedentes
los char
type tiene capacidad suficiente para contener cualquier carácter (codificación) en el juego de caracteres ASCII.
El problema es que muchos idiomas requieren más codificaciones que las cuentas ASCII. Entonces, en lugar de 127 codificaciones posibles, se necesitan más. Algunos idiomas tienen más de 256 codificaciones posibles. A char
type no garantiza un rango superior a 256. Por lo tanto, se requiere un nuevo tipo de datos.
los wchar_t
también conocido como caracteres anchos, proporciona más espacio para codificaciones.
Resumen
Utilizar char
tipo de datos cuando el rango de codificaciones es 256 o menos, como ASCII. Utilizar wchar_t
cuando necesite capacidad para más de 256.
Prefiere Unicode para manejar juegos de caracteres grandes (como emojis).
Nunca usar wchar_t
.
Cuando sea posible, use (algún tipo de array de) char
tal como std::string
y asegúrese de que esté codificado en UTF-8.
Cuando deba interactuar con API que no hablan UTF-8, use char16_t
o char32_t
. Nunca los use de otra manera; solo brindan ventajas ilusorias y fomentan el código defectuoso.
Tenga en cuenta que hay mucho de los casos en que más de una char32_t
se requiere para representar un solo carácter visible para el usuario. OTOH, usando UTF-8 con char
te obliga a manejar el ancho variable muy temprano.