Seltsames Zeichen in einer Datei

6

Ich habe eine UTF-8-Datei, die ein seltsames Zeichen enthält - für mich genauso sichtbar

<96>

So erscheint es auf vi

vi Screenshot

und wie es auf erscheint gedit

gedit Screenshot

und wie es unter LibreOffice erscheint

LibreOffice Screenshot

und das führt dazu, dass sich eine Reihe grundlegender Unix-Tools schlecht verhält, darunter:

  1. cat fileLass den Charakter verschwinden und moreauch
  2. Ich kann nicht in vi / vim kopieren und einfügen - es wird sich nicht einmal selbst finden
  3. grep zeigt auch nichts an, als ob das Zeichen nicht existiert hätte.

Das Programm filefunktioniert einwandfrei und erkennt eine UTF-8-Datei. Ich weiß auch, dass die Datei aufgrund der Art höchstwahrscheinlich aus einem Copy & Paste aus dem Web stammt und das Zeichen ursprünglich einen EMDASH darstellte.

Meine grundlegenden Fragen sind:

  1. Stimmt etwas mit dieser Datei nicht?
  2. Wie kann ich in derselben Datei nach anderen Vorkommen suchen?
  3. Wie kann ich nach anderen Dateien suchen, die möglicherweise dasselbe Problem / denselben Charakter enthalten?

Die Datei finden Sie hier: file.txt

Paulo Ney
quelle
2
Der erste Schritt besteht darin hexdump -C filename, die Kodierung dessen zu betrachten, was für Sie als "sichtbar" ist <96>. Der Kontext sollte helfen, ihn genau zu bestimmen.
Dirkt
@dirkt, der Kontext zeigt auf den Charakter, der ein EMDASH ist, und hexdump -Czeigt c2 96. Wie kann ich nach anderen Vorkommen derselben Sache suchen?
Paulo Ney
@ G-Man, Sie können die Datei herunterladen, der Charakter wird zum Beispiel in vi / vim so angezeigt, und ich verwende stock "grep" unter Ubuntu 18.04.
Paulo Ney
Gibt es ein Werkzeug, mit dem man diesen Charakter gut verwalten kann? Ich denke an ein Textverarbeitungsprogramm wie LibreOffice Writer oder einen einfachen Texteditor, geditwenn Sie ihn so eingestellt haben, dass er Ihre Sprache und UTF-8 verwaltet. In diesem Fall können Sie dieses Zeichen entfernen.
Sudodus
@sudodus Ich habe die Ansichten von vi, gedit und libreOffice hinzugefügt - keine davon scheint etwas Nützliches hervorzubringen.
Paulo Ney

Antworten:

27

Diese Datei enthält Bytes C2 96, die die UTF-8- Codierung des Codepunkts U + 0096 darstellen. Dieser Codepunkt ist eines der C1-Steuerzeichen, die üblicherweise als SPA "Start of Guarded Area" (oder "Protected Area") bezeichnet werden. Das ist kein nützlicher Charakter für ein modernes System, aber es ist unwahrscheinlich, dass es dort schädlich ist.

Die ursprüngliche Quelle hierfür war wahrscheinlich ein Byte 0x96 in einer Einzelbyte-8-Bit-Codierung, die irgendwo auf dem Weg falsch transkodiert wurde. Wahrscheinlich war dies ursprünglich ein Windows CP1252 mit Bindestrich "-", der in dieser Codierung den Bytewert 96 hat - bei den meisten anderen plausiblen Kandidaten ist die Steuerung auf die Positionen 80-9F eingestellt -, der in UTF-8 übersetzt wurde, als wäre er lateinisch. 1 ( ISO / IEC 8859-1 ), was nicht ungewöhnlich ist. Dies würde dazu führen, dass das Byte als Steuerzeichen interpretiert und entsprechend übersetzt wird, wie Sie gesehen haben.


Sie können diese Datei mit dem iconvTool reparieren , das Teil von glibc ist.

iconv -f utf-8 -t iso-8859-1 < mwe.txt | iconv -f cp1252 -t utf-8

produziert eine korrekte Version Ihres minimalen Beispiels für mich. Dies funktioniert, indem zuerst UTF-8 in Latin-1 konvertiert wird (die frühere Fehlübersetzung wird invertiert) und dann als cp1252 neu interpretiert , um es wieder korrekt in UTF-8 zu konvertieren.

Es hängt jedoch davon ab, was sich sonst noch in der realen Datei befindet. Wenn Sie anderswo Zeichen außerhalb von Latin-1 haben, schlägt dies fehl, da diese im ersten Schritt nicht korrekt codiert werden können.

Wenn Sie kein iconv haben oder es für die reale Datei nicht funktioniert, können Sie die Bytes direkt mit sed ersetzen:

LC_ALL=C sed -e $'s/\xc2\x96/\xe2\x80\x93/g' < mwe.txt

Dies wird C2 96durch die UTF-8-En-Dash-Codierung ersetzt E2 80 93. Man könnte es auch mit zB einem Bindestrich oder zwei ersetzen durch eine Änderung \xe2\x80\x93in --.


Sie können auf ähnliche Weise grep. Wir verwenden LC_ALL=C, um sicherzustellen, dass wir die tatsächlichen Bytes lesen und keine grepInterpretationssachen haben:

LC_ALL=C grep -R $'\xc2\x96` .

listet überall in diesem Verzeichnis diese Bytes auf. Sie können es auf Textdateien beschränken, wenn Sie gemischten Inhalt haben, da Binärdateien ziemlich oft ein beliebiges Bytepaar enthalten.

Michael Homer
quelle
Gibt es eine Möglichkeit, andere Dateien nach dem gleichen Vorkommen zu durchsuchen grep?
Paulo Ney
1
Ja, Sie können verwenden grep $'\xc2\x96'(letzter Abschnitt).
Michael Homer
Ist die Datei eine "gültige" UTF-8-Datei?
Paulo Ney
2
Ja, es ist eine vollkommen korrekte Codierung eines nicht sehr nützlichen Zeichens.
Michael Homer
1
zu sagen, dass Windows-1252, das als ISO-8859-1 falsch bezeichnet wurde, ein häufiges Problem ist, ist eine ziemliche Untertreibung ;-); iso-8859-1 sollte immer als Synonym für windows-1252 behandelt werden, es sei denn, Sie möchten Ihre Daten in den Papierkorb werfen (dies ist auch im HTML5-Standard vorgeschrieben - und ich habe noch nie eine echte iso-8859-1-Datei gesehen, die dies nicht war das Ergebnis eines Codierungsfehlers).
Onkel Billy
2

0x96 ist ein Bindestrich in der Windows-Codepage 1252. Das c2vorangestellte Byte scheint ein Standard-Erstbyte in einem Zeichen mit doppelter Breite zu sein. Jemand anderes könnte es genauer erklären.

Um nach anderen Vorkommen zu suchen, ylbewegen Sie den Mauszeiger im Befehlsmodus darüber, drücken Sie (ziehen Sie ein Zeichen) und geben Sie ein /<Ctrl>+r". (Mit Strg + r können Sie den Inhalt eines Registers in den Befehl einfügen, und das "Register ist das, was zuletzt gezogen wurde.)

Ersetzen Sie es einfach durch zwei Bindestriche, wenn Sie möchten, dass es in Ihrem Terminal gerendert wird. Wenn es sich um eine Bibtex-Datei handelt, die Sie haben, sind zwei Bindestriche die geeignete Methode, um sie einzugeben.

Um zu zeigen, wie Sie Vorkommen des Charakters finden können, können Sie ihn durch ein Hexdump-Tool wie leiten xxd.

$ cat tmp | xxd | grep c296
00000000: 7061 6765 733d 7b31 c296 3935 7d2c 0a70  pages={1..95},.p
00000020: 6765 733d 7b31 c296 3935 7d2c 0a70 6167  ges={1..95},.pag
00000040: 733d 7b31 c296 3935 7d2c 0a70 6167 6573  s={1..95},.pages
00000060: 7b31 c296 3935 7d2c 0a70 6167 6573 3d7b  {1..95},.pages={
00000080: c296 3935 7d2c 0a70 6167 6573 3d7b 31c2  ..95},.pages={1.
00000090: 9639 357d 2c0a 7061 6765 733d 7b31 c296  .95},.pages={1..
000000b0: 357d 2c0a 7061 6765 733d 7b31 c296 3935  5},.pages={1..95
000000d0: 2c0a 7061 6765 733d 7b31 c296 3935 7d2c  ,.pages={1..95},
000000f0: 7061 6765 733d 7b31 c296 3935 7d2c 0a70  pages={1..95},.p
00000110: 6765 733d 7b31 c296 3935 7d2c 0a70 6167  ges={1..95},.pag
00000130: 733d 7b31 c296 3935 7d2c 0a70 6167 6573  s={1..95},.pages
00000150: 7b31 c296 3935 7d2c 0a70 6167 6573 3d7b  {1..95},.pages={
glglgl
quelle
Nett. Gibt es eine Möglichkeit, mit etwas wie nach Vorkommen in anderen Dateien zu suchen grep?
Paulo Ney
7
In ASCII gibt es keinen 0x96-Wert - vermutlich in einer 8-Bit-Codierung (ich habe über cp1252 spekuliert, aber es gibt andere Optionen).
Michael Homer
@PauloNey Sie können es durch einen Hex-Dump-Util wie passieren xxd. Siehe meine aktualisierte Antwort.
1

Der Text in Ihrer Datei lautet pages = {1113},: Ja, es sieht aus wie die Nummer, 1113aber nach dem ersten steht tatsächlich ein anderes Zeichen 1. Und ja, Sie können die Zeichenfolge über den Bearbeitungslink für diese Webseite kopieren und einfügen, um das codierte Zeichen zu erhalten.

Wir können mit einigen Werkzeugen in die Zeichenfolge schauen:

$ a='pages = {1113},'

Oder um es explizit zu verdeutlichen und ein einfaches Kopieren und Einfügen zu ermöglichen, ohne die Bearbeitungsseite zu verwenden:

$ a=$(printf 'pages = {1\xc2\x96113},') 

$ echo "$a" | od -An -tx1c
  70  61  67  65  73  20  3d  20  7b  31  c2  96  31  31  33  7d
   p   a   g   e   s       =       {   1 302 226   1   1   3   }
  2c  0a
   ,  \n

$ echo "$a" | sed -n l
pages = {1\302\226113},$

$ echo "$a" | xxd
00000000: 7061 6765 7320 3d20 7b31 c296 3131 337d  pages = {1..113}
00000010: 2c0a

Das Zeichen besteht also aus zwei Byte-Werten c2 96(in Hex) oder 302 226(in Oktal).
Es ist wahrscheinlich die UTF-8-Codierung eines Bytewerts von 96oder ausgedrückt als Unicode-Zeichen : U-0096.

Dieser Wert, in der heutigen Zeit UTF-8 oder noch besser in ISO-8859-1, ist ein Steuerzeichen im C1-Bereich der Steuerzeichen ( Wikipedia-Seite ) und ( Unicode PDF ), das von 128 bis 159 in Dezimalzahl reicht. Insbesondere wird der U-0096 als "START OF GUARDED AREA" oder SPA bezeichnet .

Dieser Wert (Dez. 150) liegt außerhalb des ASCII-Bereichs (0-127) und wurde (in früheren Zeiten) verwendet, um abhängig von der verwendeten Codepage mehrere Zeichen darzustellen. Es scheint vernünftig anzunehmen, dass es sich zuvor um einen Bindestrich (um den Bereich 1-113 zu markieren) handelte, der in Windows-1252 ( Microsoft-Seite ) ( Wikipedia 1252 ) codiert und als en-Bindestrich bezeichnet wurde (der kleinere der beiden Bindestriche en und) em ) ( Wikipedia en dash ) oder einfach, in Laienbegriffen, ein Bindestrich ( -).


Q1: Stimmt etwas mit dieser Datei nicht?

Nicht wirklich, Steuerzeichen sind gültige Zeichen, die selten verwendet werden, aber trotzdem gültig sind.

Sie können sie jedoch durch einen Bindestrich ersetzen, um die Bearbeitung zu vereinfachen.

<file.txt sed 's/\xc2\x96/-/'

F2 - Wie kann ich in derselben Datei nach anderen Vorkommen suchen?

sed -n '/\xc2\x96/p'             # will print lines that contain that character.

Oder grep könnte nach dem Zeichen suchen (die Hervorhebung der Farbe ist nicht sichtbar, da das Zeichen nicht druckbar ist) und die Zeile drucken.

c="$(printf "\U96")" ; grep "$c" file.txt

Oder finden Sie alle Zeichen in diesem Steuerzeichenbereich und listen Sie die Dateien auf, die solche Zeichen enthalten:

grep -rlP "[\x80-\x9f]"    

F3 - Wie kann ich nach anderen Dateien suchen, die möglicherweise dasselbe Problem / denselben Charakter enthalten?

Dadurch werden -ldie Dateien aufgelistet ( ), die dem Zeichen entsprechen.

grep -rlP "\x96"
Isaac
quelle