2010-01-12 22 views
34

Estoy empezando una programación para manejar nombres de archivos con nombres no ingleses en un sistema WinXP. He hecho algunas lecturas recomendadas en Unicode y creo que tengo la idea básica, pero algunas partes aún no son muy claras para mí.¿Qué codificación tienen los nombres de archivo en NTFS almacenados como?

Específicamente, ¿qué codificación (UTF-8, UTF-16LE/BE) son los archivos nombres (no el contenido, sino el nombre real del archivo) almacenados en NTFS? ¿Es posible abrir cualquier archivo usando fopen(), que toma un char *, o no tengo otra opción que usar wfopen(), que usa un wchar_t *, y presumiblemente toma una cadena UTF-16?

He intentado alimentar manualmente en una cadena codificada en UTF-8 a fopen(), por ej.

unsigned char filename[] = {0xEA, 0xB0, 0x80, 0x2E, 0x74, 0x78, 0x74, 0x0}; // 가.txt 

FILE* f = fopen((char*)filename, "wb+"); 

pero esto salió como 'ê °' .txt '.

Tenía la impresión (que puede ser incorrecta) de que una cadena con codificación UTF8 sería suficiente para abrir cualquier nombre de archivo en Windows, porque parece recordar vagamente alguna aplicación de Windows que pasa (char *), no (wchar_t *), y no tener problemas.

¿Alguien puede arrojar algo de luz sobre esto?

+0

El comportamiento de PHP ha cambiado desde PHP 7.1 en adelante, consulte https://stackoverflow.com/a/38466772/680382 – gogowitsch

Respuesta

32

NTFS almacena nombres de archivo en UTF16, sin embargo fopen usa ANSI (no utf8).

Para utilizar un nombre de archivo codificado en UTF16, deberá utilizar las versiones Unicode de las llamadas abiertas al archivo. Haga esto definiendo UNICODE y _UNICODE en su proyecto. Luego use la llamada CreateFile o la llamada wfopen.

+10

Si cambiar el proyecto para compilar con UNICODE definido es un cambio demasiado grande, puede invocar 'wfopen() 'o' CreateFileW() 'en una compilación no unicode. –

+1

Dado que Windows NT y NTFS son más antiguos que el estándar UTF-16, ¿es posible utilizar el UCS-2 anterior en su lugar? – hillu

+3

NTFS permite cualquier secuencia de valores de 16 bits para la codificación de nombres, excepto 0x0000. Esto significa que los puntos de código UTF-16 son compatibles, pero el sistema de archivos no verifica si una secuencia es válida UTF-16. \ [[fuente] (https://en.wikipedia.org/wiki/NTFS#Internals) \] – user

13

fopen() - en MSVC en Windows no toma (de manera predeterminada) un char * codificado para utf-8.

Desafortunadamente utf-8 se inventó bastante recientemente en el gran esquema de cosas. Las API de Windows están divididas en versiones de Unicode y Ansi. cada windows api que toma o trata con cadenas está realmente disponible con un sufijo W o A - W para caracteres "Anchos"/Unicode y A para Ansi. La magia de macro oculta todo esto lejos del desarrollador, así que simplemente llama a CreateFile con un char * o un wchar_t * según la configuración de tu compilación sin saber la diferencia.

La codificación 'Ansi' no es en realidad una codificación específica: - Pero significa que la codificación utilizada para las cadenas "char" es específica de la configuración regional de la PC.

Ahora, debido a que las funciones c-runtime, como fopen, deben funcionar de manera predeterminada sin el conocimiento del desarrollador, en los sistemas Windows esperan recibir sus cadenas en la codificación local de Windows. msdn indica que el aplo setlocal de c-runtime de microsoft puede cambiar la configuración regional del hilo actual, pero específicamente dice que fallará para cualquier configuración regional que necesite más de 2 bytes por carácter, como utf-8.

Por lo tanto, en Windows no hay atajos. Usted necesita para usar wfopen, o la API nativa CreateFileW (o cree su proyecto usando la configuración de compilación Unicode y simplemente llame a Createfile) con cadenas wchar_t *.

+0

En realidad, hay un atajo: puede convertir la cadena UTF-8 a Unicode, crear un "short" solo ASCII nombre de ruta de acceso "using [GetShortPathNameW] (http://msdn.microsoft.com/en-us/library/windows/desktop/aa364989 (v = vs.85) .aspx), y pasarlo a' fopen'. Esta es la única forma de pasar nombres de archivos que no sean ASCII a bibliotecas heredadas (o aquellas escritas en C portátil) que solo usan 'fopen' para abrir archivos. – user4815162342

3

Como contestaron otros, la mejor manera de manejar cadenas codificadas en UTF-8 es convertirlas a Unicode y usar API Unicode nativas como _wfopen o CreateFileW.

Sin embargo, este enfoque no ayudará al llamar incondicionalmente a las bibliotecas que usan fopen() porque no son compatibles con Unicode o porque están escritas en C portátil. En ese caso, aún es posible utilizar el legado "short" caminos" para convertir una cadena con codificación UTF-8 en una forma utilizable ASCII con fopen, pero requiere un poco de trabajo de campo:

  1. convertir la representación UTF-8 a UTF-16 utilizando MultiByteToWideChar.

  2. Utilice GetShortPathNameW para obtener una "ruta corta" que es solo ASCII. GetShortPathNameW lo devolverá como una cadena ancha con contenido totalmente ASCII, que deberá convertir trivialmente en una cadena estrecha mediante una copia sin pérdida cada wchar_tchar.

  3. Pase la ruta corta a fopen() o al código que utilizará eventualmente fopen(). Tenga en cuenta que los mensajes de error impresos por ese código, si los hubiera, se referirán a la antiestética "ruta corta" (por ejemplo, KINTO~1 en lugar de kinto-un-筋斗雲).

Si bien esto no es exactamente una estrategia recomendada a largo plazo, como los caminos más cortos de Windows son una característica legado que se puede apagar por volumen, es probable que la única manera de pasar los nombres de archivo de código que utiliza fopen() y otras llamadas a API relacionadas con archivos (stat, access, versiones ANSI de CreateFile y similares).

+1

¡Maravilloso, nos salvaste, MUCHAS GRACIAS! – Eric

Cuestiones relacionadas