无法将 `cut -c` (`--characters`) 与 UTF-8 一起使用?

无法将 `cut -c` (`--characters`) 与 UTF-8 一起使用?

该命令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
β

所以-c并不比-bUTF-8 更重要。

我预计区域设置不适合 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

答案1

你还没有说cut你正在使用哪一个,但既然你提到了 GNU long 选项,--characters我就假设它就是那个。在这种情况下,请注意这一点段落来自info coreutils 'cut invocation':

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

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

(强调已添加)

目前,GNUcut始终按照单字节“字符”工作,因此您看到的行为是预期的。


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

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

答案2

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

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

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

(学分

答案3

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

前两个字符:

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

最后三个字符:

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

第二个字符:

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

调整句点数量或使用{x,y}语法来模拟cut范围。

答案4

八年多后,我无法重现OP的问题(MacOS 13.4 Ventura):

~$ printf 'ABC' | cut -b 2
B
~$ printf 'αβγ' | cut -b 2
~$ printf 'αβγ' | cut -c 2
β
~$ printf 'αβγ' | cut -c 3-4
γ
~$ printf 'αβγ' | cut -b 3-4
β
~$ printf 'αβγ' | wc -c
       6
~$ printf 'αβγ' | wc -m
       3 

以上似乎是OP所希望的答案?请注意,行结尾cut -c 3-4实际上返回γ%到 下zsh,表示部分行(请求的字符数多于可返回的字符数)。

-$ man cut没有给我除macOS 13.4 August 3, 2017IEEE Std 1003.2-1992 (“POSIX.2”) 以外的版本,并带有附加-w标志作为规范的扩展。“历史:AT&T System III UNIX 中出现了 cut 命令。”

相关内容