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

jww*_*jww 163 regex macos bash sed

我正在尝试替换Mac OS X上的Makefile中的字符串,以便交叉编译到iOS.该字符串嵌入了双引号.命令是:

sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure
Run Code Online (Sandbox Code Playgroud)

错误是:

sed: RE error: illegal byte sequence
Run Code Online (Sandbox Code Playgroud)

我试着逃避双引号,逗号,破折号和冒号,没有任何快乐.例如:

sed -i "" 's|\"iphoneos-cross\"\,\"llvm-gcc\:\-O3|\"iphoneos-cross\"\,\"clang\:\-Os|g' Configure
Run Code Online (Sandbox Code Playgroud)

我有一点时间调试这个问题.有谁知道如何sed打印非法字节序列的位置?或者有谁知道非法字节序列是什么?

mkl*_*nt0 283

显示症状的示例命令:sed 's/./@/' <<<$'\xfc'failed,因为byte 0xfc不是有效的UTF-8 char.
请注意,相比之下,GNU sed(Linux,但也可以在macOS上安装)只是传递无效字节,而不报告错误.

如果您不介意失去对真实语言环境的支持,那么使用之前接受的答案是一种选择(如果您使用的是美国系统而且您永远不需要处理外国字符,那可能没问题.)

然而,同样的效果就可以了即席单个命令:

LC_ALL=C sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure
Run Code Online (Sandbox Code Playgroud)

注:重要的是一个有效 LC_CTYPE的设置C,因此LC_CTYPE=C sed ...正常也行,但如果LC_ALL碰巧被设置(除其他东西C),它将覆盖单个LC_*-category变量,如LC_CTYPE.因此,最稳健的方法是设置LC_ALL.

然而,(有效)的设定LC_CTYPE,以C把字符串,就好像每个字节是它自己的特点(没有进行基于编码规则的解释),有没有考虑对-多字节点播- UTF-8编码的是OS X采用默认,外国字符有多字节编码.

简而言之:设置LC_CTYPEC使shell和实用程序仅将基本英文字母识别为字母(7位ASCII范围内的字母),以便外部字符.不会被视为字母,例如导致大写/小写转换失败.

同样,如果您不需要匹配多字节编码的字符é,并且只是想传递这些字符,这可能会很好.

如果这不够和/或您想了解原始错误的原因(包括确定导致问题的输入字节)并按需执行编码转换,请阅读下文.


问题是输入文件的编码与shell的编码不匹配.
更具体地说,输入文件包含以UTF-8无效的方式编码的字符(如评论中所述@KlasLindbä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命令实际上是一个简单地传递输入的无操作,但是我们需要它来激发错误:

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

要简单地忽略该问题,LCTYPE=C可以使用上述方法:

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

如果要确定输入的哪些部分导致问题,请尝试以下操作:

  # 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}'
Run Code Online (Sandbox Code Playgroud)

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


按需执行编码转换:

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

例子:

在上面的例子的ISO-8859-1基础上LC_CTYPE,将FROM转换为shell中有效的编码(基于,UTF-8默认情况下是 - )

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

请注意,此转换允许您正确匹配外来字符:

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

要将输入BACK转换为ISO-8859-1处理后,只需将结果传递给另一个iconv命令:

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

  • +1用于彻底解释. (20认同)
  • 我认为这是一个更好的选择.首先,我不想失去所有终端的多语言支持.其次,接受的答案感觉就像是解决当地问题的全局解决方案 - 需要避免的事情. (4认同)
  • @ahcox:是的,因为设置 `LC_ALL` _overrides_ 所有其他 `LC_*` 变量,包括 `LC_CTYPE`,如答案所述。 (2认同)
  • @ mklement0很酷,它的工作原理是:“ LC_ALL = C sed's /.*/&amp;/'&lt;&lt;&lt; $'voil \ x {e0}'”。此处为我的不专心的忽略解释了优先顺序:http://pubs.opengroup.org/onlinepubs/7908799/xbd/envvar.html (2认同)

bin*_*lve 132

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

export LC_CTYPE=C 
export LANG=C
Run Code Online (Sandbox Code Playgroud)

  • 它确实有效,但你可以解释一下原因吗? (28认同)
  • @HoangPham:将`LC_CTYPE`设置为`C`会使字符串中的每个字节成为自己的字符,而不应用任何编码规则.由于违反(UTF-8)编码规则导致原始问题,这使问题消失.但是,您支付的价格是shell和实用程序只能识别基本英文字母(7位ASCII范围内的字母)作为字母.请参阅我的回答. (10认同)
  • 在shell的启动文件中永久设置它将禁用许多有用的行为.你想把它放在绝对需要它的单个命令中. (6认同)
  • 太危险可能会导致意外后果.可以使用`LC_CTYPE = C sed ...`,即仅使用sed命令. (4认同)
  • 这将完全禁用对Shell中的Unicode字符的支持。再见的表情符号,花哨的线条图字符,带有重音符号的字母,....最好仅将它设置为sed命令,如其他答案所述。 (2认同)

Vit*_*ich 13

我的解决方法是使用 Perl:

find . -type f -print0 | xargs -0 perl -pi -e 's/was/now/g'
Run Code Online (Sandbox Code Playgroud)

  • 这个效果很好。与其他字符不同,我在转义特殊字符时没有遇到任何错误。以前的问题给了我诸如“sed:RE错误:非法字节序列”或sed:1:“path_to_file”:无效命令代码之类的问题。 (2认同)
  • 简单,不需要配置等。喜欢它。 (2认同)

Hea*_*ers 5

mklement0 的答案很好,但我有一些小调整。

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

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

# 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
Run Code Online (Sandbox Code Playgroud)

  • 我认为可以肯定地说 Unix 世界此时已经接受了 UTF-8:默认的“LC_CTYPE”值通常是“&lt;lang_region&gt;.UTF-8”,因此任何没有 BOM(字节顺序标记)的文件都是因此解释为 UTF-8 文件。仅在_Windows_世界中使用_伪BOM_`0xef 0xbb 0xff`;根据定义,UTF-8 不需要 BOM,并且不推荐(如您所述);在 Windows 世界之外,这个伪 BOM 会导致事情发生_破坏_。 (3认同)
  • 不幸的是,当您显式指定字节顺序(UTF-16BE 或 UTF-16LE)时, iconv 不会在前面添加字节顺序标记:这是设计使然:如果您明确指定字节顺序,则无需也反映它通过 BOM,因此不会添加任何内容。 (3认同)
  • 关于“LC_*”/“LANG”变量:“bash”、“ksh”和“zsh”(可能是其他变量,但不是“dash”)确实尊重字符编码;在类似 POSIX 的 shell 中使用基于 UTF-8 的语言环境并使用 `v='ä' 进行验证;echo "${#v}"`:支持 UTF-8 的 shell 应报告 `1`;即,它应该将多字节序列“ä”(“0xc3 0xa4”)识别为_单个_字符。然而,也许更重要的是:_标准实用程序_(`sed`、`awk`、`cut` 等)也需要具有区域设置/编码感知能力,而大多数现代类 Unix 平台上的实用程序是:但也有例外,例如 OSX 上的“awk”和 Linux 上的“cut”。 (2认同)
  • 值得称赞的是,“file”可以识别 UTF-8 伪 BOM,但问题是大多数处理文件的 Unix 实用程序都_不能_,并且在遇到这种情况时通常会崩溃或至少表现错误。如果没有 BOM,“file”会正确地将全 7 位字节文件识别为 ASCII,并将具有有效 UTF-8 多字节字符的文件识别为 UTF-8。UTF-8 的优点在于它是 ASCII 的超集:根据定义,任何有效的 ASCII 文件都是有效的 UTF-8 文件(但反之则不然);将 ASCII 文件视为 UTF-8 是完全安全的(从技术上讲,它只是碰巧不包含多字节字符。) (2认同)

小智 5

您只需在sed命令之前通过管道传送iconv命令即可。\nEx 带有 file.txt 输入:

\n\n
\n

iconv -f ISO-8859-1 -t UTF8-MAC 文件.txt | sed \'s/某事/\xc3\xa0\xc3\xa9\xc3\xa8\xc3\xaa\xc3\xa7\xc3\xb9\xc3\xbb/g\' | ……

\n
\n\n

-f选项是“from”代码集,-t 选项是“to”代码集转换。

\n\n

注意大小写,网页通常显示小写,如 < charset=iso-8859-1"/>\n 而iconv使用大写。\n使用命令iconv -l可以在系统中获得iconv支持的代码集列表

\n\n

UTF8-MAC是用于转换的现代操作系统 Mac 代码集。

\n