【问题标题】:UNIX sort ignores whitespacesUNIX 排序忽略空格
【发布时间】:2011-10-18 21:41:14
【问题描述】:

给定一个文件txt

ab
a c
a a

当调用sort txt时,我得到:

a a
ab
a c

换句话说,它不是正确的排序方式,它会删除/忽略空格!我预计这是 sort -i 的行为,但无论有没有 -i 标志都会发生这种情况。

我想获得“正确”的排序:

a a
a c
ab

我该怎么做?

【问题讨论】:

  • 我已经创建了您的输入文件并使用 sort 来提供所需的输出而没有任何问题。 txt 是在 *nix 系统上创建的吗?你确定它们是空格而不是其他类型的字符吗?
  • 是的,我实际上在命令行中输入了这个确切的示例......使用 ubuntu 默认安装,几乎开箱即用,无需花哨的环境调整。
  • 请将正确的解决方案标记为已接受,而不是将问题编辑为“已解决”。
  • 其实这正确的排序。它被称为库或字典排序,其中我们只查看字母的差异,而不是空格或标点符号。这是 Unicode 排序算法的默认模式,至少在您达到第 4 级之前是这样。但是,这不是 Unix 排序应该采取的方式,因为 Unix 排序命令是基于字段的,而不是基于文本的。

标签: linux shell sorting locale


【解决方案1】:

解决者:

export LC_ALL=C

From the sort() documentation:

警告:环境指定的区域设置会影响排序顺序。设置 LC_ALL=C 以获得使用本机字节值的传统排序顺序。

(至少适用于 ASCII,不知道 UTF8)

【讨论】:

  • 这是因为sort 的帮助菜单说: *** WARNING *** 环境指定的语言环境会影响排序顺序。设置 LC_ALL=C 以获得使用本机字节值的传统排序顺序。
  • @Aditya:......是的......对......到底什么是“语言环境”? :p 为什么会影响排序?为什么默认不使用默认排序? (我的 LC_ALL 是空的)在这种情况下使用哪种排序? ...对不起,如果这一切对我来说似乎并不明显。
  • ...是的...我刚刚注意到LC_ALL=C 破坏了我的 UTF8 字符显示...所以我无法正确排序它们或无法正确显示它们。耶!
  • 您不必导出 LC_ALL,只需内联运行它 - 就像 LC_ALL=C sort ... 在单个命令中一样。
  • "到底什么是'locale'?为什么它会影响排序?为什么不使用默认排序*默认情况下?"* -- 没有正确的排序顺序。不同的人对事物应该如何分类有不同的看法。其中一些取决于“语言环境”,例如美国,或者德国。因此,计算机“区域设置”是影响排序顺序、大写/小写转换、数字格式等的环境设置,因此这些函数执行区域设置认为“默认”的任何操作。 LC_ALL=C 是最小公分母;您实际上是在告诉计算机“装傻”。
【解决方案2】:

如前所述,LC_ALL=C sort 可以解决问题。这仅仅是因为不同的语言有不同的字符排序规则,这些规则通常是由高级语言学家而不是 CS 专家制定的。这些规则,就您的语言环境而言,似乎表明在排序时应该忽略空格。

通过为 LC_ALL=C 加上前缀(或者,当 LC_ALL 未设置时,LC_COLLATE=C 就足够了),您明确声明了与语言无关的排序(并且,对于 LC_ALL,数字格式和其他内容),这就是您在此上下文中想要的.如果您想将此设为默认值,请在您的环境中导出 LC_COLLATE。

以这种方式选择默认值是为了与“正常”的现实世界排序方案(如白页)保持一致,后者通常会忽略空格。

【讨论】:

    【解决方案3】:

    在某些字母超出范围 [A-Za-z] 的语言中,使用 C 语言环境(即仅按字节值排序)并不是一个好的解决方案。这样的字母在 UTF-8 中表示为多个字节,然后字节值整理顺序不是人们想要的。 (某些字符可能有两种等效的表示形式(预组合和分解)。

    然而,空间的处理是一个问题。我尝试了以下方法:

    $ cat stest  
    a b  
    a c  
    ab  
    a d  
    
    $ sort stest  
    ab  
    a b  
    a c  
    a d  
    
    $ sort -k 1,1 stest  
    a b  
    a c  
    a d  
    ab  
    

    根据我的需要,-k 1,1 可以解决问题。我尝试的另一个但更笨拙的解决方案是将空格更改为一些辅助字符,然后排序,然后将辅助字符改回空白。

    【讨论】:

      【解决方案4】:

      您可以使用“env”程序在排序期间临时更改您的 LC_COLLATE;例如

      /usr/bin/env LC_COLLATE=POSIX /bin/sort file1 file2

      在命令行上有点麻烦,但如果你在脚本中使用它应该是透明的。

      【讨论】:

      • 在脚本中你可以定义一个函数:sort_posix() { env LC_COLLATE=POSIX sort "$@"; }
      【解决方案5】:

      我已经研究了一段时间,想要优化我维护的一个拥有大量国际用户群的 shell 脚本。 (重量是百分比,而不是数量)。

      我在网上看到的大多数选项,SO 似乎都推荐我在这里看到的,全局设置语言环境(矫枉过正)

      export LC_ALL=C
      

      或者像这样从gnu.org(乏味)将它传递到每个单独的命令中

      $ echo abcdefghijklmnopqrstuvwxyz | LC_ALL=C /usr/xpg4/bin/tr 'a-z' 'A-Z' ABCDEFGHIJKLMNOPQRSTUVWXYZ
      

      我想避免破坏用户的区域设置作为运行我的程序的看不见的副作用。正如您所期望的那样,通过放弃全球化,这很容易实现。无需将此变量导出到您的程序之外。

      出于某种原因,我不得不设置 LANG 而不是 LC_ALL,但是设置了所有单独的语言环境,这在功能上对我来说已经足够了。

      这是测试,很简单

      #!/bin/bash
      # locale_checker.sh
      
      #Check and set locale to LC_ALL to optimize character sort and search.
      echo "locale was $LANG"
      LANG=C
      locale
      

      并输出 + 证明它是临时的并且可以限制在我的脚本进程中。

      mateor@:~/snippets$ ./locale_checker.sh
      locale was en_US.UTF-8
      LANG=C
      LANGUAGE=en_US:en
      LC_CTYPE="C"
      LC_NUMERIC="C"
      LC_TIME="C"
      LC_COLLATE="C"
      LC_MONETARY="C"
      LC_MESSAGES="C"
      LC_PAPER="C"
      LC_NAME="C"
      LC_ADDRESS="C"
      LC_TELEPHONE="C"
      LC_MEASUREMENT="C"
      LC_IDENTIFICATION="C"
      LC_ALL=
      mateor@:~/snippets$ locale
      LANG=en_US.UTF-8
      LANGUAGE=en_US:en
      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=
      

      给你。您可以获得优化的语言环境,而不会破坏其他人的无辜环境,并且避免在您认为可能有帮助的任何地方管道它的乏味。

      【讨论】:

      • 命令为 locale_checker.sh 的实用程序不应修改语言环境。
      【解决方案6】:

      奇怪,在这里工作(cygwin)。

      试试sort -d txt

      【讨论】:

        【解决方案7】:

        其实对我来说

        $ cat txt
        ab
        a c
        a a
        $ sort txt
        a a
        a c
        ab
        

        我会在你的 ac 之间打赌,你有一个不间断的空间或一个 enspace 或一个 empspace 或其他高代码点空间!

        编辑

        刚刚在 Linux 上运行它。我应该看看标签。是的,我得到与您相同的输出!我的第一次运行是在 Mac 上。看起来像是 GNU 和 BSD 之间的区别。我会进一步调查。

        编辑 2:

        Linux 使用基于字段的排序....仍在寻找如何抑制它。试过了

        sort -t, txt
        

        希望诱使 GNU 认为整行是一个字段,但它仍然使用当前语言环境进行排序。

        编辑 3:

        OP 通过将语言环境设置为 C 解决了问题

        export LC_ALL=C
        

        似乎没有其他方法。 sort 命令将使用当前语言环境,虽然它经常说C(或其别名POSIX)是默认语言环境,但如果你有Linux,它可能已经为你设置好了。输入 locale -a 以查看可用的语言环境。在我的系统上:

        $ locale -a
        C
        POSIX
        en_AG
        en_AU.utf8
        en_BW.utf8
        en_CA.utf8
        en_DK.utf8
        en_GB.utf8
        en_HK.utf8
        en_IE.utf8
        en_IN
        en_NG
        en_NZ.utf8
        en_PH.utf8
        en_SG.utf8
        en_US.utf8
        en_ZA.utf8
        en_ZW.utf8
        

        似乎将语言环境设置为 C(或其别名 POSIX)是打破sort 的基于字段的行为并将整行视为一个字段的唯一方法。恕我直言,这是如何做到的,这很奇怪。我认为-t-k 选项,或者一些新选项可能是实现这一目标的更明智的方式。

        顺便说一句,看起来这个问题之前已经在 SO:unexpected result from gnu sort 上提出过。

        【讨论】:

        • hmmm ...奇怪...我绝对有一个普通的空间...实际上我在命令行中输入了相同的示例并且结果与您不同...多么奇怪。我正在使用 ubuntu 默认安装 btw,几乎是开箱即用的,没有花哨的环境调整。
        • 嗯,我得到了和@arnaud 一样的输出。
        • 查看我的回答以避免破坏用户的语言环境。
        猜你喜欢
        • 2013-05-10
        • 2021-12-19
        • 1970-01-01
        • 2014-11-14
        • 1970-01-01
        • 1970-01-01
        • 2012-04-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多