【发布时间】:2014-09-01 12:53:23
【问题描述】:
我有以下输入 csv 文件:
"aaa","1","xxx" "ccc, Inc.","6100","yyy" "bbb","609","zzz"我希望按第二列作为数字排序, 我试过了
排序 --field-separator=',' --key=2n问题在于,由于所有值都被引用,它们没有被 -n(数字)选项正确排序。有解决办法吗?
【问题讨论】:
我有以下输入 csv 文件:
"aaa","1","xxx" "ccc, Inc.","6100","yyy" "bbb","609","zzz"我希望按第二列作为数字排序, 我试过了
排序 --field-separator=',' --key=2n问题在于,由于所有值都被引用,它们没有被 -n(数字)选项正确排序。有解决办法吗?
【问题讨论】:
一个小技巧,使用双引号作为分隔符:
sort --field-separator='"' --key=4 -n
【讨论】:
对于引用的csv,请使用具有适当csv 解析器的语言。这是使用perl 的示例。
perl -MText::ParseWords -lne '
chomp;
push @line, [ parse_line(",", 0, $_) ];
}{
@line = sort { $a->[1] <=> $b->[1] } @line;
for (@line) {
local $" = qw(",");
print qq("@$_");
}
' file
输出:
"aaa","1","xxx"
"bbb","609","zzz"
"ccc, Inc.","6100","yyy"
说明:
chomp 函数从输入中删除新行。 END 块中,对第二列的数组进行排序并将其分配给原始数组。 ",",并使用前面和后面的" 打印它以创建原始格式的行。 【讨论】:
将您的示例放入名为 sort2.txt 的文件中,我发现以下内容运行良好。sort -t'"' -k4n sort2.txt
使用以下命令排序(感谢乔纳森的改进)
希望这会有所帮助!
【讨论】:
less 的行为类似于cat,当它的输出进入管道时,sort 完全能够读取文件(所以sort -t '"' -k 4n sort2.txt 会更好;它也会按数字排序)。
"Joe ""The Man"" Bloggs"(有效 CSV)的第一个字段会使您的字段计数非常糟糕。你也可以在我的回答中提出这个抱怨,但它需要像 "Joe ""The Man"",""The Guy"" Bloggs" 这样的东西来混淆它,这更加深奥(我的一个“合理假设”是这样的字符串不会出现 - 有是嵌入逗号后的空格)。
不会有一个非常简单的解决方案。如果你做出一些合理的假设,那么你可以考虑:
sed 's/","/^A/g' input.csv |
sort -t'^A' -k 2n |
sed 's/^A/","/g'
这会将"," 序列替换为Control-A(在代码中显示为^A),然后将其用作sort 中的字段分隔符(第2 列的数字排序),然后再次将 Control-A 字符替换为 ","。
如果你使用bash,你可以使用ANSI C quoting机制$'\1'将控制字符可见地嵌入到脚本中;你只需要在转义之前完成单引号字符串,然后重新启动它:
sed 's/","/'$'\1''/g' input.csv |
sort -t$'\1' -k 2n |
sed 's/'$'\1''/","/g'
或者使用双引号而不是单引号,但由于您要替换双引号,这会变得混乱。但是您可以简单地逐字输入字符,vim 等编辑将很乐意向您展示。
【讨论】:
^A,还是应该使用$'\1'?
$'\1' 仅使用可打印字符(反斜杠和数字 1),而第一个命令序列中的 ^A 必须输入为 control-A。它们映射到同一个字符。您可以使用其中任何一个;您可以在其中一个中使用^A,在另一个中使用$'\1',但这可能会不必要地混淆人们。 (是的,我的代码自相矛盾;很抱歉,再次感谢 Dale Anderson!)
有时 CSV 文件中的值是可选的,仅在必要时才被引用。在这种情况下,使用" 作为分隔符是不可靠的。
例子:
"Forest fruits",198
Apples,456
bananas,67
使用awk、sort和cut,您可以对原始文件进行排序,这里按第一列:
awk -F',' '{
a = $1; # or the column index you want
gsub(/(^"|"$)/, "", a);
print a","$0
}' file.csv | sort -k1 | cut -d',' -f1 --complement
这会将您要排序的列放在前面,不带引号,然后按照您想要的方式对其进行排序,并在最后删除此列。
【讨论】: