Vol*_*gel 19 character-encoding text-processing cut unicode
该命令cut
有一个选项-c
可以处理字符,而不是带有选项的字节-b
。但这似乎不起作用,在en_US.UTF-8
语言环境中:
第二个字节给出了第二个 ASCII 字符(在 UTF-8 中编码完全相同):
$ printf 'ABC' | cut -b 2
B
Run Code Online (Sandbox Code Playgroud)
但不会在 UTF-8 语言环境中给出三个希腊非 ASCII 字符中的第二个:
$ printf '???' | cut -b 2
?
Run Code Online (Sandbox Code Playgroud)
没关系 - 这是第二个字节。
所以我们看第二个字符:
$ printf '???' | cut -c 2
?
Run Code Online (Sandbox Code Playgroud)
那看起来坏了。
通过一些实验,结果表明范围3-4
显示了第二个字符:
$ printf '???' | cut -c 3-4
?
Run Code Online (Sandbox Code Playgroud)
但这与字节 3 到 4 相同:
$ printf '???' | cut -b 3-4
?
Run Code Online (Sandbox Code Playgroud)
所以-c
不超过-b
UTF-8。
我希望语言环境设置不适合 UTF-8,但相比之下,它wc
按预期工作;
它通常用于计算字节数,带有选项-c
( --bytes
)。
(注意令人困惑的选项名称。)
$ printf '???' | wc -c
6
Run Code Online (Sandbox Code Playgroud)
但它也可以使用选项-m
( --chars
)计算字符数,该选项有效:
$ printf '???' | wc -m
3
Run Code Online (Sandbox Code Playgroud)
所以我的配置似乎没问题 - 但cut
.
也许它根本不支持UTF-8?但它似乎确实支持多字节字符,否则就不需要支持-b
和-c
。
那么,怎么了?为什么?
据我所知,语言环境设置看起来适合 utf8:
$ locale
LANG=en_US.UTF-8
LANGUAGE=en_US
LC_CTYPE=en_US.UTF-8
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=
Run Code Online (Sandbox Code Playgroud)
输入,逐字节:
$ printf '???' | hd
00000000 ce b1 ce b2 ce b3 |......|
00000006
Run Code Online (Sandbox Code Playgroud)
Mic*_*mer 18
你还没有说cut
你在使用哪个,但既然你提到了 GNU 长选项,--characters
我假设它就是那个选项。在这种情况下,请注意以下段落info coreutils 'cut invocation'
:
Run Code Online (Sandbox Code Playgroud)‘-c character-list’ ‘--characters=character-list’
选择仅打印字符列表中列出的位置中的字符。与
-b
现在相同,但国际化将改变这一点。
(强调)
目前,GNUcut
始终以单字节“字符”的形式工作,因此您看到的行为是意料之中的。
POSIX 要求同时支持-b
和-c
选项——它们没有被添加到 GNU 中,因为它具有多字节支持并且它们工作正常,但为了避免在符合 POSIX 的输入上出错。在其他一些实现中也做了同样的事情,尽管至少不是FreeBSD和OS X。cut
-c
cut
这是历史行为的-c
。-b
新添加来接管字节角色,以便-c
可以处理多字节字符。也许几年后它会一直按预期工作,尽管进展并不快(已经十多年了)。GNUcut
甚至还没有实现这个-n
选项,即使它是正交的并且旨在帮助转换。旧脚本存在潜在的兼容性问题,这可能是一个问题,尽管我不确切知道原因是什么。
由于许多grep
实现都是多字节感知的,因此您还可以使用grep -o
它来模拟cut -c
.
前两个字符:
\n$ echo \xce\xa4\xce\xb7\xce\xb5\xce\xbf\xce\xb429 | grep -o '^..'\n\xce\xa4\xce\xb7\n
Run Code Online (Sandbox Code Playgroud)\n最后三个字符:
\n$ echo \xce\xa4\xce\xb7\xce\xb5\xce\xbf\xce\xb429 | grep -o '...$'\n\xce\xb429\n
Run Code Online (Sandbox Code Playgroud)\n第二个字符:
\n$ echo \xce\xa4\xce\xb7\xce\xb5\xce\xbf\xce\xb429 | grep -o '^..' | grep -o '.$'\n\xce\xb7\n
Run Code Online (Sandbox Code Playgroud)\n调整句点数量或使用{x,y}
语法来模拟cut
范围。
colrm
(部分util-linux
, 应该已经安装在大多数发行版上)似乎可以更好地处理国际化:
$ echo '???' | colrm 3
??
$ echo '???' | colrm 2
?
Run Code Online (Sandbox Code Playgroud)
注意编号:colrm N
将从 中删除列N
,最多打印字符N-1
。
(学分)