Wie kann ich die Stückliste aus einer UTF-8-Datei entfernen?

64

Ich habe eine Datei in UTF-8-Codierung mit Stückliste und möchte die Stückliste entfernen. Gibt es Linux-Befehlszeilentools, um die Stückliste aus der Datei zu entfernen?

$ file test.xml
test.xml:  XML 1.0 document, UTF-8 Unicode (with BOM) text, with very long lines
m13r
quelle
1
Ich habe vor ein paar Monaten ein sehr einfaches Tool entwickelt: oskog97.com/read/?path=/small-scripts/killbom&referer=/… Vielleicht lohnt es sich, so etwas in / usr / local / bin zu installieren, wenn Sie haben viele UTF-8-codierte Dateien mit Stücklisten.
Oskar Skog

Antworten:

76

Wenn Sie nicht sicher sind, ob die Datei eine UTF-8-Stückliste enthält, wird diese Stückliste (unter der Annahme der GNU-Implementierung von sed) entfernt, falls vorhanden, oder es werden keine Änderungen vorgenommen, falls dies nicht der Fall ist.

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

Sie können die vorhandene Datei auch mit der folgenden -iOption überschreiben :

sed -i '1s/^\xEF\xBB\xBF//' orig.txt
CSM
quelle
4
Dies funktioniert möglicherweise nicht in einem utf8-Gebietsschema, aber das Voranstellen einer Gebietsschemaüberschreibung vor c oder posix funktioniert immer.
Hildred
3
@hildred Ich habe es mit dem en_US.UTF-8Gebietsschema getestet und es hat funktioniert. Wann wird es scheitern?
m13r
2
@ m13r, Das hängt von der Version der sed- und compile-Optionen ab. Im Fehlerfall wird in einer sehr neuen Version von sed mit Unicode-Zeichenklassen die Drei-Byte-Sequenz als ein einzelnes Zeichen eingefügt, das nicht mit der Drei-Zeichen-Sequenz übereinstimmt. In diesem Fall können Sie jedoch eine 16-Bit-Zeichenübereinstimmung durchführen. Dies ist jedoch ein neues Feature und nicht allgemein verfügbar. Wenn Sie testen möchten, empfehle ich, die neueste Version zu kompilieren.
Hildred
4
Um das Problem mit einem Unicode-fähigen sed zu beheben, führen Sie LC_ALL = C sed '1s / ^ \ xEF \ xBB \ xBF //' aus
Joshua,
1
@ mazunki 1s/bedeutet , dass nur die erste Zeile durchsucht wird. andere Zeilen bleiben davon unberührt. Die ^Mittel stimmen nur am Anfang der (ersten) Zeile überein. \xEF\xBB\xBFist die UTF-8-Stückliste (Escape-Hex-Zeichenfolge). //bedeutet durch nichts ersetzen. Ich hätte 1am Ende (für 1s/^xEF\xBB\xBF//1) hinzufügen können , was bedeutet, dass nur das erste Vorkommen des Musters in der Zeile übereinstimmt. Da die Suche jedoch verankert ist ^, macht dies keinen Unterschied. Wenn die Datei am Anfang der ersten Zeile keine Stückliste enthält, stimmt das Muster nicht überein und es werden keine Änderungen vorgenommen.
CSM
64

Eine Stückliste ist in UTF-8 nicht sinnvoll. Diese werden in der Regel versehentlich durch falsche Software unter Microsoft-Betriebssystemen hinzugefügt.

dos2unix entfernt es und kümmert sich auch um andere Besonderheiten von Windows-Textdateien.

dos2unix test.xml
Stéphane Chazelas
quelle
17
Ich stimme zu, dass eine UTF-8-codierte Stückliste keinen Sinn ergibt, aber ob Sie es glauben oder nicht, es gibt viele Leute, die es für eine großartige Idee halten, UTF-8 von anderen 8-Bit-Codierungen zu unterscheiden. Es ist also Geschmackssache. Windows Notepad fügt absichtlich eine Stückliste hinzu.
Johan Myréen
17
Was macht es aus, ob es sinnvoll ist oder nicht, wenn der Kontext nur eine Frage zum Entfernen ist? Laut Wikipedia muss die Stückliste in Notepad eine Datei als UTF-8 erkennen, und Google Docs fügt sie beim Exportieren einer Datei als Text hinzu. Ich bezweifle , dass sie alle tun es durch Fehler .
Ilkkachu
Kommentare sind nicht für längere Diskussionen gedacht. Diese Unterhaltung wurde in den Chat verschoben .
Terdon
1
Gibt es eine Möglichkeit, die Zeilenenden nicht zu konvertieren und die Stückliste einfach mit zu entfernen dos2unix?
m13r
2
@ m13r Dann benutze das sed-Skript in dieser Antwort . Dadurch wird nur die Stückliste entfernt (falls vorhanden), nichts anderes wird geändert.
Pfeil
26

Es ist möglich, die Stückliste mit dem folgenden tailBefehl aus einer Datei zu entfernen :

tail -c +4 withBOM.txt > withoutBOM.txt
m13r
quelle
2
Warum 4? Die Stückliste hat 3 Byte.
Deviantfan
10
@deviantfan Aus diesem Grund müssen Sie beim 4. Byte beginnen, wenn Sie es überspringen möchten.
Stéphane Chazelas
9
tailverwendet 1-basierte Indizierung ?! WTF!
CodesInChaos
5
@CodesInChaos tail -c -1oder tail -c 1(was tailallgemein verwendet wird) ist der Inhalt, der mit dem letzten Byte tail -c +1beginnt und mit dem ersten Byte beginnt. tail -c 0/ tail -c +0denn das wäre viel uninteressanter.
Stéphane Chazelas
2
@deviantfan: (dd bs=1 count=3 of=/dev/null; cat) <input >output. Oder mit GNU (head -c3 >/dev/null; cat)- sogar in UTF8 oder einem anderen Gebietsschema ohne Einzelbyte; GNU-Kopf bedeutet 'char' = Byte.
Dave_thompson_085
20

Verwenden von VIM

  1. Datei in VIM öffnen:

    vi text.xml
    
  2. Stücklistencodierung entfernen:

    :set nobomb
    
  3. Speichern und Beenden:

    :wq
    
Joshua Pinter
quelle
Seltsamerweise habe ich mit vim 8 auf einem Mac eine von Excel erstellte csv utf-8-Datei, die mit beginnt <feff>, sie :set nobombjedoch nicht ändert oder entfernt.
Dlamblin
5

Sie können verwenden

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

Entfernen der Markierung für die Bytereihenfolge vom Anfang der Datei, falls vorhanden, sowie Konvertieren von CR-LF-Zeilenumbrüchen in LF-Zeilenumbrüche. Das LANG=C LC_ALL=Cteilt der Shell mit, dass der Befehl im Standardgebietsschema C (auch als Standard-POSIX-Gebietsschema bezeichnet) ausgeführt werden soll. Dabei werden die drei Bytes, die die Byte-Ordnungsmarke bilden, als Bytes behandelt. Die -iOption zu sed bedeutet vor Ort. Wenn Sie verwenden -i.old, speichert sed die Originaldatei als filename.oldund die neue Datei (mit den Änderungen, falls vorhanden) als filename.


Ich persönlich habe das gerne als ~/bin/fix-ms; zum Beispiel als

#!/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

Wenn ich dies anwenden muss, um alle C-Quelldateien und -Header (z. B. meinen alten Code aus der MS-DOS-Ära) auszudrücken, führe ich einfach aus

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

oder, wenn ich nur eine solche Datei anschauen möchte, ohne sie zu ändern, kann ich sie ausführen

~/bin/ms-fix < filename | less

und nicht das hässliche <U+FEFF>in meinem UTF-8-Terminal sehen.

Nominelles Tier
quelle
Warum nicht einfach sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//' -i -- "$@"?
Stéphane Chazelas
@ StéphaneChazelas: Weil ich möchte, dass das Skript sofort beendet wird, wenn es ein Problem mit einem Ersatz gibt, das sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//' -i -- "$@"nicht funktioniert. Es gibt zwar einen Exit-Code zurück, verarbeitet jedoch vor dem Beenden alle in der Argumentliste aufgeführten Dateien.
Nominal Animal
@ StéphaneChazelas: Das --Voranstellen der Dateinamen ist natürlich wichtig: Ohne diese Angabe können Dateinamen, die mit einem Bindestrich beginnen, von sed als Optionen angesehen werden. Ich habe diese in meine Antwort eingearbeitet. Danke für die Erinnerung!
Nominal Animal
0

Kürzlich habe ich dieses winzige Befehlszeilen-Tool gefunden, das die Stückliste für beliebige UTF-8-kodierte Dateien hinzufügt oder entfernt: UTF BOM Utils ( neuer Link bei github)

Kleiner Nachteil, Sie können nur den einfachen C ++ - Quellcode herunterladen. Sie müssen das Makefile erstellen ( zum Beispiel mit CMake ) und es selbst kompilieren, Binärdateien werden auf dieser Seite nicht bereitgestellt.

Wernfried Domscheit
quelle