RE 错误:Mac OS X 上的非法字节序列
Posted
技术标签:
【中文标题】RE 错误:Mac OS X 上的非法字节序列【英文标题】:RE error: illegal byte sequence on Mac OS X 【发布时间】:2013-10-15 01:20:10 【问题描述】:我正在尝试替换 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
打印非法字节序列的位置?或者有谁知道非法字节序列是什么?
【问题讨论】:
非法字节序列听起来像是将 8 位 ascii 输入需要 utf-8 的东西时得到的结果。 你可以试试:LC_CTYPE=C && LANG=C && sed command
谢谢各位。这是LANG
的事情。叹息....
@user2719058:BSD sed
(也用于 OS X)需要 -i ''
(单独的,空字符串选项参数)在没有备份文件的情况下进行就地更新;使用 GNU sed
,只有 -i
本身有效 - 请参阅 ***.com/a/40777793/45375
加一个用于 LANG 的东西。天哪,这是晦涩难懂、不明显且难以研究的。
【参考方案1】:
有谁知道如何让 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。我在瑞士,所以我必须处理像苏黎世这样的词。怀疑 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
您可以在 374 字符处看到 tr 保释。
使用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
【讨论】:
【参考方案2】:您只需在 sed 命令之前通过管道传递一个 iconv 命令。 例如带有 file.txt 输入:
iconv -f ISO-8859-1 -t UTF8-MAC 文件.txt | sed 's/something/àéèêçùû/g' | .....
-f 选项是“从”代码集,-t 选项是“到”代码集转换。
注意大小写,网页一般都是小写的
UTF8-MAC 是用于转换的现代 OS Mac 代码集。
【讨论】:
另见 iconv 邮件列表中的iconv and charset names。【参考方案3】:出现以下症状的示例命令:sed 's/./@/' <<<$'\xfc'
失败,因为字节 0xfc
不是有效的 UTF-8 字符。
请注意,相比之下,GNU sed
(Linux,但也可安装在 macOS 上)只是简单地传递无效字节,而不报告错误。
如果您不介意失去对真实语言环境的支持,则可以使用 formerly accepted answer(如果您使用的是美国系统并且您永远不需要处理外来字符,那可能没问题。)
然而,同样的效果可以临时仅用于单个命令:
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
之外的其他值),它将覆盖单个LC_*
类别变量,例如LC_CTYPE
。因此,最稳健的方法是设置LC_ALL
。
但是,(有效地)将LC_CTYPE
设置为C
会将字符串视为每个字节都是其自己的字符(不会执行基于编码规则的解释) , 不考虑 - multibyte-on-demand - OS X 默认采用的 UTF-8 编码,其中 外来字符 具有 多字节编码。
简而言之:将 LC_CTYPE
设置为 C
会导致 shell 和实用程序仅将基本英文字母识别为字母(7 位 ASCII 范围内的字母),因此 外国字符。不会被视为字母,从而导致例如大写/小写转换失败。
同样,如果您不需要匹配诸如é
之类的多字节编码字符,而只是想通过这些字符,这可能没问题。 p>
如果这还不够和/或您想了解原始错误的原因(包括确定导致问题的输入字节)并按需执行编码转换 ,继续阅读。
问题是输入文件的编码与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 下无效。
这是一个问题演示,使用编码为 ISO-8859-1
的字符串 voilà
,à
表示为 one 字节(通过 ANSI-C - 引用 bash 字符串 ($'...'
),使用 \xe0
创建字节):
请注意,sed
命令实际上是一个空操作,只是简单地传递输入,但我们需要它来引发错误:
# -> 'illegal byte sequence': byte 0xE0 is not a valid char.
sed 's/.*/&/' <<<$'voil\xe0'
为了简单地忽略问题,可以使用上面的LCTYPE=C
方法:
# No error, bytes are passed through ('á' will render as '?', though).
LC_CTYPE=C sed 's/.*/&/' <<<$'voil\xe0'
如果您想确定输入的哪些部分导致问题,请尝试以下操作:
# Convert bytes in the 8-bit range (high bit set) to hex. representation.
# -> 'voil\xe0'
iconv -f ASCII --byte-subst='\x%02x' <<<$'voil\xe0'
输出将以十六进制形式显示所有设置了高位的字节(超过 7 位 ASCII 范围的字节)。 (但是请注意,这还包括正确编码的 UTF-8 多字节序列 - 需要更复杂的方法来专门识别无效的 UTF-8 字节。)
按需执行编码转换:
标准实用程序 iconv
可用于转换为 (-t
) 和/或从 (-f
) 编码; iconv -l
列出了所有支持的。
示例:
在上述示例的基础上,将 FROM ISO-8859-1
转换为 shell 中有效的编码(基于 LC_CTYPE
,默认基于 UTF-8
):
# Converts to UTF-8; output renders correctly as 'voilà'
sed 's/.*/&/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\xe0')"
请注意,此转换允许您正确匹配外来字符:
# Correctly matches 'à' and replaces it with 'ü': -> 'voilü'
sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\xe0')"
要在处理后将输入 BACK 转换为 ISO-8859-1
,只需将结果通过管道传递给另一个 iconv
命令:
sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\xe0')" | iconv -t ISO-8859-1
【讨论】:
我会说这是一个更好的选择。首先,我不想失去所有终端的多语言支持。其次,接受的答案感觉像是针对局部问题的全局解决方案——这是需要避免的。 我对此做了一些小调整。我很感激反馈。 ***.com/a/35046218/9636LC_CTYPE=C sed 's/.*/&/' <<<$'voil\xe0'
在 Sierra 上为我打印 sed: RE error: illegal byte sequence
。 echo $LC_ALL
输出 en_US.UTF-8
FWIW。
@ahcox:是的,因为设置LC_ALL
覆盖所有其他LC_*
变量,包括LC_CTYPE
,如答案中所述。
@mklement0 很酷,这有效:“LC_ALL=C sed 's/.*/&/' pubs.opengroup.org/onlinepubs/7908799/xbd/envvar.html【参考方案4】:
我的解决方法是使用 Perl:
find . -type f -print0 | xargs -0 perl -pi -e 's/was/now/g'
【讨论】:
这个很好用。与其他人不同,我在转义特殊字符时没有任何错误。以前的问题给了我诸如“sed:RE错误:非法字节序列”或sed:1:“path_to_file”:无效命令代码之类的问题。 简单,无需配置等。喜欢它。【参考方案5】:我的解决方法是使用 gnu sed
。对我的目的来说工作得很好。
【讨论】:
确实,如果您想忽略输入流中的无效字节,GNUsed
是一个选项(不需要LC_ALL=C sed ...
解决方法),因为 GNU sed
只是 传递无效字节 而不是报告错误,但请注意,如果您想正确识别和处理输入字符串中的所有字符,则无法更改首先是输入的编码(通常使用iconv
)。【参考方案6】:
mklement0's answer 很棒,但我有一些小调整。
在使用iconv
时明确指定bash
的编码似乎是个好主意。另外,我们应该在前面加上一个字节顺序标记(even though the unicode standard doesn't recommend it),因为there can be legitimate confusions between UTF-8 and ASCII without a byte-order mark。不幸的是,iconv
在显式指定字节序(UTF-16BE
或UTF-16LE
)时不会预先添加字节顺序标记,因此我们需要使用UTF-16
,它使用特定于平台的字节序,然后使用@ 987654330@ 发现真正的字节序 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
【讨论】:
++ 获得有用的技术,尤其是file -b --mime-encoding
用于发现和报告文件的编码。但是,有些方面值得讨论,我将在单独的 cmets 中进行。
我认为可以肯定地说 Unix 世界在这一点上已经接受了 UTF-8:默认的LC_CTYPE
值通常是<lang_region>.UTF-8
,所以任何文件没有因此,BOM(字节顺序标记)被解释为 UTF-8 文件。仅在 Windows 世界中使用 pseudo-BOM 0xef 0xbb 0xff
;根据定义,UTF-8 不需要 BOM 并且不推荐(如您所说);在 Windows 世界之外,这种伪 BOM 会导致事物中断。
Re Unfortunately, iconv doesn't prepend a byte-order mark when you explicitly specify an endianness (UTF-16BE or UTF-16LE)
:这是设计使然:如果您明确指定字节顺序,则无需通过 BOM 也反映它,因此不会添加任何内容。跨度>
Re LC_*
/ LANG
变量:bash
、ksh
和 zsh
(可能是其他人,但 不是 dash
)确实尊重字符编码;在类似 POSIX 的 shell 中使用基于 UTF-8 的语言环境和v='ä'; echo "$#v"
进行验证:支持 UTF-8 的 shell 应该报告 1
;即,它应该将多字节序列ä
(0xc3 0xa4
) 识别为 single 字符。然而,也许更重要的是:标准实用程序(sed
、awk
、cut
、...)也需要支持区域设置/编码,而 大多数 它们在现代类 Unix 平台上是有例外的,例如 OSX 上的 awk
和 Linux 上的 cut
。
值得称赞的是file
能够识别 UTF-8 伪 BOM,但问题是大多数处理文件的 Unix 实用程序不,并且通常会中断或至少面对一个时行为不端。如果没有 BOM,file
会正确地将全 7 位字节文件识别为 ASCII,并将具有有效 UTF-8 多字节字符的文件正确识别为 UTF-8。 UTF-8 的美妙之处在于它是 ASCII 的超集:根据定义,任何有效的 ASCII 文件都是有效的 UTF-8 文件(但反之则不然);将 ASCII 文件视为 UTF-8 是完全安全的(从技术上讲,它恰好不包含多字节字符。)【参考方案7】:
将以下行添加到您的 ~/.bash_profile
或 ~/.zshrc
文件中。
export LC_CTYPE=C
export LANG=C
【讨论】:
它确实有效,但您能解释一下原因吗? @HoangPham:将LC_CTYPE
设置为C
会导致字符串中的每个字节都成为自己的字符,而无需应用任何编码规则。由于违反(UTF-8)编码规则导致了原始问题,这使得问题消失了。但是,您付出的代价是 shell 和实用程序只能将基本的英文字母(7 位 ASCII 范围内的那些)识别为字母。有关更多信息,请参阅我的答案。
在 shell 的启动文件中永久设置它会禁用许多有用的行为。您只想将其用于绝对需要它的单个命令。
太危险可能会导致意想不到的后果。可以使用LC_CTYPE=C sed …
,即仅在 sed 命令上。
这将完全禁用您的 shell 中对 Unicode 字符的支持。再见表情符号、花哨的线条画字符、带重音符号的字母......。最好只为 sed 命令设置这个,如其他答案中所述。以上是关于RE 错误:Mac OS X 上的非法字节序列的主要内容,如果未能解决你的问题,请参考以下文章
如何检测非法 UTF-8 字节序列以在 java 输入流中替换它们?
UnicodeEncodeError:“cp949”编解码器无法在位置 90 编码字符“\u20a9”:非法多字节序列