RE-Fehler: Unzulässige Bytesequenz unter Mac OS X.

183

Ich versuche, eine Zeichenfolge in einem Makefile unter Mac OS X für das Cross-Compilieren auf iOS zu ersetzen. Die Zeichenfolge enthält doppelte Anführungszeichen. Der Befehl lautet:

sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure

Und der Fehler ist:

sed: RE error: illegal byte sequence

Ich habe versucht, den doppelten Anführungszeichen, Kommas, Bindestrichen und Doppelpunkten ohne Freude zu entkommen. Beispielsweise:

sed -i "" 's|\"iphoneos-cross\"\,\"llvm-gcc\:\-O3|\"iphoneos-cross\"\,\"clang\:\-Os|g' Configure

Ich habe verdammt viel Zeit damit, das Problem zu beheben. Weiß jemand, wie man seddie Position der illegalen Byte-Sequenz druckt? Oder weiß jemand, was die illegale Bytesequenz ist?

jww
quelle
2
Eine unzulässige Byte-Sequenz klingt nach etwas, das Sie erhalten, wenn Sie 8-Bit-ASCII an etwas weitergeben, das utf-8 erwartet.
Klas Lindbäck
36
Können Sie versuchen:LC_CTYPE=C && LANG=C && sed command
Anubhava
5
Danke Leute. Es war das LANGDing.
Seufz
3
@ user2719058: BSD sed(wie auch unter OS X verwendet) erfordert -i ''(separates Optionsargument mit leeren Zeichenfolgen) für die direkte Aktualisierung ohne Sicherungsdatei. mit GNU funktioniert sednur -ivon selbst - siehe stackoverflow.com/a/40777793/45375
mklement0
1
Plus eins für die LANG-Sache. Meine Güte, das ist dunkel, nicht offensichtlich und überraschend schwer zu erforschen.
Spudley

Antworten:

298

Ein Beispielbefehl mit dem folgenden Symptom sed 's/./@/' <<<$'\xfc'schlägt fehl, da das Byte 0xfckein gültiges UTF-8-Zeichen ist.
Beachten Sie, dass GNU sed (Linux, aber auch unter macOS installierbar) das ungültige Byte einfach weiterleitet, ohne einen Fehler zu melden.

Die Verwendung der zuvor akzeptierten Antwort ist eine Option, wenn es Ihnen nichts ausmacht, die Unterstützung für Ihr wahres Gebietsschema zu verlieren (wenn Sie sich in einem US-System befinden und nie mit fremden Zeichen umgehen müssen, kann dies in Ordnung sein.)

Doch das kann die gleiche Wirkung werden mußte Ad-hoc für einen einzigen Befehl nur :

LC_ALL=C sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure

Hinweis: Was zählt, ist eine effektive LC_CTYPE Einstellung von C, LC_CTYPE=C sed ...die normalerweise auch funktioniert. Wenn LC_ALLsie jedoch (auf etwas anderes als C) eingestellt wird, werden einzelne LC_*Variablen der Kategorie wie z LC_CTYPE. Der robusteste Ansatz ist daher das Festlegen LC_ALL.

Die (effektive) Einstellung LC_CTYPE, CZeichenfolgen so zu behandeln, als ob jedes Byte ein eigenes Zeichen wäre (es wird keine Interpretation basierend auf Codierungsregeln durchgeführt), ohne Berücksichtigung der UTF-8-Codierung (Multibyte-on-Demand) , die OS X standardmäßig verwendet , wo fremde Zeichen haben Multibyte - Codierungen .

Kurz gesagt: Die Einstellung LC_CTYPEaufC bewirkt , dass die Shell und die Dienstprogramme nur grundlegende englische Buchstaben als Buchstaben erkennen (diejenigen im 7-Bit-ASCII-Bereich), sodass fremde Zeichen entstehen. werden nicht als Buchstaben behandelt , was beispielsweise dazu führt, dass Konvertierungen in Groß- / Kleinbuchstaben fehlschlagen.

Auch dies kann in Ordnung sein, wenn Sie nicht mit Multibyte-codierten Zeichen wie übereinstimmené müssen und diese Zeichen einfach weitergeben möchten .

Wenn dies nicht ausreicht und / oder Sie die Ursache des ursprünglichen Fehlers verstehen möchten (einschließlich der Ermittlung, welche Eingabebytes das Problem verursacht haben) und bei Bedarf Codierungskonvertierungen durchführen möchten , lesen Sie weiter unten.


Das Problem ist, dass die Codierung der Eingabedatei nicht mit der der Shell übereinstimmt.
Genauer gesagt, enthält die Eingabedatei in einer Weise codierten Zeichen , die nicht gültig in UTF-8 (als @Klas Lindbäck in einem Kommentar angegeben) - das ist , was die sedFehlermeldung von zu sagen versucht invalid byte sequence.

Höchstwahrscheinlich verwendet Ihre Eingabedatei eine Einzelbyte-8-Bit-Codierung, wie sie ISO-8859-1häufig zum Codieren von "westeuropäischen" Sprachen verwendet wird.

Beispiel:

Der akzentuierte Buchstabe àhat den Unicode-Codepunkt 0xE0(224) - der gleiche wie in ISO-8859-1. Aufgrund der Art der UTF-8- Codierung wird dieser einzelne Codepunkt jedoch als 2 Byte dargestellt 0xC3 0xA0, während der Versuch, das einzelne Byte zu übergeben, unter UTF-8 ungültig0xE0 ist .

Hier ist eine Demonstration des Problems unter Verwendung der Zeichenfolge voilà, die als codiert ist ISO-8859-1, wobei die Zeichenfolge àals ein Byte dargestellt wird (über eine in ANSI-C zitierte Bash-Zeichenfolge ( $'...'), mit \x{e0}der das Byte erstellt wird):

Beachten Sie, dass der sedBefehl praktisch ein No-Op ist, der die Eingabe einfach weiterleitet, aber wir brauchen ihn, um den Fehler zu provozieren:

  # -> 'illegal byte sequence': byte 0xE0 is not a valid char.
sed 's/.*/&/' <<<$'voil\x{e0}'

Um das Problem einfach zu ignorieren , kann der obige LCTYPE=CAnsatz verwendet werden:

  # No error, bytes are passed through ('á' will render as '?', though).
LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'

Wenn Sie feststellen möchten, welche Teile der Eingabe das Problem verursachen , versuchen Sie Folgendes:

  # Convert bytes in the 8-bit range (high bit set) to hex. representation.
  # -> 'voil\x{e0}'
iconv -f ASCII --byte-subst='\x{%02x}' <<<$'voil\x{e0}'

Die Ausgabe zeigt Ihnen alle Bytes, für die das High-Bit gesetzt ist (Bytes, die den 7-Bit-ASCII-Bereich überschreiten), in hexadezimaler Form. (Beachten Sie jedoch, dass dies auch korrekt codierte UTF-8-Multibyte-Sequenzen umfasst - ein komplexerer Ansatz wäre erforderlich, um In-UTF-8-Bytes spezifisch ungültig zu identifizieren.)


Codierungskonvertierungen bei Bedarf durchführen :

Das Standarddienstprogramm iconvkann zum Konvertieren in ( -t) und / oder von ( -f) -Codierungen verwendet werden. iconv -llistet alle unterstützten auf.

Beispiele:

Konvertieren Sie FROM ISO-8859-1in die in der Shell gültige Codierung (basierend auf LC_CTYPE, die UTF-8standardmäßig basiert ), basierend auf dem obigen Beispiel:

  # Converts to UTF-8; output renders correctly as 'voilà'
sed 's/.*/&/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')"

Beachten Sie, dass Sie mit dieser Konvertierung Fremdzeichen richtig abgleichen können :

  # Correctly matches 'à' and replaces it with 'ü': -> 'voilü'
sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')"

Um die Eingabe ISO-8859-1nach der Verarbeitung wieder in umzuwandeln, leiten Sie das Ergebnis einfach an einen anderen iconvBefehl weiter:

sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')" | iconv -t ISO-8859-1
mklement0
quelle
4
Ich würde sagen, das ist eine viel bessere Option. Erstens möchte ich nicht die mehrsprachige Unterstützung in ganz Terminal verlieren. Zweitens fühlt sich die akzeptierte Antwort wie eine globale Lösung für ein lokales Problem an - etwas, das vermieden werden sollte.
Alex
Ich hatte ein paar kleine Änderungen daran. Ich würde mich über Feedback freuen. stackoverflow.com/a/35046218/9636
Heath Borders
LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'druckt sed: RE error: illegal byte sequencefür mich auf Sierra. echo $LC_ALLgibt en_US.UTF-8FWIW aus.
Ahcox
1
@ahcox: Ja, da die Einstellung alle anderen Variablen LC_ALL überschreibtLC_* , einschließlich LC_CTYPE, wie in der Antwort erläutert.
mklement0
2
@ mklement0 Cool, das funktioniert: "LC_ALL = C sed 's /.*/&/' <<< $ 'voil \ x {e0}'". Vorrang
ahcox
142

Fügen Sie die folgenden Zeilen zu Ihren ~/.bash_profileoder Ihren ~/.zshrcDateien hinzu.

export LC_CTYPE=C 
export LANG=C
binarytemple_picsolve
quelle
29
es funktioniert tatsächlich, aber können Sie bitte erklären, warum?
Hoang Pham
11
@HoangPham: Wenn Sie festlegen LC_CTYPE, Cdass jedes Byte in Zeichenfolgen ein eigenes Zeichen ist, ohne dass Codierungsregeln angewendet werden. Da ein Verstoß gegen (UTF-8) -Codierungsregeln das ursprüngliche Problem verursacht hat, verschwindet das Problem. Der Preis, den Sie zahlen, ist jedoch, dass die Shell und die Dienstprogramme dann nur die grundlegenden englischen Buchstaben (die im 7-Bit-ASCII-Bereich) als Buchstaben erkennen. Siehe meine Antwort für mehr.
mklement0
6
Wenn Sie dies dauerhaft in den Startdateien Ihrer Shell festlegen, werden viele nützliche Verhaltensweisen deaktiviert. Sie möchten dies nur für einzelne Befehle eingeben, die dies unbedingt erfordern.
Tripleee
4
Zu gefährlich kann unerwartete Folgen haben. Man könnte verwenden LC_CTYPE=C sed …, dh nur auf den Befehl sed.
Yongwei Wu
2
Dadurch wird die Unterstützung für Unicode-Zeichen in Ihrer Shell vollständig deaktiviert. Auf Wiedersehen Emojis, ausgefallene Strichzeichnungen, Buchstaben mit Akzenten, ... Viel besser, dies nur für den Befehl sed festzulegen, wie in anderen Antworten beschrieben.
Asmeurer
6

Meine Problemumgehung war die Verwendung von Perl:

find . -type f -print0 | xargs -0 perl -pi -e 's/was/now/g'
Vitaly Zdanevich
quelle
Dieser funktioniert großartig. Und ich hatte im Gegensatz zu den anderen keine Fehler beim Entkommen von Sonderzeichen. Die vorherigen gaben mir Probleme wie "sed: RE-Fehler: unzulässige Bytesequenz" oder sed: 1: "path_to_file": ungültiger Befehlscode.
JMags1632
3

Die Antwort von mklement0 ist großartig, aber ich habe einige kleine Verbesserungen.

Es scheint eine gute Idee zu sein, bashdie Codierung bei der Verwendung explizit anzugeben iconv. Außerdem sollten wir ein Zeichen für die Bytereihenfolge voranstellen ( obwohl der Unicode-Standard dies nicht empfiehlt ), da es zu legitimen Verwechslungen zwischen UTF-8 und ASCII ohne ein Zeichen für die Bytereihenfolge kommen kann . Leider wird iconvkeine Bytereihenfolge vorangestellt, wenn Sie explizit eine Endianness ( UTF-16BEoder UTF-16LE) angeben. Daher müssen wir diese verwenden UTF-16, die plattformspezifische Endianness verwendet, und dann verwenden file --mime-encoding, um die tatsächlich verwendete Endianness zu ermitteln iconv.

(Ich schreibe alle meine Codierungen in Großbuchstaben, denn wenn Sie alle iconvunterstützten Codierungen iconv -lauflisten, sind sie alle in Großbuchstaben.)

# Find out MY_FILE's encoding
# We'll convert back to this at the end
FILE_ENCODING="$( file --brief --mime-encoding MY_FILE )"
# Find out bash's encoding, with which we should encode
# MY_FILE so sed doesn't fail with 
# sed: RE error: illegal byte sequence
BASH_ENCODING="$( locale charmap | tr [:lower:] [:upper:] )"
# Convert to UTF-16 (unknown endianness) so iconv ensures
# we have a byte-order mark
iconv -f "$FILE_ENCODING" -t UTF-16 MY_FILE > MY_FILE.utf16_encoding
# Whether we're using UTF-16BE or UTF-16LE
UTF16_ENCODING="$( file --brief --mime-encoding MY_FILE.utf16_encoding )"
# Now we can use MY_FILE.bash_encoding with sed
iconv -f "$UTF16_ENCODING" -t "$BASH_ENCODING" MY_FILE.utf16_encoding > MY_FILE.bash_encoding
# sed!
sed 's/.*/&/' MY_FILE.bash_encoding > MY_FILE_SEDDED.bash_encoding
# now convert MY_FILE_SEDDED.bash_encoding back to its original encoding
iconv -f "$BASH_ENCODING" -t "$FILE_ENCODING" MY_FILE_SEDDED.bash_encoding > MY_FILE_SEDDED
# Now MY_FILE_SEDDED has been processed by sed, and is in the same encoding as MY_FILE
Heidegrenzen
quelle
1
++ für hilfreiche Techniken, insbesondere file -b --mime-encodingzum Erkennen und Melden der Codierung einer Datei. Es gibt jedoch einige Aspekte, die es wert sind, angesprochen zu werden, was ich in separaten Kommentaren tun werde.
mklement0
2
Ich denke, man kann mit Sicherheit sagen, dass die Unix-Welt zu diesem Zeitpunkt UTF-8 angenommen hat: Der Standardwert LC_CTYPEist normalerweise <lang_region>.UTF-8, daher wird jede Datei ohne Stückliste (Byte-Order-Markierung) daher als UTF-8-Datei interpretiert. Nur in der Windows- Welt wird die Pseudo-Stückliste 0xef 0xbb 0xff verwendet. definitions UTF-8 nicht brauchen eine Stückliste und wird nicht empfohlen (wie Sie Zustand); Außerhalb der Windows-Welt führt diese Pseudo-Stückliste dazu, dass Dinge kaputt gehen .
mklement0
2
Betreff Unfortunately, iconv doesn't prepend a byte-order mark when you explicitly specify an endianness (UTF-16BE or UTF-16LE): Das ist beabsichtigt: Wenn Sie die Endianness explizit angeben , müssen Sie sie nicht auch über eine Stückliste wiedergeben, sodass keine hinzugefügt wird.
mklement0
1
Re LC_*/ LANGVariablen: bash, ksh, und zsh(möglicherweise andere, aber nicht dash ) tun , um die Zeichenkodierung respektieren; Überprüfen Sie in POSIX-ähnlichen Shells mit einem UTF-8-basierten Gebietsschema Folgendes v='ä'; echo "${#v}": Eine UTF-8-fähige Shell sollte einen Bericht erstellen 1. dh es sollte die Mehrbyte-Sequenz ä( 0xc3 0xa4) als einzelnes Zeichen erkennen. Vielleicht noch wichtiger ist jedoch: Die Standard - Utilities ( sed, awk, cut, ...) auch locale / Codierung bewusst sein müssen, und während die meisten von ihnen auf modernen Unix-Plattformen sind, gibt es Ausnahmen, wie awkauf OSX, und cutunter Linux.
mklement0
1
Es ist lobenswert, dass filedie UTF-8-Pseudo-Stückliste erkannt wird, aber das Problem ist, dass die meisten Unix-Dienstprogramme, die Dateien verarbeiten, dies nicht tun und sich normalerweise brechen oder zumindest schlecht verhalten, wenn sie mit einer konfrontiert werden. fileIdentifiziert ohne Stückliste eine 7-Bit-Byte-Datei korrekt als ASCII und eine Datei mit gültigen UTF-8-Mehrbyte-Zeichen als UTF-8. Das Schöne an UTF-8 ist, dass es eine Obermenge von ASCII ist: Jede gültige ASCII-Datei ist per Definition eine gültige UTF-8-Datei (aber nicht umgekehrt); Es ist vollkommen zu sicher, eine ASCII-Datei als UTF-8 zu behandeln (was technisch gesehen einfach keine
Mehrbyte
2

Sie müssen lediglich einen iconv- Befehl vor dem sed- Befehl weiterleiten . Ex mit file.txt Eingabe:

iconv -f ISO-8859-1 -t UTF8-MAC file.txt | sed 's / etwas / àéèêçùû / g' | ..... .....

Die Option -f ist der Codesatz 'von' und die Option -t ist die Konvertierung des Codesatzes 'nach'.

Achten Sie auf Groß- und Kleinschreibung. Webseiten zeigen normalerweise Kleinbuchstaben wie <charset = iso-8859-1 "/> und iconv verwendet Großbuchstaben. Sie haben eine Liste der von iconv unterstützten Codesätze in Ihrem System mit dem Befehl iconv -l

UTF8-MAC ist ein moderner OS Mac-Codesatz für die Konvertierung.

Denis aus Val Thorens
quelle
Siehe auch iconv- und charset-Namen in der iconv-Mailingliste.
JWW
1

Weiß jemand, wie man sediert, um die Position der illegalen Byte-Sequenz zu drucken? Oder weiß jemand, was die illegale Bytesequenz ist?

$ uname -a
Darwin Adams-iMac 18.7.0 Darwin Kernel Version 18.7.0: Tue Aug 20 16:57:14 PDT 2019; root:xnu-4903.271.2~2/RELEASE_X86_64 x86_64

Ich habe einen Teil des Weges zur Beantwortung der oben genannten Fragen mit tr .

Ich habe eine CSV-Datei, bei der es sich um eine Kreditkartenabrechnung handelt, und ich versuche, sie in Gnucash zu importieren. Ich bin in der Schweiz ansässig und muss mich mit Worten wie Zürich auseinandersetzen. Bei dem Verdacht, dass Gnucash in numerischen Feldern nicht "" mag, entscheide ich mich, einfach alle zu ersetzen

; ;

mit

;;

Hier geht:

$ head -3 Auswertungen.csv | tail -1 | sed -e 's/; ;/;;/g'
sed: RE error: illegal byte sequence

Ich habe od verwendet , um etwas Licht ins Dunkel zu bringen: Beachten Sie den 374 auf halber Höhe dieses od-c- Ausgangs

$ head -3 Auswertungen.csv | tail -1 | od -c
0000000    1   6   8   7       9   6   1   9       7   1   2   2   ;   5
0000020    4   6   8       8   7   X   X       X   X   X   X       2   6
0000040    6   0   ;   M   Y       N   A   M   E       I   S   X   ;   1
0000060    4   .   0   2   .   2   0   1   9   ;   9   5   5   2       -
0000100        M   i   t   a   r   b   e   i   t   e   r   r   e   s   t
0000120                Z 374   r   i   c   h                            
0000140    C   H   E   ;   R   e   s   t   a   u   r   a   n   t   s   ,
0000160        B   a   r   s   ;   6   .   2   0   ;   C   H   F   ;    
0000200    ;   C   H   F   ;   6   .   2   0   ;       ;   1   5   .   0
0000220    2   .   2   0   1   9  \n                                    
0000227

Dann dachte ich, ich könnte versuchen, tr davon zu überzeugen , den richtigen Bytecode durch 374 zu ersetzen. Also habe ich zuerst etwas Einfaches ausprobiert, das nicht funktioniert hat, aber den Nebeneffekt hatte, mir zu zeigen, wo das störende Byte war:

$ head -3 Auswertungen.csv | tail -1 | tr . .  ; echo
tr: Illegal byte sequence
1687 9619 7122;5468 87XX XXXX 2660;MY NAME ISX;14.02.2019;9552 - Mitarbeiterrest   Z

Sie können tr- Kautionen am 374-Zeichen sehen.

Die Verwendung von Perl scheint dieses Problem zu vermeiden

$ head -3 Auswertungen.csv | tail -1 | perl -pne 's/; ;/;;/g'
1687 9619 7122;5468 87XX XXXX 2660;ADAM NEALIS;14.02.2019;9552 - Mitarbeiterrest   Z?rich       CHE;Restaurants, Bars;6.20;CHF;;CHF;6.20;;15.02.2019
Magische Oberschenkel
quelle
0

Meine Problemumgehung hatte Gnu verwendet sed. Hat für meine Zwecke gut funktioniert.

lu_zero
quelle
In der Tat ist GNU sed eine Option, wenn Sie ungültige Bytes im Eingabestream ignorieren möchten (keine LC_ALL=C sed ...Problemumgehung erforderlich ), da GNU sedeinfach ungültige Bytes weiterleitet, anstatt einen Fehler zu melden. Beachten Sie dies jedoch, wenn Sie alle ordnungsgemäß erkennen und verarbeiten möchten Bei Zeichen in der Eingabezeichenfolge führt kein Weg daran vorbei, zuerst die Codierung der Eingabe zu ändern (normalerweise mit iconv).
mklement0