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 ...
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)US-ASCII
les fichiers sont réellement encodés en UTF-8
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.
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).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.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.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
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.
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.
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.
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.
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
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
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.