web-dev-qa-db-fra.com

Erreur RE: séquence d'octets non conforme sur Mac OS X

J'essaie de remplacer une chaîne dans un Makefile sur Mac OS X pour une compilation croisée sur iOS. La chaîne contient des guillemets doubles. La commande est:

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

Et l'erreur est la suivante:

sed: RE error: illegal byte sequence

J'ai essayé d'échapper aux guillemets, aux virgules, aux tirets et aux deux points sans joie. Par exemple:

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

Je passe énormément de temps à résoudre le problème. Est-ce que quelqu'un sait comment obtenir sed pour imprimer la position de la séquence d'octets illégale? Ou est-ce que quelqu'un sait ce qu'est la séquence d'octets illégale?

156
jww

Un exemple de commande présentant le symptôme suivant: sed 's/./@/' <<<$'\xfc' échoue, car l'octet 0xfc n'est pas un caractère UTF-8 valide.
Notez que, au contraire, GNUsed (Linux, mais peut également être installé sur macOS) ne fait que passer l'octet invalide, sans générer de rapport. Erreur.

L'utilisation de réponse autrefois acceptée est une option possible si vous ne craignez pas de perdre le support de vos paramètres régionaux véritables (si vous êtes sur un système américain et que vous n'avez jamais besoin de vous en occuper. caractères étrangers, ça peut aller.)

Cependant, le même effet peut être eu ad-hoc pour une commande unique uniquement:

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

Remarque: ce qui compte est un réglage efficace LC_CTYPE de C, de sorte que LC_CTYPE=C sed ... serait normalement fonctionne également, mais si LC_ALL est défini (sur autre chose que C), il remplacera les variables individuelles LC_*- telles que LC_CTYPE. Ainsi, l’approche la plus robuste consiste à définir LC_ALL.

Cependant, régler (effectivement) LC_CTYPE sur C traite les chaînes comme si chaque octet était son propre caractère ( no basé sur l'interprétation sur les règles de codage est effectuée), avec no égard pour - multibyte-on-demand - codage UTF-8 utilisé par OS X par défaut, où foreign caractères ont codages multi-octets.

En bref: paramètre LC_CTYPE à C permet au shell et aux utilitaires de ne reconnaître que les lettres anglaises de base en tant que lettres (celles du 7 bits ASCII gamme), de sorte que caractères étrangers. ne seront pas traités comme des lettres, ce qui entraînera, par exemple, l'échec des conversions majuscules/minuscules.

Encore une fois, cela peut suffire si vous n'avez pas besoin de faire correspondre des caractères codés sur plusieurs octets, tels que é, et que vous souhaitiez simplement les transmettre. par.

Si cela est insuffisant et/ou si vous voulez comprendre la cause de l'erreur d'origine (y compris déterminer quels octets d'entrée ont causé le problème) et effectuer des conversions de codage à la demande, - lisez la suite ci-dessous.


Le problème est que l'encodage du fichier d'entrée ne correspond pas à celui du Shell.
Plus précisément, le fichier d'entrée contient des caractères codés de manière non valide en UTF-8 (comme @Klas Lindbäck l'a indiqué dans un commentaire) - c'est ce que la sed message d'erreur tente de dire par invalid byte sequence.

Très probablement, votre fichier d'entrée utilise un codage sur un octet sur 8 bits tel que ISO-8859-1, fréquemment utilisé pour coder les langues "d'Europe occidentale".

Exemple:

La lettre accentuée à a un point de code Unicode 0xE0 (224) - comme dans ISO-8859-1. Cependant, en raison de la nature du codage UTF-8 , ce code unique est représenté par 2 octets - 0xC3 0xA0 , alors que l’essai de transmettre l’octet unique 0xE0 est invalide sous UTF-8.

Voici une démonstration du problème utilisant la chaîne voilà codée en tant que ISO-8859-1, avec le à représenté par un octet (via une chaîne bash citée par ANSI-C ($'...') qui utilise \x{e0} pour créer l'octet):

Notez que la commande sed est en fait un no-op qui passe simplement l'entrée, mais nous en avons besoin pour provoquer l'erreur:

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

Pour simplement ignorer le problème, l'approche ci-dessus LCTYPE=C peut être utilisée:

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

Si vous voulez déterminer quelles parties de l'entrée sont à l'origine du problème, essayez ce qui suit:

  # 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}'

La sortie affiche tous les octets pour lesquels le bit haut est défini (octets dépassant la plage de 7 bits ASCII) sous forme hexadécimale. (Notez, cependant, que cela inclut également les séquences multi-octets UTF-8 correctement codées - une approche plus sophistiquée serait nécessaire pour identifier spécifiquement les octets invalides dans UTF-8.)


Effectuer des conversions d'encodage à la demande:

L'utilitaire standard iconv peut être utilisé pour convertir les encodages en (-t) et/ou à partir de (-f); iconv -l répertorie tous ceux pris en charge.

Exemples:

Convertissez FROM ISO-8859-1 en codage en vigueur dans le shell (basé sur LC_CTYPE, qui est basé sur UTF-8- par défaut), à partir de l'exemple ci-dessus:

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

Notez que cette conversion vous permet de faire correspondre correctement les caractères étrangers :

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

Pour convertir l'entrée BACK en ISO-8859-1 après traitement, il suffit de diriger le résultat vers une autre commande iconv:

sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')" | iconv -t ISO-8859-1
268
mklement0

Ajoutez les lignes suivantes à vos fichiers ~/.bash_profile ou ~/.zshrc.

export LC_CTYPE=C 
export LANG=C
126

Ma solution de contournement utilisait Perl:

find . -type f -print0 | xargs -0 Perl -pi -e 's/was/now/g'
3
Vitaly Zdanevich

La réponse de mklement c'est bien, mais j'ai quelques petites modifications à apporter.

Cela semble être une bonne idée de spécifier explicitement le codage de bash lorsque vous utilisez iconv. De plus, nous devrions ajouter une marque d'ordre des octets ( même si le standard unicode ne le recommande pas ) car il peut y avoir une confusion légitime entre UTF-8 et ASCII sans marque d'ordre des octets . Malheureusement, iconv ne comporte pas de marque d'ordre d'octet lorsque vous spécifiez explicitement une finalité (UTF-16BE ou UTF-16LE), nous devons donc utiliser UTF-16, qui utilise la plate-forme. -endianness spécifique, puis utilisez file --mime-encoding pour découvrir le véritable endianness iconv utilisé.

(Je mets en majuscule tous mes encodages car lorsque vous listez tous les encodages pris en charge par iconv avec iconv -l ils sont tous en majuscules.)

# 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
3
Heath Borders

Vous devez simplement diriger une commande iconv avant la commande sed. Ex avec entrée file.txt:

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

- f option est le jeu de codes 'à partir de' et l'option -t est la conversion 'à' jeux de codes.

Attention, les pages Web affichent généralement des minuscules telles que <charset = iso-8859-1 "/> et iconv utilise des majuscules. Vous avez la liste des iconv jeux de codes pris en charge dans votre système avec la commande iconv -l

TF8-MAC est un jeu de codes Mac OS moderne pour la conversion.

Ma solution de contournement utilisait gnu sed. A bien fonctionné pour mes besoins.

0
lu_zero