RE错误:在Mac OS X上的非法字节序列

我试图在Mac OS X上替换Makefile中的字符串,以便交叉编译到iOS。字符串内嵌了双引号。命令如下:

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

错误是:

sed: RE error: illegal byte sequence

我曾经尝试过转义双引号、逗号、破折号和冒号,但没有任何乐趣。例如:

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

我在调试这个问题上花了不少时间。有人知道如何让sed打印非法字节序列的位置吗?有人知道非法字节序列是什么吗?

180560 次浏览

将以下行添加到~/.bash_profile~/.zshrc文件中。

export LC_CTYPE=C
export LANG=C

一个显示如下现象的示例命令:sed 's/./@/' <<<$'\xfc'失败,因为字节0xfc不是一个有效的UTF-8字符 注意,相比之下,GNU sed (Linux,但也可安装在macOS上)只是简单地传递无效字节,而不报告错误

使用以前接受的答案是一个选项,如果你不介意失去对真实地区的支持(如果你在美国系统上,你永远不需要处理外国字符,这可能是好的。)

然而,对于单一命令ad-hoc也有同样的效果:

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

注意:重要的是有效的 LC_CTYPE设置为C,因此LC_CTYPE=C sed ...正常情况下也可以工作,但如果LC_ALL恰好被设置(为C以外的值),它将覆盖单个__abc5类别变量,如LC_CTYPE。因此,最健壮的方法是设置LC_ALL

然而,(有效地)将LC_CTYPE设置为C处理字符串就好像每个字节都是它自己的字符(执行基于编码规则的没有解释),而不顾用于OS X默认使用的- multibyte- demand - utf - 8编码,其中外国字符多字节编码

简而言之:将__ABC0设置为C导致shell和实用程序只将基本的英文字母识别为字母(7位ASCII范围内的字母),从而导致外国字符。会不会被当成信件,例如,导致大小写转换失败。

同样,如果你不需要匹配多字节编码字符,如é,而只是想传递这样的字符,这可能很好。

如果这是不够的和/或你想了解原因的原始错误(包括确定什么输入字节导致的问题)和执行编码转换按需,下面的继续读下去


问题是输入文件的编码与shell的不匹配 更具体地说,输入文件包含以UTF-8无效的方式编码的字符(正如@Klas Lindbäck在评论中所述)-这就是sed错误消息试图通过invalid byte sequence说的

最有可能的是,你的输入文件使用单字节8位编码,例如ISO-8859-1,经常用于编码“西欧”语言。

例子:

重读字母à具有Unicode代码点0xE0(224) -与ISO-8859-1相同。然而,由于utf - 8编码的性质,这个单码点被表示为2字节- 0xC3 0xA0,而试图传递单字节 0xE0则是UTF-8下的无效的

下面是一个问题的演示,使用字符串voilà编码为ISO-8859-1à表示为一个字节(通过一个ansi - c引用的bash字符串($'...'),使用\x{e0}创建字节):

注意,sed命令实际上是一个简单地传递输入的no-op,但我们需要它来引发错误:

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

要简单地ignore问题,可以使用上面的LCTYPE=C方法:

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

如果你想确定输入的哪一部分导致了问题,试试下面的方法:

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

输出将以十六进制形式显示具有高位集的所有字节(超过7位ASCII范围的字节)。(但请注意,这也包括正确编码的UTF-8多字节序列——需要更复杂的方法来明确识别UTF-8字节中的无效字节。)


按需执行编码转换:

标准实用程序iconv可用于转换为(-t)和/或从(-f)编码;iconv -l列出所有支持的。

例子:

ISO-8859-1转换为shell中有效的编码(基于LC_CTYPE,默认情况下基于__abc2),构建在上面的示例之上:

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

注意这个转换允许您正确匹配外部字符:

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

要在处理后将输入转换回ISO-8859-1,只需将结果管道到另一个iconv命令:

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

mklement0的回答是伟大的,但我有一些小调整。

在使用iconv时显式指定bash的编码似乎是个好主意。另外,我们应该在前面加上一个字节顺序标记(尽管统一码标准不推荐这么做),因为UTF-8和没有字节顺序标记的ASCII之间可能存在合法的混淆。不幸的是,iconv在显式指定一个字节顺序符号(UTF-16BEUTF-16LE)时没有前置字节顺序标记,因此我们需要使用UTF-16,它使用特定于平台的字节顺序,然后使用file --mime-encoding来发现iconv使用的真正的字节顺序。

(我把所有的编码都大写,因为当你用iconv -l列出所有iconv支持的编码时,它们都是大写的。)

# 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

我的解决方案一直使用gnu sed。对我来说还行。

我的解决方法是使用Perl:

find . -type f -print0 | xargs -0 perl -pi -e 's/was/now/g'
你只需要在sed命令之前插入一个iconv命令。 输出文件:

.txt

iconv - f iso - 8859 - 1 - t UTF8-MAC file.txt | sed的s /一些/ aeeecuu / g |…

- f选项是“从”码集,而-t选项是“到”码集转换。

注意大小写,网页通常显示小写字母<charset = iso - 8859 - 1 " / > 和iconv使用大写。 通过命令iconv - l

,您可以在系统中获得iconv支持的代码集列表

UTF8-MAC是现代OS Mac转换代码集。

有人知道如何让sed打印非法字节序列的位置吗?有人知道非法字节序列是什么吗?

$ 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

我通过使用tr来回答上面的问题。

我有一个.csv文件,这是一个信用卡对账单,我试图将其导入Gnucash。我住在瑞士,所以我必须处理Zürich这样的单词。怀疑Gnucash不喜欢数字字段中的“”,我决定简单地替换所有

; ;

;;

是:

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

我使用od来阐明一些问题:注意od - c输出中间的374

$ 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

然后我想我可以尝试说服tr将374替换为正确的字节码。所以首先我尝试了一些简单的东西,它没有工作,但有一个副作用,显示我麻烦的字节在哪里:

$ 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

你可以看到tr在374字符处释放。

使用perl似乎可以避免这个问题

$ 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