【问题标题】:Sorting a space delimited list with uneven spaces对具有不均匀空格的空格分隔列表进行排序
【发布时间】:2023-03-05 10:15:01
【问题描述】:

我有一个以空格分隔的列表,其中 是第一列的空格数量不均匀。我想按字符串后面出现的第一个数字对它进行反向排序。我需要使用 bash 命令来执行此操作。

例子:

Pontiac Firebird 19.0 6 250.0 100.0 3282. 15.0 71 US
Pontiac J2000 SE Hatchback 31.0 4 112.0 85.00 2575. 16.2 82 US
Oldsmobile Delta 88 Royale 12.0 8 350.0 160.0 4456. 13.5 72 US
Oldsmobile Omega 11.0 8 350.0 180.0 3664. 11.0 73 US
AMC Gremlin 20.0 6 232.0 100.0 2914. 16.0 75 US
AMC Gremlin 21.0 6 199.0 90.00 2648. 15.0 70 US
Pontiac Lemans V6 21.5 6 231.0 115.0 3245. 15.4 79 US

会变成:


Oldsmobile Omega 11.0 8 350.0 180.0 3664. 11.0 73 US
Oldsmobile Delta 88 Royale 12.0 8 350.0 160.0 4456. 13.5 72 US
Pontiac Firebird 19.0 6 250.0 100.0 3282. 15.0 71 US
AMC Gremlin 20.0 6 232.0 100.0 2914. 16.0 75 US
AMC Gremlin 21.0 6 199.0 90.00 2648. 15.0 70 US
Pontiac Lemans V6 21.5 6 231.0 115.0 3245. 15.4 79 US
Pontiac J2000 SE Hatchback 31.0 4 112.0 85.00 2575. 16.2 82 US

我试过sort -nr 来看看会发生什么,它会对列表进行反向排序,但相对于它的字母顺序。我想根据所有值进行排序。

诀窍是我必须用空格分隔。使用 bash 执行此操作的最佳方法是什么?

【问题讨论】:

  • 您的意思是要对第一个全数字字段(可能包含小数)进行排序?所以你忽略了V6中的6,你忽略了J2000中的2000,对吧?如果“第一个全数字字段”中有重复项,我们应该怎么做,即是否有二级/三级字段用于排序?
  • @markp-fuso 是的,准确地说。如果“第一个全数字字段”中有重复项,我会假设我可以将这些重复项按任何顺序排列。
  • 您确定“第一个全数字字段”标准有效吗?因为在Oldsmobile Delta 88 Royale 12.0 8 ... 行中,那是“88”,而不是“12.0”。
  • 鉴于第一个以空格分隔的字符串是汽车模型,并且您需要高级排序功能,为什么不说服您的团队以可识别的结构化格式(如 CSV 或 TSV)存储这些数据?有人可能会花几分钟时间给你答案,但这些答案不太可能通过大量输入值的压力测试。

标签: linux bash sorting awk grep


【解决方案1】:

必须用空格分隔

你的意思是,结果必须再次用空格分隔,对吗?在处理过程中,您可以随意变换输入。

假设您知道文件中永远不会出现的字符,请使用 sed 按该字符分隔要排序的值,然后按该值排序,然后再次删除其他分隔符。 (这个过程基本上就是一个Schwartzian transform。)

这里我们使用铃铛字符\a 来分隔key 进行排序。该字符不太可能出现在文本文件中。

sed -E 's/ ([0-9]+\.[0-9]+) / \a\1\a /' | sort -t $'\a' -k2,2n | tr -d \\a

【讨论】:

  • 我认为运算应该以防错格式存储数据。
【解决方案2】:

这是一个简短的 ruby​​ 程序:

ruby -e '
    puts IO.readlines(ARGV.shift, chomp: true)
        .map {|line|
            fields = line.split
            [fields[0..(fields.size - 9)].join(" ")] + fields[-8 .. -1]
        }
        .sort_by {|row| row[1]}
        .map {|row| row.join(" ")}
        .join("\n")
' file

【讨论】:

    【解决方案3】:

    我将为此使用 GNU AWK,如下所示,让 file.txt 内容为

    Pontiac Firebird 19.0 6 250.0 100.0 3282. 15.0 71 US
    Pontiac J2000 SE Hatchback 31.0 4 112.0 85.00 2575. 16.2 82 US
    Oldsmobile Delta 88 Royale 12.0 8 350.0 160.0 4456. 13.5 72 US
    Oldsmobile Omega 11.0 8 350.0 180.0 3664. 11.0 73 US
    AMC Gremlin 20.0 6 232.0 100.0 2914. 16.0 75 US
    AMC Gremlin 21.0 6 199.0 90.00 2648. 15.0 70 US
    Pontiac Lemans V6 21.5 6 231.0 115.0 3245. 15.4 79 US
    

    然后

    awk 'BEGIN{FPAT="[0-9]*[.][0-9]*";PROCINFO["sorted_in"]="@ind_num_asc"}{arr[$1]=$0}END{for(i in arr){print arr[i]}}' file.txt
    

    输出

    Oldsmobile Omega 11.0 8 350.0 180.0 3664. 11.0 73 US
    Oldsmobile Delta 88 Royale 12.0 8 350.0 160.0 4456. 13.5 72 US
    Pontiac Firebird 19.0 6 250.0 100.0 3282. 15.0 71 US
    AMC Gremlin 20.0 6 232.0 100.0 2914. 16.0 75 US
    AMC Gremlin 21.0 6 199.0 90.00 2648. 15.0 70 US
    Pontiac Lemans V6 21.5 6 231.0 115.0 3245. 15.4 79 US
    Pontiac J2000 SE Hatchback 31.0 4 112.0 85.00 2575. 16.2 82 US
    

    解释:我通知 GNU AWK 该字段是 0 位或更多位,后跟文字点 ([.]),后跟 0 位或更多位(注意:我假设第一个数字中总会有点,而不是点在带有名称的列中)并且该数组遍历应该是视为Predefined Array Scanning Orders之一。对于每一行,我添加到数组对中,键是第一个数字 ($1),值是整行 ($0)。在遍历所有行后,我 print 来自数组 arr 的值,其顺序观察选定的数组遍历。

    (在 gawk 4.2.1 中测试)

    【讨论】:

      猜你喜欢
      • 2021-03-30
      • 2011-02-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多