Utilisation de awk pour supprimer la marque d'ordre des octets

105

À quoi ressemblerait un awkscript (vraisemblablement une ligne) pour supprimer une nomenclature ?

Spécification:

  • imprimer chaque ligne après le premier ( NR > 1)
  • pour la première ligne: si elle commence par #FE #FFou#FF #FE , supprimez-les et imprimez le reste
Boldewyn
la source

Réponses:

114

Essaye ça:

awk 'NR==1{sub(/^\xef\xbb\xbf/,"")}{print}' INFILE > OUTFILE

Sur le premier enregistrement (ligne), supprimez les caractères de la nomenclature. Imprimez chaque enregistrement.

Ou légèrement plus court, en sachant que l'action par défaut dans awk est d'imprimer l'enregistrement:

awk 'NR==1{sub(/^\xef\xbb\xbf/,"")}1' INFILE > OUTFILE

1 est la condition la plus courte qui donne toujours la valeur true, de sorte que chaque enregistrement est imprimé.

Prendre plaisir!

-- ADDENDA --

La FAQ sur les marques d'ordre d'octets Unicode (BOM) comprend le tableau suivant répertoriant les octets exacts de la nomenclature pour chaque codage:

Bytes         |  Encoding Form
--------------------------------------
00 00 FE FF   |  UTF-32, big-endian
FF FE 00 00   |  UTF-32, little-endian
FE FF         |  UTF-16, big-endian
FF FE         |  UTF-16, little-endian
EF BB BF      |  UTF-8

Ainsi, vous pouvez voir comment \xef\xbb\xbfcorrespond aux EF BB BF UTF-8octets de la nomenclature du tableau ci-dessus.

Bartosz
la source
1
Il semble que le point au milieu de la déclaration secondaire soit trop (au moins, mon awk s'en plaint). A côté de cela, c'est exactement ce que j'ai cherché, merci!
Boldewyn
5
Cette solution, cependant, ne fonctionne que pour les fichiers encodés en UTF-8. Pour d'autres, comme UTF-16, voir Wikipedia pour la représentation de la nomenclature correspondante: en.wikipedia.org/wiki/Byte_order_mark
Boldewyn
2
Donc: awk '{if(NR==1)sub(/^\xef\xbb\xbf/,"");print}' INFILE > OUTFILEet assurez-vous que INFILE et OUTFILE sont différents!
Steve Clay
1
Si vous avez utilisé, perl -i.orig -pe 's/^\x{FFFE}//' badfilevous pouvez vous fier à vos envariables PERL_UNICODE et / ou PERLIO pour l'encodage. PERL_UNICODE = SD fonctionnerait pour UTF-8; pour les autres, vous auriez besoin de PERLIO.
tchrist
1
Peut-être une version un peu plus courte:awk 'NR==1{sub(/^\xef\xbb\xbf/,"")}1'
TrueY
122

Utilisation de GNU sed(sous Linux ou Cygwin):

# Removing BOM from all text files in current directory:
sed -i '1 s/^\xef\xbb\xbf//' *.txt

Sur FreeBSD:

sed -i .bak '1 s/^\xef\xbb\xbf//' *.txt

Avantage d'utiliser GNU ou FreeBSD sed: le -iparamètre signifie "en place", et mettra à jour les fichiers sans avoir besoin de redirections ou de trucs bizarres.

Sur Mac:

Cette awksolution dans une autre réponse fonctionne , mais la sedcommande ci-dessus ne fonctionne pas. Au moins sur Mac (Sierra), la seddocumentation ne mentionne pas la prise en charge de l'échappement hexadécimal ala \xef.

Une astuce similaire peut être réalisée avec n'importe quel programme en raccordant l' spongeoutil à partir de moreutils :

awk '…' INFILE | sponge INFILE
Denilson Sá Maia
la source
5
J'ai essayé la deuxième commande précisément sur Mac OS X et le résultat a été "succès", mais la substitution n'a pas eu lieu.
Trejkaz
1
Il est intéressant de noter que ces commandes remplacent une séquence d'octets spécifique, qui est l' une des marques d'ordre d'octet possibles . Peut-être que votre fichier avait une séquence de nomenclature différente. (Je ne peux pas aider à part ça, car je n'ai pas de Mac)
Denilson Sá Maia
3
Lorsque j'ai essayé la deuxième commande sur OS X sur un fichier qui utilisait 0xef 0xbb 0xbf comme nomenclature, cela n'a pas réellement fait la substitution.
John Wiseman
Sous OSX, je ne pouvais faire fonctionner cela que via perl, comme indiqué ici: stackoverflow.com/a/9101056/2063546
Ian
Sur OS X El Capitan 10.11.6, cela ne fonctionne pas, mais la réponse officielle stackoverflow.com/a/1068700/9636 fonctionne bien.
Heath Borders
42

Pas génial, mais plus simple:

tail -c +4 UTF8 > UTF8.nobom

Pour vérifier la nomenclature:

hd -n 3 UTF8

Si la nomenclature est présente, vous verrez: 00000000 ef bb bf ...

Steve Clay
la source
6
Les nomenclatures sont de 2 octets pour UTF-16 et 4 octets pour UTF-32, et bien sûr, il n'y a rien à faire en UTF-8 en premier lieu.
tchrist
2
@KarolyHorvath Oui, précisément. Son utilisation n'est pas recommandée. Ça casse des trucs. Le codage doit être spécifié par un protocole de niveau supérieur.
tchrist
1
@tchrist: tu veux dire que ça casse des trucs cassés? :) les applications appropriées devraient être capables de gérer cette nomenclature.
Karoly Horvath
7
@KarolyHorvath Je veux dire que ça casse beaucoup de programmes . N'est-ce pas ce que j'ai dit? Lorsque vous ouvrez un flux dans les encodages UTF-16 ou UTF-32, le décodeur sait ne pas compter la nomenclature. Lorsque vous utilisez UTF-8, les décodeurs présentent la nomenclature sous forme de données. Il s'agit d'une erreur de syntaxe dans d'innombrables programmes. Même le décodeur de Java se comporte de cette façon, PAR CONCEPTION! Les nomenclatures sur les fichiers UTF-8 sont mal placées et une douleur dans le cul: c'est une erreur! Ils cassent beaucoup de choses. Même juste cat file1.utf8 file2.utf8 file3.utf3 > allfiles.utf8sera brisé. N'utilisez jamais de nomenclature sur UTF-8. Période.
tchrist
6
hd n'est pas disponible sur OS X (à partir de 10.8.2), donc pour rechercher une nomenclature UTF-8, vous pouvez utiliser ce qui suit: head -c 3 file | od -t x1 .
mklement0
21

Outre la conversion des fins de ligne CRLF en LF, dos2unixsupprime également les nomenclatures:

dos2unix *.txt

dos2unix convertit également les fichiers UTF-16 avec une nomenclature (mais pas les fichiers UTF-16 sans nomenclature) en UTF-8 sans nomenclature:

$ printf '\ufeffä\n'|iconv -f utf-8 -t utf-16be>bom-utf16be
$ printf '\ufeffä\n'|iconv -f utf-8 -t utf-16le>bom-utf16le
$ printf '\ufeffä\n'>bom-utf8
$ printf 'ä\n'|iconv -f utf-8 -t utf-16be>utf16be
$ printf 'ä\n'|iconv -f utf-8 -t utf-16le>utf16le
$ printf 'ä\n'>utf8
$ for f in *;do printf '%11s %s\n' $f $(xxd -p $f);done
bom-utf16be feff00e4000a
bom-utf16le fffee4000a00
   bom-utf8 efbbbfc3a40a
    utf16be 00e4000a
    utf16le e4000a00
       utf8 c3a40a
$ dos2unix -q *
$ for f in *;do printf '%11s %s\n' $f $(xxd -p $f);done
bom-utf16be c3a40a
bom-utf16le c3a40a
   bom-utf8 c3a40a
    utf16be 00e4000a
    utf16le e4000a00
       utf8 c3a40a
Lri
la source
3

Je sais que la question était dirigée vers unix / linux, j'ai pensé qu'il valait la peine de mentionner une bonne option pour les défis Unix (sur Windows, avec une interface utilisateur).
J'ai rencontré le même problème sur un projet WordPress (BOM causait des problèmes avec le flux rss et la validation de page) et j'ai dû examiner tous les fichiers dans une assez grande arborescence de répertoires pour trouver celui qui était avec BOM. J'ai trouvé une application appelée Replace Pioneer et dedans:

Batch Runner -> Rechercher (pour trouver tous les fichiers dans les sous-dossiers) -> Remplacer le modèle -> Binary remove BOM (il existe un modèle de recherche et de remplacement prêt à l'emploi).

Ce n'était pas la solution la plus élégante et elle nécessitait l'installation d'un programme, ce qui est un inconvénient. Mais une fois que j'ai découvert ce qui se passait autour de moi, cela a fonctionné comme un charme (et j'ai trouvé 3 fichiers sur environ 2300 qui étaient avec BOM).

Arnon Zamir
la source
1
Je suis si heureux quand j'ai trouvé votre solution, mais je n'ai pas le privilège d'installer le logiciel sur l'ordinateur de l'entreprise. Il a fallu beaucoup de temps aujourd'hui, jusqu'à ce que je trouve l'alternative: utiliser Notepad ++ avec le plugin PythonScript. superuser.com/questions/418515/... Merci quand même!
Hoàng Long