46 votes

Quel codage utilisent les noms de fichiers stockés dans NTFS?

Je viens de commencer à travailler sur la programmation pour gérer les noms de fichiers avec des noms non anglais sur un système WinXP. J'ai lu quelques recommandations sur l'unicode et je pense avoir saisi l'idée de base, mais certaines parties ne sont pas très claires pour moi.

Plus précisément, dans quel encodage (UTF-8, UTF-16LE/BE) les noms de fichiers (pas le contenu, mais le nom réel du fichier) sont-ils stockés dans NTFS? Est-il possible d'ouvrir n'importe quel fichier en utilisant fopen(), qui prend un char*, ou n'ai-je pas d'autre choix que d'utiliser wfopen(), qui utilise un wchar_t*, et qui prend probablement une chaîne UTF-16?

J'ai essayé d'introduire manuellement une chaîne encodée en UTF-8 à fopen(), par exemple.

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

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

mais cela est sorti comme '.txt'.

J'étais sous l'impression (qui peut être fausse) qu'une chaîne encodée en UTF8 serait suffisante pour ouvrir n'importe quel nom de fichier sous Windows, car je me souviens vaguement qu'une application Windows passait autour de (char*), pas de (wchar_t*), et qu'il n'y avait pas de problèmes.

Est-ce que quelqu'un peut éclairer ma lanterne à ce sujet?

0 votes

Le comportement de PHP a changé à partir de PHP 7.1, voir stackoverflow.com/a/38466772/680382

0 votes

"J'avais l'impression (qui peut être fausse) qu'une chaîne encodée en UTF-8 suffirait pour ouvrir n'importe quel fichier sous Windows" - Windows NE prend PAS en charge les noms de fichiers encodés en UTF-8, seulement UTF-16 et ANSI (qui est converti en interne en UTF-16). Les noms de fichiers UTF-8 contenant uniquement des caractères ASCII fonctionneront comme des chaînes ANSI, cependant. "Je me souviens vaguement qu'une application Windows passait des (char), pas des (wchar_t), et qu'il n'y avait aucun problème" - char* n'implique pas l'UTF-8, mais peut être utilisé pour cela. Aucune API standard Win32 ou C/C++ de fichiers n'accepte l'UTF-8 en entrée, mais des bibliothèques tierces peuvent le faire.

39voto

villintehaspam Points 4470

NTFS stocke les noms de fichiers en UTF-16, cependant fopen utilise ANSI (pas UTF-8).

Pour utiliser un nom de fichier encodé en UTF-16, vous devrez utiliser les versions Unicode des appels d'ouverture de fichier. Faites cela en définissant UNICODE et _UNICODE dans votre projet. Ensuite, utilisez l'appel CreateFile ou l'appel wfopen.

15 votes

Si le changement du projet pour qu'il soit construit avec UNICODE défini est trop important, vous pouvez appeler wfopen() ou CreateFileW() dans une version non-unicode.

2 votes

Étant donné que Windows NT et NTFS sont plus anciens que la norme UTF-16, est-il possible que le UCS-2 plus ancien soit utilisé à la place?

3 votes

NTFS autorise n'importe quelle séquence de valeurs sur 16 bits pour le codage des noms sauf 0x0000. Cela signifie que les points de code UTF-16 sont supportés, mais le système de fichiers ne vérifie pas si une séquence est valide UTF-16. [source]

15voto

Chris Becke Points 19910

Fopen() - in MSVC on windows does not (by default) take a utf-8 encoded char*.

Malheureusement, l'utf-8 a été inventé assez récemment dans le grand schéma des choses. Les API Windows sont divisées en versions Unicode et Ansi. Chaque API Windows qui prend ou traite des chaînes est en fait disponible avec un suffixe W ou A - W pour "Wide" caractère/Unicode et A pour Ansi. La magie des macros cache tout cela au développeur, vous appelez donc simplement CreateFile avec un char* ou un wchar_t* selon la configuration de votre construction sans connaître la différence.

L'encodage 'Ansi' n'est en fait pas un encodage spécifique:- mais signifie que l'encodage utilisé pour les chaînes "char" est spécifique au paramètre local du PC.

Maintenant, parce que les fonctions de la c-runtime - comme fopen - doivent fonctionner par défaut sans connaissances du développeur - sur les systèmes Windows, elles s'attendent à recevoir leurs chaînes dans l'encodage local de Windows. msdn indique que l'api setlocale du runtime C de Microsoft peut changer le paramètre local du thread actuel - mais précise spécifiquement qu'il échouera pour tous les paramètres locaux qui nécessitent plus de 2 octets par caractère - comme l'utf-8.

Donc, sur Windows, il n'y a pas de raccourci. Vous devez utiliser wfopen, ou l'API native CreateFileW (ou créer votre projet en utilisant les paramètres de construction Unicode et simplement appeler Createfile) avec des chaînes wchar_t*.

2 votes

En fait, il existe un raccourci : vous pouvez convertir la chaîne UTF-8 en Unicode, créer un "chemin d'accès court" ne contenant que des caractères ASCII en utilisant GetShortPathNameW, et passer cela à fopen. C'est le seul moyen de transmettre des noms de fichiers non-ASCII à des bibliothèques anciennes (ou écrites en C portable) qui utilisent simplement fopen pour ouvrir des fichiers.

1 votes

"Toute API Windows qui prend ou traite des chaînes de caractères est en fait disponible avec un suffixe W ou A - W pour "Wide" (large)/Unicode et A pour Ansi" - LA PLUPART des fonctions, mais pas CHAQUE fonction. Les fonctions qui existent depuis longtemps, notamment celles remontant aux premiers jours où Windows était basé sur ANSI, le sont certainement. Mais les nouvelles fonctions introduites ces dernières années, et à l'avenir, tendent à n'avoir que des versions larges, sans le suffixe W. Microsoft souhaite progressivement éliminer ANSI.

8voto

user4815162342 Points 27348

Comme le soulignent d'autres, la meilleure façon de gérer les chaînes encodées en UTF-8 est de les convertir en UTF-16 et d'utiliser des API Unicode natives telles que _wfopen ou CreateFileW.

Cependant, cette approche ne fonctionnera pas lorsque vous appelez des bibliothèques qui utilisent fopen() de manière inconditionnelle car elles ne prennent pas en charge Unicode ou parce qu'elles sont écrites en C portable. Dans ce cas, il est toujours possible d'utiliser les anciens "chemins courts" pour convertir une chaîne encodée en UTF-8 en une forme ASCII utilisable avec fopen, mais cela nécessite un certain travail :

  1. Convertissez la représentation UTF-8 en UTF-16 en utilisant MultiByteToWideChar.

  2. Utilisez GetShortPathNameW pour obtenir un "chemin court" qui n'est que de l'ASCII. GetShortPathNameW le renverra sous forme de chaîne large avec un contenu entièrement ASCII, que vous devrez convertir trivialement en une chaîne étroite en copiant chaque wchar_t char de manière sans perte.

  3. Transmettez le chemin court à fopen() ou au code qui utilisera éventuellement fopen(). Notez que les messages d'erreur imprimés par ce code, le cas échéant, feront référence au "chemin court" laid (par exemple KINTO~1 au lieu de kinto-un-筋斗雲).

Bien que cela ne soit pas exactement une stratégie recommandée à long terme, car les chemins courts de Windows sont une fonctionnalité héritée qui peut être désactivée par volume, c'est probablement le seul moyen de transmettre des noms de fichiers à du code utilisant fopen() et d'autres appels d'API liés au fichier (stat, access, versions ANSI de CreateFile et similaires).

1 votes

Magnifique, tu nous as sauvés, MERCI !!

0 votes

"pour gérer les chaînes encodées en UTF-8 ... les convertir en Unicode" UTF-8 (et UTF-16) sont des encodages Unicode. Je suppose que vous vouliez dire convertir en UTF-16

1 votes

@leonbloy Oui, je voulais dire Unicode tel que défini par Windows. Le point #1 indique clairement que l'encodage UTF-16 est nécessaire. J'ai maintenant modifié la réponse pour mentionner UTF-16 dès le départ.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X