wchar_t convert c++ char wchar

c++ - convert - problemas de conversión de wchar_t*a char*



char to wchar (3)

Para hacer lo que intentas hacer de la manera correcta, hay varias cosas no triviales que debes tener en cuenta. Haré todo lo posible para desglosarlos por ti aquí.

Comencemos con la definición del parámetro count de la documentación de la función wcstombs() en MSDN :

El número máximo de bytes que se pueden almacenar en la cadena de salida de multibyte.

Tenga en cuenta que esto NO dice nada sobre el número de caracteres anchos en la cadena de entrada de caracteres anchos. Aunque todos los caracteres anchos en su cadena de entrada de ejemplo ("Nuevo archivo de texto.txt") pueden representarse como caracteres ASCII de un solo byte, no podemos suponer que cada carácter ancho en la cadena de entrada generará exactamente un byte en la salida cadena para cada cadena de entrada posible (si esta afirmación lo confunde, debe consultar el artículo de Joel sobre Unicode y conjuntos de caracteres ). Entonces, si pasa wcstombs() el tamaño del búfer de salida, ¿cómo sabe cuánto tiempo dura la cadena de entrada? La documentación indica que se espera que la cadena de entrada sea terminada en nulo, según la convención estándar de lenguaje C:

Si wcstombs encuentra el carácter nulo de carácter ancho (L ''/ 0'') antes o cuando se produce el recuento , lo convierte en un 0 de 8 bits y se detiene.

Aunque esto no se menciona explícitamente en la documentación, podemos inferir que si la cadena de entrada no es terminada en nulo, wcstombs() seguirá leyendo caracteres anchos hasta que haya escrito bytes de count en la cadena de salida. Entonces, si se trata de una cadena de caracteres anchos que no tiene terminación nula, no es suficiente con saber cuánto tiempo dura la cadena de entrada; de alguna manera debería saber exactamente cuántos bytes debería ser la cadena de salida (lo cual es imposible de determinar sin realizar la conversión) y pasar eso como el parámetro de count para hacer que wcstombs() haga lo que usted quiere que haga.

¿Por qué me estoy concentrando tanto en este problema de terminación nula? Debido a que la FILE_NOTIFY_INFORMATION estructura FILE_NOTIFY_INFORMATION en MSDN tiene esto que decir sobre su campo FileName :

Un campo de longitud variable que contiene el nombre del archivo relativo al identificador del directorio. El nombre del archivo está en formato de caracteres Unicode y no tiene terminación nula.

El hecho de que el campo FileName no tenga terminación nula explica por qué tiene un montón de "letras chinas desconocidas" al final cuando lo mira en el depurador. La FILE_NOTIFY_INFORMATION estructura FILE_NOTIFY_INFORMATION también contiene otro nugget de sabiduría con respecto al campo FileNameLength :

El tamaño de la porción del nombre de archivo del registro, en bytes.

Tenga en cuenta que esto dice bytes , no caracteres . Por lo tanto, incluso si quisiera suponer que cada carácter ancho en la cadena de entrada generará exactamente un byte en la cadena de salida, no debe pasar fileInfo.FileNameLength para count ; Debería pasar fileInfo.FileNameLength / sizeof(WCHAR) (o usar una cadena de entrada terminada en nulo, por supuesto). Al unir toda esta información, finalmente podemos entender por qué su llamada original a wcstombs() estaba fallando: estaba leyendo más allá del final de la cadena y ahogado en datos no válidos (desencadenando así el error EILSEQ ).

Ahora que hemos elucidado el problema, es hora de hablar sobre una posible solución. Para hacer esto de la manera correcta, lo primero que debe saber es qué tan grande debe ser su buffer de salida. Afortunadamente, hay un tidbit final en la documentación de wcstombs() que nos ayudará aquí:

Si el argumento mbstr es NULL, wcstombs devuelve el tamaño requerido en bytes de la cadena de destino.

Entonces la forma idiomática de usar la función wcstombs() es llamarla dos veces: la primera vez para determinar qué tan grande debe ser el buffer de salida, y la segunda vez para hacer la conversión. Lo último a tener en cuenta es que, como dijimos anteriormente, la cadena de entrada de caracteres anchos debe terminar en nulo al menos para la primera llamada a wcstombs() .

Poniendo todo esto junto, aquí hay un fragmento de código que hace lo que estás tratando de hacer:

size_t fileNameLengthInWChars = fileInfo.FileNameLength / sizeof(WCHAR); //get the length of the filename in characters WCHAR *pwNullTerminatedFileName = new WCHAR[fileNameLengthInWChars + 1]; //allocate an intermediate buffer to hold a null-terminated version of fileInfo.FileName; +1 for null terminator wcsncpy(pwNullTerminatedFileName, fileInfo.FileName, fileNameLengthInWChars); //copy the filename into a the intermediate buffer pwNullTerminatedFileName[fileNameLengthInWChars] = L''/0''; //null terminate the new buffer size_t fileNameLengthInChars = wcstombs(NULL, pwNullTerminatedFileName, 0); //first call to wcstombs() determines how long the output buffer needs to be char *pFileName = new char[fileNameLengthInChars + 1]; //allocate the final output buffer; +1 to leave room for null terminator wcstombs(pFileName, pwNullTerminatedFileName, fileNameLengthInChars + 1); //finally do the conversion!

Por supuesto, no te olvides de llamar a delete[] pwNullTerminatedFileName y delete[] pFileName cuando hayas terminado de limpiarlos.

UNA ÚLTIMA COSA

Después de escribir esta respuesta, volví a leer tu pregunta un poco más de cerca y pensé en otro error que podrías estar cometiendo. Usted dice que wcstombs() falla después de simplemente convertir las dos primeras letras ("Ne"), lo que significa que está golpeando datos no inicializados en la cadena de entrada después de los primeros dos caracteres anchos. ¿ FILE_NOTIFY_INFORMATION el operador de asignación para copiar una variable FILE_NOTIFY_INFORMATION a otra? Por ejemplo,

FILE_NOTIFY_INFORMATION fileInfo = someOtherFileInfo;

Si hiciera esto, solo copiaría los primeros dos caracteres anchos de someOtherFileInfo.FileName a fileInfo.FileName . Para entender por qué este es el caso, considere la declaración de la estructura FILE_NOTIFY_INFORMATION :

typedef struct _FILE_NOTIFY_INFORMATION { DWORD NextEntryOffset; DWORD Action; DWORD FileNameLength; WCHAR FileName[1]; } FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;

Cuando el compilador genera código para la operación de asignación, no comprende el truco que se está obteniendo con FileName como un campo de longitud variable, por lo que simplemente copia el sizeof(FILE_NOTIFY_INFORMATION) bytes sizeof(FILE_NOTIFY_INFORMATION) de someOtherFileInfo a fileInfo . Como FileName se declara como una matriz de un WCHAR , se podría pensar que solo se copiaría un carácter, pero el compilador rellena la estructura para que tenga dos bytes adicionales (de modo que su longitud es un múltiplo entero del tamaño de un int ), por lo que también se copia un segundo WCHAR .

Tengo un problema con la wchar_t* a char* .

wchar_t* una cadena wchar_t* de la estructura FILE_NOTIFY_INFORMATION , devuelta por la función ReadDirectoryChangesW WinAPI, por lo que asumo que la cadena es correcta.

Supongamos que wchar string es "Nuevo archivo de texto.txt" En el depurador de Visual Studio cuando se pasa el mouse sobre la variable en "N" y algunas letras chinas desconocidas. Aunque en relojes la cuerda se representa correctamente.

Cuando intento convertir wchar a char con wcstombs

wcstombs(pfileName, pwfileName, fileInfo.FileNameLength);

convierte solo dos letras en char* ("Ne") y luego genera un error.

Algún error interno en wcstombs.c en la función _wcstombs_l_helper () en este bloque:

if (*pwcs > 255) /* validate high byte */ { errno = EILSEQ; return (size_t)-1; /* error */ }

No se lanza como excepción.

¿Cual puede ser el problema?


Supongo que la cadena ancha que está pasando no es válida o está definida incorrectamente.

¿Cómo se define pwFileName ? Parece que tiene una estructura FILE_NOTIFY_INFORMATION definida como fileInfo , entonces ¿por qué no está utilizando fileInfo.FileName , como se muestra a continuación?

wcstombs(pfileName, fileInfo.FileName, fileInfo.FileNameLength);


el error que obtienes lo dice todo, encontró un personaje que no puede convertir a MB (porque no tiene representación en MB), fuente :

Si wcstombs encuentra un carácter ancho que no puede convertir a un carácter multibyte, devuelve -1 cast para escribir size_t y establece errno en EILSEQ

En casos como este, debe evitar la entrada ''asumida'' y dar un caso de prueba real que falla.