不能在UTF-8中使用`cut -c`(`--characters`)吗?


15

该命令cut具有一个-c处理字符的选项,而不是带有选项的字节-b。但这似乎在en_US.UTF-8区域设置中不起作用:

第二个字节给出第二个ASCII字符(在UTF-8中编码相同):

$ printf 'ABC' | cut -b 2          
B

但不会在UTF-8语言环境中给出三个希腊非ASCII字符中的第二个:

$ printf 'αβγ' | cut -b 2         
�

没关系-这是第二个字节
因此,我们改为看第二个字符

$ printf 'αβγ' | cut -c 2 
�

看起来坏了。
通过一些实验,结果表明范围3-4显示了第二个字符:

$ printf 'αβγ' | cut -c 3-4
β

但这与字节3到4相同:

$ printf 'αβγ' | cut -b 3-4
β

因此,对于UTF-8,-c最多不会超过-b

我希望语言环境设置不适用于UTF-8,但是相比之下,它wc可以按预期工作;
它通常用于带有选项-c--bytes)的字节计数。 (请注意令人困惑的选项名称。)

$ printf 'αβγ' | wc -c
6

但是它也可以使用选项-m--chars)来计算字符,该选项才有效:

$ printf 'αβγ' | wc -m
3

因此,我的配置似乎还可以-但有些特殊之处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=

输入,逐字节:

$ printf 'αβγ' | hd 
00000000  ce b1 ce b2 ce b3                                 |......|
00000006

有趣!似乎-c正在使用与相同的代码-b。您看过源代码了吗?也许您可以找到一个提示,这-c实际上意味着什么。
michas

Answers:


13

您没有说出cut正在使用哪个选项,但是由于您已经提到了GNU long选项,因此--characters我假设它就是那个。在这种情况下,注意,这个通道从info coreutils 'cut invocation'

‘-c character-list’
‘--characters=character-list’

选择仅打印字符列表中列出的位置中的字符。-b现在相同,但是国际化将改变这一点。

(添加了重点)

目前,GNU cut始终以单字节“字符”的形式工作,因此您所看到的行为是可以预期的。


POSIX需要同时支持-b-c选项-并没有将它们添加到GNU,因为它具有多字节支持并且可以正常工作,但是要避免在POSIX兼容的输入上出现错误。在其他一些实现中也做了同样的事情,尽管至少不是FreeBSDOSXcut-ccut

这是历史行为-c-b是新添加的,以接管字节角色,以便-c可以处理多字节字符。也许几年后它会一直按预期工作,尽管进展并不十分迅速(已经过去了十年)。GNU cut 甚至没有实现该-n选项,即使它是正交的并且旨在帮助过渡。旧脚本可能存在兼容性问题,这可能是一个问题,尽管我不确定原因是什么。


1
辛苦了 您tr也会在GNU的文档中找到相同的注释。甚至tar除非我记错了。我想这是一个大项目。
mikeserv

Unicode探针在中有什么解决方法cut吗?例如,在哪里可以下载已修补的源cut?还是使用其他实用程序会更容易?(grep以下解决方案在范围内无法正常工作,例如5-8,44-49
dma_k '18

请参阅此2017年文章,标题为“关于在GNU Coreutils中不断增加多字节和unicode支持的持续努力的随机注释和指针”crashcourse.housegordon.org/coreutils-multibyte-support.html
myrdd,

您可以在cut -c此处找到一些替代方法:superuser.com/questions/506164/…–
myrdd

5

colrm(的一部分util-linux,应该已经安装在大多数发行版上)似乎更好地处理了国际化:

$ echo 'αβγ' | colrm 3
αβ
$ echo 'αβγ' | colrm 2
α

小心编号:colrm N将从中删除列N,最多打印字符N-1

学分


2

由于许多grep实现都是多字节感知的,因此您还可以grep -o用来模拟的某些用法cut -c

$ echo Τηεοδ29 | grep -o '^..'
Τη
$ echo Τηεοδ29 | egrep -o '^..' | grep -o '.$'
η

调整周期数以模拟cut范围。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.