38 votes

Quel est l'encodage d'argv?

Il n'est pas clair pour moi ce que les codages sont utilisés là où en C argv. En particulier, je suis intéressé par le scénario suivant:

  • Un utilisateur utilise les paramètres régionaux de L1 pour créer un fichier dont le nom, N, contient des caractères non-ASCII
  • Plus tard, un utilisateur utilise les paramètres régionaux de L2 à onglet-compléter le nom du fichier sur la ligne de commande, qui est introduit dans un programme P, comme un argument de ligne de commande

Ce que la séquence d'octets ne P voir sur la ligne de commande?

J'ai observé que sur Linux, la création d'un nom de fichier en UTF-8 puis onglet-en le complétant en (par exemple) le zw_TW.big5 semble la cause de mon programme P d'être nourri UTF-8 plutôt que de Big5. Cependant, sur OS X de la même série d'actions résultats dans mon programme P l'obtention d'un Big5 codée nom de fichier.

Voici ce que je pense qui se passe jusqu'à présent (long, et je suis probablement mal et doivent être corrigées):

Windows

Les noms de fichiers sont stockés sur le disque dans certains format Unicode. Afin que Windows prend le nom de N, se convertit en L1 (la page de code) pour une version Unicode de N nous appellerons N1, et les magasins de N1 sur le disque.

Ce que je puis supposer qui se passe est que lorsque l'onglet de remplir plus tard, le nom N1 est converti à locale L2 (la nouvelle page de code) pour l'affichage. Avec de la chance, cela donnera le nom d'origine N -- mais ce ne sera pas vrai si N caractères contenus irreprésentable dans la L2. Nous appelons le nouveau nom de N2.

Lorsque l'utilisateur appuie sur entrée pour exécuter P avec cet argument, le nom de N2 est de nouveau converti en unicode, produisant N1 de nouveau. Cette N1 est maintenant disponible pour le programme en UCS2 format via GetCommandLineW/wmain/tmain, mais les utilisateurs de GetCommandLine/main verrez le nom de N2 dans les paramètres régionaux en cours (page de code).

OS X

Le disque de stockage de l'histoire est la même, autant que je sache. OS X stocke les noms de fichiers en Unicode.

Avec un terminal unicode, je pense que se passe est que le terminal construit la ligne de commande dans une Unicode tampon. Donc, lors de l'onglet complet, il copie le nom du fichier comme un fichier Unicode nom de la mémoire tampon.

Lorsque vous exécutez la commande, qui unicode tampon est converti à la locale courante, L2, et nourrir le programme via argv, et le programme peut décoder argv avec les paramètres régionaux actuels en Unicode pour l'affichage.

Linux

Sur Linux, tout est différent et je suis extra-confus au sujet de ce qui se passe. Linux stocke les noms de fichiers comme des chaînes d'octets, pas dans Unicode. Donc, si vous créez un fichier avec le nom de N dans les paramètres régionaux de L1 qui N comme une chaîne d'octets est ce qui est stocké sur le disque.

Quand je plus tard, lancez le terminal et d'essayer d'onglet-compléter le nom, je ne suis pas sûr de ce qui se passe. Il me semble que la ligne de commande est construit comme un tampon d'octets, et le nom du fichier comme une chaîne d'octets est juste concaténé à la mémoire tampon. Je suppose que lorsque vous tapez un caractère standard il est codé à la volée d'octets qui sont ajoutés à la mémoire tampon.

Lorsque vous exécutez un programme, je pense que la mémoire tampon est envoyé directement à argv. Maintenant, ce que le codage ne argv ont? Il ressemble à tous les caractères que vous avez tapé dans la ligne de commande alors que dans les paramètres régionaux de L2 sera en L2 l'encodage, mais le nom de fichier dans la L1 de l'encodage. Donc, argv contient un mélange de deux codages!

Question

J'aimerais vraiment que quelqu'un pourrait me permettre de savoir ce qui se passe ici. Tout ce que j'ai en ce moment est à moitié suppositions et de la spéculation, et il n'a pas vraiment d'ajustement ensemble. Ce que j'aimerais vraiment être vrai, c'est pour argv encodé dans la page de code (Windows) ou les paramètres régionaux actuels (Linux / OS X), mais qui ne semble pas être le cas...

Extras

Voici un simple candidat programme P qui vous permet d'observer les codages pour vous-même:

#include <stdio.h>

int main(int argc, char **argv)
{
    if (argc < 2) {
        printf("Not enough arguments\n");
        return 1;
    }

    int len = 0;
    for (char *c = argv[1]; *c; c++, len++) {
        printf("%d ", (int)(*c));
    }

    printf("\nLength: %d\n", len);

    return 0;
}

Vous pouvez utiliser locale -a voir les locales disponibles, et d'utiliser export LC_ALL=my_encoding modifier vos paramètres régionaux.

20voto

Max Bolingbroke Points 1175

Merci à tous pour vos réponses. J'ai appris beaucoup de choses sur cette question et ont découvert les choses suivantes qui a résolu ma question:

  1. Tel que discuté, sur Windows la argv est codé à l'aide de la page de code. Toutefois, vous pouvez récupérer la ligne de commande en tant que UTF-16 à l'aide de GetCommandLineW. L'utilisation de argv n'est pas recommandé pour les Fenêtres modernes applications avec le support de l'unicode, car les pages de code sont obsolètes.

  2. Sur les systèmes Unix, la argv a pas de correction de l'encodage:

    a) les noms de Fichier inséré par onglet-achèvement/expansion va se produire dans argv verbatim exactement comme les séquences d'octets par lesquelles ils sont nommés sur le disque. Cela est vrai même si ces séquences d'octets n'ont pas de sens dans la locale courante.

    b) d'Entrée saisies directement par l'utilisateur à l'aide de leurs IME se produire dans argv dans l'encodage des paramètres régionaux. (Ubuntu semble utiliser les paramètres RÉGIONAUX de décider de la façon de coder IME entrée, alors que OS X utilise le Terminal.application de codage de Préférence).

Ce qui est gênant pour des langages comme Python ou Haskell qui veut traiter les arguments de ligne de commande comme des Chaînes de caractères, parce qu'il doit décider de la façon de décoder argv dans n'importe quel encodage est utilisé en interne pour la Chaîne (qui est de l'UTF-16 pour les deux langues). Cependant, si ils utilisent l'encodage des paramètres régionaux pour ce faire, le décodage, puis valide les noms de fichiers dans l'entrée peut ne pas décoder, provoquant une exception.

La solution à ce problème adopté par Python 3 est un substitut-schéma de codage octet (http://www.python.org/dev/peps/pep-0383/), qui représente tout undecodable octet dans argv spécial des points de code Unicode. Lors de ce point de code est décodé retour à un flux d'octets, il devient simplement l'origine de l'octet de nouveau. Cela permet des échanges de données de argv qui n'est pas valide dans le courant de l'encodage (c'est à dire un nom de fichier nommé dans autre chose que les paramètres régionaux en cours) par le biais de la maternelle Python type de chaîne et de retour d'octets sans perte d'informations.

Comme vous pouvez le voir, la situation est assez compliqué :-)

6voto

Philipp Points 21479

Je ne peux parler que sur Windows pour le moment. Sur Windows, les pages de code sont destinées uniquement pour les applications héritées et n'est pas utilisé par le système ou les applications modernes. Windows utilise UTF-16 (et l'a fait pour les âges) pour tout: affichage de texte, les noms de fichiers, le terminal, le système API. Les Conversions entre UTF-16 et l'héritage des pages de code sont uniquement réalisées au niveau le plus élevé possible, directement à l'interface entre le système et l'application (techniquement, les anciennes fonctions de l'API sont mis en œuvre deux fois-une fonction FunctionW qui ne le travail réel et s'attend à ce UTF-16 chaînes, et une compatibilité fonction FunctionA que simplement convertit les chaînes d'entrée de courant (fil) de la page de code UTF-16, les appels de l' FunctionW, et convertit les résultats). Onglet-achèvement doivent toujours céder le passage en UTF-16 chaînes (il n'a certainement lors de l'utilisation d'une police TrueType) parce que la console utilise que de l'UTF-16 ainsi. L'onglet complété UTF-16 de nom de fichier est remis à la demande. Si maintenant que l'application est un héritage de l'application (c'est à dire, il utilise main au lieu de wmain/GetCommandLineW etc.), puis Microsoft C runtime (probablement) utilise GetCommandLineA d'avoir le système de convertir la ligne de commande. Donc, fondamentalement, je pense que ce que vous dites à propos de Windows est correct (sauf qu'il n'y a probablement pas de conversion en cause, tout en onglet-achèvement des travaux): le argv tableau contiendra toujours les arguments dans la page de code de l' actuelle application, car l'information ce que le code de la page (L1) à l' origine du programme a utilise a été irrémédiablement perdue au cours de l'intermédiaire UTF-16 stade.

La conclusion, comme toujours sur Windows: Éviter l'héritage des pages de code; l'utilisation de l'UTF-16 API partout où vous le pouvez. Si vous devez utiliser main au lieu de wmain (par exemple, pour être indépendant de la plateforme), utilisez GetCommandLineW , au lieu de l' argv tableau.

2voto

Johan Points 6127

La sortie de votre application de test avait besoin de quelques modifications pour donner un sens, vous avez besoin des codes hex et vous avez besoin de se débarrasser de l de valeurs négatives. Ou vous ne pouvez pas imprimer des choses comme UTF-8, les caractères spéciaux de sorte que vous pouvez les lire.

D'abord la modification de SW:

#include <stdio.h>

int main(int argc, char **argv)
{
    if (argc < 2) {
        printf("Not enough arguments\n");
        return 1;
    }

    int len = 0;
    for (unsigned char *c = argv[1]; *c; c++, len++) {
        printf("%x ", (*c));
    }

    printf("\nLength: %d\n", len);

    return 0;
}

Ensuite, sur mon Ubuntu zone de l'utilisation de l'UTF-8, j'obtiens cette sortie.

$> gcc -std=c99 argc.c -o argc
$> ./argc 1ü
31 c3 bc 
Length: 3

Et ici vous pouvez voir que dans mon cas, ü est codé sur 2 caractères, et que le 1 est un seul caractère. Plus ou moins exactement ce que vous attendez d'un encodage UTF-8.

Et c'est en fait correspondre à ce qui est dans l'env LANG varible.

$> env | grep LANG
LANG=en_US.utf8

Espérons que cela clarifie la linux un peu.

/Bonne chance

1voto

Yuhong Bao Points 1454

Oui, les utilisateurs doivent être prudents lorsqu'ils mélangent des paramètres régionaux sur Unix en général. Les gestionnaires de fichiers GUI qui affichent et modifient les noms de fichiers ont également ce problème. Sur Mac OS X, l'encodage Unix standard est UTF-8. En fait, le système de fichiers HFS +, lorsqu'il est appelé via les interfaces Unix, applique les noms de fichiers UTF-8 car il doit le convertir en UTF-16 pour le stockage dans le système de fichiers lui-même.

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