我应该使用 LANG 的什么值来“sort -u”正确处理中文字符?

rdr*_*109 10 sort unicode locale

    \n
  • 上下文
  • \n
  • 发现问题\n
      \n
    • 问题编号 1
    • \n
    • 问题编号 2
    • \n
    • 问题编号 3
    • \n
    \n
  • \n
  • 问题
  • \n
\n

\n

上下文

\n

今天,我想保留包含汉字的文件中的唯一行。我决定使用该sort实用程序,因为我熟悉该工具,并且删除文件中的重复行就像使用该-u标志一样简单。我了解到我需要更改区域设置才能sort正确使用中文字符。我注意到使用不同的区域设置会sort产生不同的行为。在这篇文章中,我展示了我的发现。

\n

我知道删除文件中重复行的任务可以使用多种工具/编程语言来完成。虽然我感谢任何建议完成该任务的工具的人,但我更感兴趣的是了解有关语言环境的更多信息以及它们如何影响 Unix 实用程序。

\n

\n

发现了问题

\n

\n

问题编号 1

\n

以下是我的系统的区域设置。

\n
locale\n
Run Code Online (Sandbox Code Playgroud)\n
LANG=en_US.UTF-8\nLC_CTYPE="en_US.UTF-8"\nLC_NUMERIC="en_US.UTF-8"\nLC_TIME="en_US.UTF-8"\nLC_COLLATE="en_US.UTF-8"\nLC_MONETARY="en_US.UTF-8"\nLC_MESSAGES="en_US.UTF-8"\nLC_PAPER="en_US.UTF-8"\nLC_NAME="en_US.UTF-8"\nLC_ADDRESS="en_US.UTF-8"\nLC_TELEPHONE="en_US.UTF-8"\nLC_MEASUREMENT="en_US.UTF-8"\nLC_IDENTIFICATION="en_US.UTF-8"\nLC_ALL=\n
Run Code Online (Sandbox Code Playgroud)\n

考虑以下名为main.txt

\n
\xe4\x94\x8d\n\xe4\x8f\x9d\n
Run Code Online (Sandbox Code Playgroud)\n

en_US.UTF-8如果我尝试使用作为我的排序$LANG对其进行排序。我失去了行号。2

\n
sort -u main.txt\n
Run Code Online (Sandbox Code Playgroud)\n
\xe4\x94\x8d\n
Run Code Online (Sandbox Code Playgroud)\n

LANG我已经通过设置解决了这个问题zh_CN.UTF-8

\n
export LANG=zh_CN.UTF-8\nsort -u main.txt\n
Run Code Online (Sandbox Code Playgroud)\n
\xe4\x94\x8d\n\xe4\x8f\x9d\n
Run Code Online (Sandbox Code Playgroud)\n

我在 Stack Overflow 中创建了一个关于此问题的问题。有人建议我更改语言环境,它似乎有效,我以为我已经解决了问题,但后来我发现问题没有。2 和发行号。3(如下所述)。

\n

\n

问题编号 2

\n

现在,假设我们在文件中添加两行main.txt

\n
\xe4\x94\x8d\n\xe4\x8f\x9d\n\n\n
Run Code Online (Sandbox Code Playgroud)\n

如果我在等于sort -u时执行它,我会丢失第 1 行。4.LANGzh_CN.UTF-8

\n
export LANG=zh_CN.UTF-8\nsort -u main.txt\n
Run Code Online (Sandbox Code Playgroud)\n
\xe4\x94\x8d\n\n\xe4\x8f\x9d\n
Run Code Online (Sandbox Code Playgroud)\n

我尝试了不同的语言环境,zh.CN因为这些字符是汉字。未注释的语言环境是我尝试使用的语言环境。

\n
grep \'^#\\?zh\' /etc/locale.gen\n
Run Code Online (Sandbox Code Playgroud)\n
zh_CN.GB18030 GB18030  \nzh_CN.GBK GBK  \nzh_CN.UTF-8 UTF-8  \nzh_CN GB2312  \n#zh_HK.UTF-8 UTF-8  \n#zh_HK BIG5-HKSCS  \n#zh_SG.UTF-8 UTF-8  \n#zh_SG.GBK GBK  \n#zh_SG GB2312  \n#zh_TW.EUC-TW EUC-TW  \n#zh_TW.UTF-8 UTF-8  \n#zh_TW BIG5  \n
Run Code Online (Sandbox Code Playgroud)\n

我注意到当设置为orsort -u时,它可以按预期工作。我通过使用或解决了这个问题。LANGzh_CN.GBKzh_CN.GB18030zh_CN.GBKzh_CN.GB18030

\n

这是设置为 时它按预期工作的LANG证明zh_CN.GBK

\n
export LANG=zh_CN.GBK\nsort -u main.txt\n
Run Code Online (Sandbox Code Playgroud)\n
\xe4\x94\x8d\n\n\n\xe4\x8f\x9d\n
Run Code Online (Sandbox Code Playgroud)\n

这是设置为 时它按预期工作的LANG证明zh_CN.GB18030

\n
export LANG=zh_CN.GB18030\nsort -u main.txt\n
Run Code Online (Sandbox Code Playgroud)\n
\xe4\x94\x8d\n\n\n\xe4\x8f\x9d\n
Run Code Online (Sandbox Code Playgroud)\n

\n

问题编号 3

\n

假设我们在文件中再添加两行main.txt

\n
\xe4\x94\x8d\n\xe4\x8f\x9d\n\n\n\xe4\xb9\xa6\n\xe4\xb9\x99\n
Run Code Online (Sandbox Code Playgroud)\n

如果我使用zh_CN.GB18030or zh_CN.GBK,我会丢失第 1 行。6.

\n
export LANG=zh_CN.GB18030\nsort -u main.txt\n
Run Code Online (Sandbox Code Playgroud)\n
\xe4\xb9\xa6\n\xe4\x94\x8d\n\n\n\xe4\x8f\x9d\n
Run Code Online (Sandbox Code Playgroud)\n
export LANG=zh_CN.GBK\nsort -u main.txt\n
Run Code Online (Sandbox Code Playgroud)\n
\xe4\xb9\xa6\n\xe4\x94\x8d\n\n\n\xe4\x8f\x9d\n
Run Code Online (Sandbox Code Playgroud)\n

\n

问题

\n

$LANG我应该使用哪个值来sort真正保留以下文件中的唯一行?

\n
\xe4\x94\x8d\n\xe4\x8f\x9d\n\n\n\xe4\xb9\xa6\n\xe4\xb9\x99\n
Run Code Online (Sandbox Code Playgroud)\n

我之前表明使用zh_CN.GB18030zh_CN.GBK或似乎不起作用。zh_CN.UTF-8zh_CN

\n

Sté*_*las 10

sort -u输出在区域设置中整理相同(具有相同排序顺序)的每组行之一。

\n

在 GNU 系统(使用 GNU libc 的系统)上,在大多数语言环境中,许多字符\xc2\xb9 具有未定义的排序顺序,这意味着它们最终排序相同,并且还存在显式定义为具有相同排序顺序的字符。

\n

当字节无法解码为文本时,它们通常也会被忽略或被认为具有相同的排序顺序\xc2\xb2。

\n

因此,为了sort -u给您基于字节到字节相等比较的独特行,无论文本使用何种编码编写,您都需要一个区域设置,其中每个字节都可以解码为一个字符和一个具有总顺序的区域设置。

\n

最简单的方法是使用C一个字节 == 一个字符映射的区域设置(某些字节可能导致未定义的字符),并且排序基于字节值(至少在基于 ASCII 的系统上),并且是唯一的区域设置您一定可以在任何给定系统上找到它。

\n

就您的情况而言,要了解为什么zh_CN会给您带来更好的行为,您可以查看通常在/usr/share/i18n/localeGNU 系统上的语言环境定义,zh_CN您可以在其中找到:

\n
LC_COLLATE\ncopy "iso14651_t1_pinyin"\nEND LC_COLLATE\n
Run Code Online (Sandbox Code Playgroud)\n

哪里iso14651_t1_pinyin有:

\n
copy "iso14651_t1_common"\n
Run Code Online (Sandbox Code Playgroud)\n

然后指定许多附加汉字的排序,而en_US仅使用iso14651_t1_common.

\n

如果您设置LC_CTYPE(通过LANG)为 zh_CN.GB18030 或 zh_CN.GBK 并尝试处理以 UTF-8 编码的文件,sort通常无法将行解码为文本,因为 UTF-8 编码文本的字节通常不会形成有效的 GB18030 编码文本会让问题变得更糟。

\n

在这里,如果您想获得唯一的(根据字节到字节比较)行,但也想在您的语言环境中获得“正确”排序的输出,您可以这样做:

\n
LC_ALL=C sort -u your-file | sort\n
Run Code Online (Sandbox Code Playgroud)\n

第一个sort删除重复项,第二个根据您的区域设置排序规则对剩余行进行排序。

\n

我们使用or +LC_ALL代替,因为它是覆盖所有内容的变量(优先于which 优先于),因此如果环境中也设置了or ,它仍然有效。LANGLC_CTYPELC_COLLATELC_ALLLC_individual_settingLANGLANGLC_COLLATE

\n

理论上,你还可以这样做:

\n
LC_ALL= LC_CTYPE=C LC_COLLATE=C sort\n
Run Code Online (Sandbox Code Playgroud)\n

以便仅设置“字符类型”和“排序规则”设置,而单独保留其他区域设置类别(由LANG或覆盖LC_xxx),但 所使用的其余类别sort用于LC_MESSAGES例如错误消息,如果您设置 ,则LC_CTYPE=C仅使用 US-无论如何,英语消息都可以正确显示,因为它只有 ASCII 字符。例如:

\n
$ LC_MESSAGES=zh_CN LC_CTYPE=C sort --version\nsort (GNU coreutils) 9.1\nCopyright (C) 2022 Free Software Foundation, Inc.\n??? GPLv3+:GNU ???????? 3 ?????? <https://gnu.org/licenses/gpl.html>?\n????????:??????????????\n?????????,????????\n\n? Mike Haertel ? Paul Eggert ???\n
Run Code Online (Sandbox Code Playgroud)\n

您可以在以下位置找到更多详细信息:

\n\n
\n

\xc2\xb9 严格来说,主要单位不是字符,而是整理元素,它可以由多个字符组成,例如 在某些捷克语环境中ch排序的字符。hi

\n

\xc2\xb2 请注意,某些sort实现(不是 GNU sort)也会因 NUL 字符或过长的行而阻塞。

\n