Comment puis-je supprimer la nomenclature d'un fichier UTF-8?

64

J'ai un fichier en codage UTF-8 avec nomenclature et je souhaite supprimer la nomenclature. Existe-t-il des outils de ligne de commande linux pour supprimer la nomenclature du fichier?

$ file test.xml
test.xml:  XML 1.0 document, UTF-8 Unicode (with BOM) text, with very long lines
m13r
la source
1
J'ai créé un outil extrêmement simple à cet effet il y a quelques mois: oskog97.com/read/?path=/small-scripts/killbom&referer=/… Il pourrait être intéressant d'installer quelque chose de similaire dans / usr / local / bin si vous avez beaucoup de fichiers encodés UTF-8 avec des nomenclatures.
Oskar Skog

Réponses:

76

Si vous ne savez pas si le fichier contient une nomenclature UTF-8, cela (en supposant que la mise en oeuvre GNU de sed) supprime la nomenclature si elle existe ou ne fait aucune modification si elle ne l’a pas.

sed '1s/^\xEF\xBB\xBF//' < orig.txt > new.txt

Vous pouvez également écraser le fichier existant avec l' -ioption suivante:

sed -i '1s/^\xEF\xBB\xBF//' orig.txt
CSM
la source
4
cela peut ne pas fonctionner dans les paramètres régionaux utf8, mais l'ajout d'un remplacement de paramètres régionaux à c ou à posix fonctionnera toujours.
hildred
3
@hildred Je l'ai testé avec les en_US.UTF-8paramètres régionaux et cela a fonctionné. Quand va-t-il échouer?
m13r
2
@ m13r, Cela dépend de la version des options sed et compile. En cas d'échec, une toute nouvelle version de sed avec les classes de caractères Unicode introduira la séquence de trois octets sous la forme d'un seul caractère qui ne correspond pas à la séquence de trois caractères. Cependant, dans ce cas, vous pouvez faire une correspondance de caractère de 16 bits. Cependant, il s'agit d'une nouvelle fonctionnalité non universellement présente. Si vous voulez tester, je vous recommande de compiler la dernière version.
hildred
4
LC_ALL = C sed '1s / ^ \ xEF \ xBB \ xBF //' pour que cela fonctionne avec un unicode activé
Joshua
1
@mazunki, 1s/signifie seulement chercher dans la première ligne; les autres lignes ne sont pas affectées. Les ^moyennes ne correspondent qu'au début de la (première) ligne. \xEF\xBB\xBFest la nomenclature UTF-8 (chaîne hexadécimale échappée). //signifie remplacer avec rien. J'aurais pu ajouter 1à la fin (pour 1s/^xEF\xBB\xBF//1), ce qui signifierait seulement correspondre à la première occurrence du motif sur la ligne. Mais comme la recherche est ancrée avec ^, cela ne fera aucune différence. Si le fichier ne contient pas la nomenclature au début de la première ligne, le modèle ne correspond pas et aucune modification n'est donc apportée.
CSM
65

Une nomenclature n'a pas de sens dans UTF-8. Ceux-ci sont généralement ajoutés par erreur par un logiciel bidon sur les systèmes d'exploitation Microsoft.

dos2unix va l'enlever et également prendre en charge d'autres idiosyncrasies de fichiers texte Windows.

dos2unix test.xml
Stéphane Chazelas
la source
17
Je conviens qu’une BOM encodée en UTF-8 n’a pas de sens, mais qu’il en soit ou non, beaucoup de gens pensent que c’est une excellente idée qui permet de différencier UTF-8 des autres encodages 8 bits. C'est donc une question de goût. Le Bloc-notes Windows ajoute volontairement une nomenclature.
Johan Myréen
17
Qu'importe si cela a du sens ou non, quand le contexte est juste une question sur la façon de le supprimer? Selon Wikipedia, Notepad exige que la nomenclature reconnaisse un fichier au format UTF-8, et Google Documents l'ajoute également lors de l'exportation d'un fichier au format texte. Je doute qu'ils le fassent tous par erreur .
ilkkachu
Les commentaires ne sont pas pour une discussion prolongée; cette conversation a été déplacée pour discuter .
terdon
1
Existe-t-il un moyen de ne pas convertir les fins de ligne et de simplement supprimer la nomenclature avec dos2unix?
m13r
2
@ m13r Ensuite, utilisez le script sed dans cette réponse . Cela enlèvera seulement le bom (s'il existe), rien d'autre ne sera changé.
Arrow
27

Il est possible de supprimer la nomenclature d'un fichier à l'aide de la tailcommande suivante:

tail -c +4 withBOM.txt > withoutBOM.txt
m13r
la source
2
Pourquoi 4? La nomenclature a 3 octets.
deviantfan
10
@deviantfan C'est pourquoi vous devez commencer par le 4ème octet si vous voulez le sauter.
Stéphane Chazelas
9
tailutilise 1 indexation basée?! WTF!
CodesInChaos
5
@CodesInChaos, tail -c -1ou tail -c 1(ce qui tailest généralement utilisé pour) est le contenu commençant par le dernier octet, tail -c +1commençant par le premier octet. tail -c 0/ tail -c +0cela serait beaucoup moins intuitif.
Stéphane Chazelas
2
@deviantfan: (dd bs=1 count=3 of=/dev/null; cat) <input >output. Ou avec GNU (head -c3 >/dev/null; cat)- même en UTF8 ou dans d'autres paramètres régionaux autres qu'un octets; La tête GNU fait 'char' = octet.
dave_thompson_085
20

Utiliser VIM

  1. Ouvrir le fichier dans VIM:

    vi text.xml
    
  2. Supprimer le codage de la nomenclature:

    :set nobomb
    
  3. Sauvegarder et quitter:

    :wq
    
Joshua Pinter
la source
Bizarrement avec vim 8 sur un mac, j’ai un fichier csv utf-8 créé par Excel et qui commence par <feff>, mais :set nobombne le modifie ni ne le supprime.
Dlamblin
5

Vous pouvez utiliser

LANG=C LC_ALL=C sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//' -i -- filename

pour supprimer la marque d'ordre des octets au début du fichier, le cas échéant, ainsi que pour convertir les nouvelles lignes CR LF en LF uniquement. Il LANG=C LC_ALL=Cindique au shell que la commande doit être exécutée dans les paramètres régionaux C par défaut (également appelés paramètres régionaux POSIX par défaut), où les trois octets formant la marque d'ordre d'octet sont traités comme des octets. L' -ioption de sed signifie in-situ. Si vous utilisez -i.old, sed enregistre le fichier d'origine sous filename.oldle nouveau fichier (avec les modifications éventuelles) sous filename.


Je tiens personnellement à avoir cela comme ~/bin/fix-ms; par exemple, comme

#!/bin/dash
export LANG=C LC_ALL=C
if [ $# -gt 0 ]; then
    for FILE in "$@" ; do
        sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//' -i -- "$FILE" || exit 1
    done
else
    exec sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//'
fi

de sorte que si j’ai besoin d’appliquer ceci pour dire tous les fichiers sources C et tous les en-têtes (mon ancien code de l’ère MS-DOS, par exemple!), je ne fais que lancer

find . -name '*.[CHch]' -print0 | xargs -r0 ~/bin/ms-fix

ou, si je veux juste regarder un tel fichier, sans le modifier, je peux courir

~/bin/ms-fix < filename | less

et pas voir le laid <U+FEFF>dans mon terminal UTF-8.

Animal nominal
la source
Pourquoi pas simplement sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//' -i -- "$@"?
Stéphane Chazelas
@ StéphaneChazelas: Parce que je veux que le script se ferme immédiatement s'il y a un problème avec un remplacement, ce qui sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//' -i -- "$@"ne fonctionne pas; il renvoie un code de sortie, mais il traite tous les fichiers listés dans la liste des arguments avant de quitter.
Nominal Animal
@ StéphaneChazelas: L'important --avant le (s) nom (s) de fichier est bien sûr important: sans cela, les noms de fichier commençant par un tiret peuvent être considérés comme des options par sed. J'ai modifié ceux-ci dans ma réponse; Merci pour le rappel!
Nominal Animal
0

Récemment, j'ai trouvé ce petit outil de ligne de commande qui ajoute ou supprime la nomenclature sur des fichiers encodés au format UTF-8 arbitraires: UTF BOM Utils ( nouveau lien sur github).

Petit inconvénient, vous ne pouvez télécharger que le code source en clair C ++. Vous devez créer le fichier makefile (avec CMake , par exemple) et le compiler vous-même. Les fichiers binaires ne sont pas fournis sur cette page.

Wernfried Domscheit
la source