web-dev-qa-db-fra.com

Forcer le codage de US-ASCII à UTF-8 (iconv)

J'essaie de transcoder un tas de fichiers US-ASCII en UTF-8.

Pour cela, j'utilise iconv:

iconv -f US-ASCII -t UTF-8 file.php > file-utf8.php

Les fichiers originaux sont encodés en US-ASCII, ce qui empêche la conversion. Apparemment, il se produit cause ASCII est un sous-ensemble de UTF-8 ...

http://www.linuxquestions.org/questions/linux-software-2/iconv-us-ascii-to-utf-8-or-iso-8859-15-a-705054/

Et en citant:

Il n’est pas nécessaire que le fichier texte apparaisse autrement tant que des caractères non ascii ne sont introduits

Vrai. Si j'introduis un caractère non-ASCII dans le fichier et le sauvegarde, disons avec Eclipse, le codage du fichier (jeu de caractères) est commuté sur UTF-8.

Dans mon cas, je voudrais forcer iconv à convertir les fichiers au format UTF-8 de toute façon . S'il contient ou non des caractères non-ASCII.

Remarque: La raison en est que mon PHP (fichiers non-ASCII ...) traite une chaîne non-ASCII, ce qui provoque une mauvaise interprétation des chaînes (français):

Il était une fois ... l'homme série animée mythique d'Albert

Barillé (Procidis), 1ère

...

[~ # ~] éditer [~ # ~]

  • US-ASCII - est - un sous-ensemble de UTF-8 _ (voir réponse de Ned ci-dessous)
  • Qui veut dire US-ASCII les fichiers sont réellement encodés en UTF-8
  • Mon problème est venu d'ailleurs
54
eightyfive

ASCII est un sous-ensemble de UTF-8, donc tous les fichiers ASCII sont déjà codés en UTF-8. Les octets du fichier ASCII et les octets qui en résultent de "coder en UTF-8" seraient exactement les mêmes octets. Il n'y a pas de différence entre eux, donc il n'y a pas besoin de faire quoi que ce soit.

Il semble que votre problème soit que les fichiers ne sont pas réellement ASCII. Vous devez déterminer quel encodage ils utilisent et les transcoder correctement.

61
Ned Batchelder

Réponse courte

  • file ne devine que l'encodage du fichier et peut être erroné (notamment dans les cas où les caractères spéciaux n'apparaissent que tardivement dans les fichiers volumineux).
  • vous pouvez utiliser hexdump pour consulter les octets de texte non-7 bits ascii et les comparer aux tables de codes pour les codages courants (iso-8859- *, utf-8) afin de déterminer vous-même le codage. .
  • iconv utilisera le codage d'entrée/sortie que vous spécifiez, quel que soit le contenu du fichier. Si vous spécifiez le mauvais codage d'entrée, la sortie sera tronquée.
  • même après avoir exécuté iconv, file ne peut signaler aucun changement en raison de la manière limitée dont file tente de deviner l'encodage. Pour un exemple spécifique, voir ma réponse longue.
  • L'ascii 7 bits (us-ascii) est identique au niveau d'octets à l'utf-8 et aux extensions ascii 8 bits (iso-8859- *). Ainsi, si votre fichier ne contient que des caractères 7 bits, vous pouvez l'appeler utf-8, iso-8859- * ou us-ascii car, à un niveau d'octet, ils sont tous identiques. Il n’est utile de parler d’utf-8 et d’autres encodages (dans ce contexte) que lorsque votre fichier contient des caractères extérieurs à la plage ascii 7 bits.

Longue réponse

Je me suis heurté à cela aujourd'hui et j'ai rencontré votre question. Je pourrais peut-être ajouter un peu plus d'informations pour aider les autres personnes confrontées à ce problème.

Premièrement, le terme ASCII est surchargé, ce qui entraîne une confusion.

7 bits ASCII ne comprend que 128 caractères (00-7F ou 0-127 en décimal). 7 bits ASCII est également appelé US-ASCII.

https://en.wikipedia.org/wiki/ASCII

Le codage UTF-8 utilise le même codage que 7 bits ASCII pour ses 128 premiers caractères. Ainsi, un fichier texte ne contenant que des caractères de cette plage des 128 premiers caractères sera identique à un niveau d'octet, qu'il soit codé au format UTF-8 ou ASCII 7 bits.

https://en.wikipedia.org/wiki/UTF-8#Codepage_layout

Le terme ascii étendu (ou haut ascii) fait référence à des codages de caractères de huit bits ou plus incluant le code standard à sept bits ASCII caractères, plus des caractères supplémentaires.

https://en.wikipedia.org/wiki/Extended_ASCII

ISO-8859-1 (ou "ISO Latin 1") est une norme d'extension spécifique à 8 bits ASCII qui couvre la plupart des caractères de l'Europe de l'Ouest. Il existe d'autres normes ISO pour les langues d'Europe orientale et les langues cyrilliques. ISO-8859-1 inclut des caractères tels que Ö, é, ñ et ß pour l'allemand et l'espagnol. "Extension" signifie que ISO-8859-1 inclut la norme à 7 bits ASCII et lui ajoute des caractères à l'aide du huitième bit. Ainsi, pour les 128 premiers caractères, il équivaut, au niveau des octets, aux fichiers codés ASCII et UTF-8. Cependant, lorsque vous commencez à traiter avec des caractères supérieurs aux 128 premiers caractères, vous n'êtes plus équivalent au niveau octet au format UTF-8, et vous devez effectuer une conversion si vous souhaitez que votre fichier "extended ascii" soit codé en UTF-8.

https://en.wikipedia.org/wiki/Extended_ASCII#ISO_8859_and_proprietary_adaptations

L’une des leçons que j’ai apprises aujourd’hui est qu’on ne peut pas faire confiance à file pour toujours interpréter correctement le codage des caractères d’un fichier.

https://en.wikipedia.org/wiki/File_%28command%29

La commande indique uniquement à quoi ressemble le fichier, et non ce à quoi il ressemble (dans le cas où fichier regarde le contenu). Il est facile de tromper le programme en mettant un nombre magique dans un fichier dont le contenu ne lui correspond pas. Ainsi, la commande ne peut être utilisée comme outil de sécurité que dans des situations spécifiques.

file recherche dans le fichier des nombres magiques faisant allusion au type, mais ceux-ci peuvent être erronés, sans garantie de correction. file essaie également de deviner l'encodage des caractères en consultant les octets du fichier. Fondamentalement, file dispose d'une série de tests qui l'aident à deviner le type de fichier et l'encodage.

Mon fichier est un gros fichier CSV. file indique que ce fichier est codé par us-ascii, ce qui est [~ # ~] incorrect [~ # ~] .

$ ls -lh
total 850832
-rw-r--r--  1 mattp  staff   415M Mar 14 16:38 source-file
$ file -b --mime-type source-file
text/plain
$ file -b --mime-encoding source-file
us-ascii

Mon fichier contient des trémas (c.-à-d. Ö). La première ascii non-7 bits n'apparaît pas avant plus de 100 000 lignes dans le fichier. Je suppose que c'est la raison pour laquelle file ne réalise pas que l'encodage de fichier n'est pas US-ASCII.

$ pcregrep -no '[^\x00-\x7F]' source-file | head -n1
102321:�

Je suis sur un Mac, donc en utilisant grep de PCRE. Avec gnu grep, vous pouvez utiliser l’option -P. Alternativement sur un mac, on pourrait installer coreutils (via homebrew ou autre) afin d’obtenir gnu grep.

Je n'ai pas creusé dans le code source de file, et la page de manuel ne traite pas de la détection du codage de texte en détail, mais je suppose que file ne regarde pas l'ensemble du fichier avant de deviner le codage.

Quel que soit le codage de mon fichier, ces caractères non-ASCII 7 bits cassent des données. Mon fichier CSV allemand est ; - séparé et l'extraction d'une seule colonne ne fonctionne pas.

$ cut -d";" -f1 source-file > tmp
cut: stdin: Illegal byte sequence
$ wc -l *
 3081673 source-file
  102320 tmp
 3183993 total

Notez l'erreur cut et que mon fichier "tmp" ne contient que 102320 lignes avec le premier caractère spécial de la ligne 102321.

Jetons un coup d'œil à la manière dont ces caractères non-ASCII sont codés. Je vide la première ascii non-7 bits dans hexdump, fais un peu de formatage, supprime les nouvelles lignes (0a) Et ne prend que les premières.

$ pcregrep -o '[^\x00-\x7F]' source-file | head -n1 | hexdump -v -e '1/1 "%02x\n"'
d6
0a

Autrement. Je sais que le premier caractère non-7 bits ASCII se trouve à la position 85 de la ligne 102321. Je saisis cette ligne et dis à hexdump de prendre les deux octets à partir de la position 85. Vous pouvez voir le caractère spécial (non -7-bit-ASCII) caractère représenté par un ".", Et l'octet suivant est "M" ... il s'agit donc d'un codage de caractères à un octet.

$ tail -n +102321 source-file | head -n1 | hexdump -C -s85 -n2
00000055  d6 4d                                             |.M|
00000057

Dans les deux cas, on voit que le caractère spécial est représenté par d6. Étant donné que ce caractère est une lettre allemande, je suppose que ISO-8859-1 devrait l'inclure. Vous pouvez voir que "d6" est une correspondance ( https://en.wikipedia.org/wiki/ISO/IEC_8859-1#Codepage_layout ).

Question importante ... comment savoir si ce caractère est un Ö sans être sûr du codage du fichier? La réponse est le contexte. J'ai ouvert le fichier, lu le texte, puis déterminé quel caractère il était censé être. Si je l’ouvre dans vim, il s’affiche en tant que Ö parce que vim fait un meilleur travail de deviner le codage de caractères (dans ce cas) que file fait.

Donc, mon fichier semble être ISO-8859-1. En théorie, je devrais vérifier le reste des caractères ASCII non 7 bits pour m'assurer que l'ISO-8859-1 convient bien ... Rien n'oblige un programme à n'utiliser qu'un seul encodage lors de l'écriture d'un fichier disque (autre que les bonnes manières).

Je vais sauter le chèque et passer à l'étape de conversion.

$ iconv -f iso-8859-1 -t utf8 source-file > output-file
$ file -b --mime-encoding output-file
us-ascii

Hmm. file me dit toujours que ce fichier est au format US-ASCII, même après la conversion. Vérifions avec hexdump à nouveau.

$ tail -n +102321 output-file | head -n1 | hexdump -C -s85 -n2
00000055  c3 96                                             |..|
00000057

Certainement un changement. Notez que nous avons deux octets non-ASCII 7 bits (représentés par le "." Sur la droite) et que le code hexadécimal pour les deux octets est maintenant c3 96. Si nous jetons un coup d'œil, il semble que nous ayons maintenant UTF-8 (c3 96 est le bon encodage de Ö en UTF-8) http://www.utf8-chartable.de/

Mais file rapporte toujours notre fichier comme us-ascii? Eh bien, je pense que cela revient au fait que file ne regarde pas l'intégralité du fichier et le fait que les premiers caractères non ASCII 7 bits ne se produisent pas jusqu'au plus profond du fichier.

Je vais utiliser sed pour coller un Ö au début du fichier et voir ce qui se passe.

$ sed '1s/^/Ö\'$'\n/' source-file > test-file
$ head -n1 test-file
Ö
$ head -n1 test-file | hexdump -C
00000000  c3 96 0a                                          |...|
00000003

Cool, nous avons un tréma. Notez que le codage est cependant c3 96 (utf-8). Hmm.

Vérification à nouveau de nos autres trémas dans le même fichier:

$ tail -n +102322 test-file | head -n1 | hexdump -C -s85 -n2
00000055  d6 4d                                             |.M|
00000057

ISO-8859-1. Oops! Cela montre bien à quel point il est facile de décoder les encodages.

Essayons de convertir notre nouveau fichier de test avec le tréma situé à l'avant et voyons ce qui se passe.

$ iconv -f iso-8859-1 -t utf8 test-file > test-file-converted
$ head -n1 test-file-converted | hexdump -C
00000000  c3 83 c2 96 0a                                    |.....|
00000005
$ tail -n +102322 test-file-converted | head -n1 | hexdump -C -s85 -n2
00000055  c3 96                                             |..|
00000057

Oops. Le premier umlaut qui était UTF-8 a été interprété comme ISO-8859-1 car c’est ce que nous avons dit iconv. Le deuxième tréma est correctement converti de d6 En c3 96.

Je vais réessayer, cette fois-ci, je vais utiliser vim pour effectuer l'insertion Ö à la place de sed. vim semblait mieux détecter le codage ("latin1", alias ISO-8859-1); il insérera donc peut-être le nouveau Ö avec un codage cohérent.

$ vim source-file
$ head -n1 test-file-2
�
$ head -n1 test-file-2 | hexdump -C
00000000  d6 0d 0a                                          |...|
00000003
$ tail -n +102322 test-file-2 | head -n1 | hexdump -C -s85 -n2
00000055  d6 4d                                             |.M|
00000057

Cela semble bon. Ressemble à ISO-8859-1 pour les anciens et les anciens trémas.

Maintenant le test.

$ file -b --mime-encoding test-file-2
iso-8859-1
$ iconv -f iso-8859-1 -t utf8 test-file-2 > test-file-2-converted
$ file -b --mime-encoding test-file-2-converted
utf-8

Boom! Morale de l'histoire. Ne faites pas confiance à file pour toujours deviner le bon encodage. Facile à mélanger les encodages dans le même fichier. En cas de doute, regardez l'hexagone.

Un hack (également sujet à l’échec) qui permettrait de résoudre cette limitation spécifique de file lorsqu’il s’agit de fichiers volumineux consisterait à raccourcir le fichier pour s’assurer que les caractères spéciaux apparaissent au début du fichier afin que file est plus susceptible de les trouver.

$ first_special=$(pcregrep -o1 -n '()[^\x00-\x7F]' source-file | head -n1 | cut -d":" -f1)
$ tail -n +$first_special source-file > /tmp/source-file-shorter
$ file -b --mime-encoding /tmp/source-file-shorter
iso-8859-1

Mise à jour

Christos Zoulas a mis à jour file pour rendre la quantité d'octets examinés configurable. Un jour de retour sur demande de fonctionnalité, génial!

http://bugs.gw.com/view.php?id=5https://github.com/file/fichier/commit/d04de269e0b06ccd0a7d1bf4974fed1d75be7d9e

La fonctionnalité a été publiée dans file version 5.26.

Il faut du temps avant de pouvoir deviner l’encodage d’un fichier volumineux. Cependant, il est agréable d’avoir la possibilité de choisir des cas d’utilisation spécifiques pour lesquels une estimation plus précise peut l'emporter sur le temps/io supplémentaire.

Utilisez l'option suivante:

−P, −−parameter name=value

    Set various parameter limits.

    Name    Default     Explanation
    bytes   1048576     max number of bytes to read from file

Quelque chose comme...

file_to_check="myfile"
bytes_to_scan=$(wc -c < $file_to_check)
file -b --mime-encoding -P bytes=$bytes_to_scan $file_to_check

... devrait faire l'affaire si vous voulez forcer file à regarder tout le fichier avant de deviner. Bien sûr, cela ne fonctionne que si vous avez file 5.26 ou plus récent.

Je n'ai pas encore construit/testé les dernières versions. La plupart de mes machines ont actuellement file 5.04 (2010) ... espérons qu'un jour cette version parviendra à la version amont.

36
mattpr

Donc, les gens disent que vous ne pouvez pas et je comprends que vous pourriez être frustré de poser une question et d’obtenir une telle réponse.

Si vous voulez vraiment que cela apparaisse dans utf-8 au lieu de nous-ascii, alors vous devez le faire en 2 étapes.

première :

iconv -f us-ascii -t utf-16 yourfile > youfileinutf16.*

seconde:

iconv -f utf-16le -t utf-8 yourfileinutf16 > yourfileinutf8.*

alors si vous faites un fichier -i, vous verrez que le nouveau jeu de caractères est utf-8.

J'espère que ça aide.

16
Mathieu

Je pense que Ned a le cœur du problème - vos fichiers ne sont pas réellement ASCII. Essayer

iconv -f ISO-8859-1 -t UTF-8 file.php > file-utf8.php

Je suppose que vous utilisez réellement iso-8859-1 , il est populaire dans la plupart des langues européennes.

11
sarnold

Il n'y a pas de différence entre l'US-ASCII et l'UTF-8, il n'est donc pas nécessaire de le reconvertir. Mais voici un petit indice, si vous rencontrez des problèmes avec les caractères spéciaux lors du recodage.

Ajoutez // TRANSLIT après le paramètre-charset-source.

Exemple:

iconv -f ISO-8859-1//TRANSLIT -t UTF-8 filename.sql > utf8-filename.sql

Cela m'aide sur les types de guillemets étranges, qui sont toujours rompus le processus de réencodage du jeu de caractères.

2
suther

Voici un script qui trouvera tous les fichiers correspondant à un modèle que vous transmettez, puis les convertira de leur encodage de fichier actuel en utf-8. Si l'encodage est us-ascii, il sera toujours affiché comme us-ascii, puisqu'il s'agit d'un sous-ensemble de utf-8.

#!/usr/bin/env bash    
find . -name "${1}" |
    while read line;
    do
        echo "***************************"
        echo "Converting ${line}"

        encoding=$(file -b --mime-encoding ${line}) 
        echo "Found Encoding: ${encoding}"

        iconv -f "${encoding}" -t "utf-8" ${line} -o ${line}.tmp
        mv ${line}.tmp ${line}
    done
2
Pytry

Vous pouvez utiliser file -i file_name pour vérifier quel est exactement votre format de fichier original.

Une fois que vous obtenez cela, vous pouvez faire ce qui suit:

iconv -f old_format -t utf-8 input_file -o output_file
1
user2830451

J'ai accidentellement encodé un fichier en UTF-7 et avais un problème similaire. Quand j'ai tapé file -i name.file Je voudrais obtenir charset=us-ascii. iconv -f us-ascii -t utf-9//translit name.file _ ne fonctionnerait pas puisque j'ai rassemblé UTF-7 est un sous-ensemble de nous-ascii, tout comme UTF-8.

Pour résoudre ceci, je suis entré: iconv -f UTF-7 -t UTF-8//TRANSLIT name.file -o output.file

Je ne sais pas comment déterminer le codage autre que ce que d'autres ont suggéré ici.

1
Schabry