按行长度(包括空格)sorting文本文件
我有一个看起来像这样的CSV文件
AS2345,ASDF1232,Plain案例,110 Binary Ave.,Atlantis,RI,12345,(999)123-5555,1.56 AS2345,ASDF1232,夫人平原例子,1121110三元st。 110 Binary ave ..,Atlantis,RI,12345,(999)123-5555,1.56 AS2345,ASDF1232,Plain案例,110 Binary Ave.,Liberty City,RI,12345,(999)123-5555,1.56 AS2345,ASDF1232,平原例,110三重大学,一些城市,RI,12345,(999)123-5555,1.56
我需要按照包括空格在内的行长来sorting。 下面的命令不包含空格,有没有办法修改它,所以它会为我工作?
cat $@ | awk '{ print length, $0 }' | sort -n | awk '{$1=""; print $0}'
回答
cat testfile | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2-
或者,对原始的(也许是无意的)任何等长的行进行子sorting:
cat testfile | awk '{ print length, $0 }' | sort -n | cut -d" " -f2-
在这两种情况下,我们已经通过从awk中删除最终剪切来解决您所陈述的问题。
匹配长度的线 – 在平局的情况下该怎么做:
这个问题没有具体说明是否需要对匹配长度的行进一步分类。 我认为这是不必要的,并build议使用-s
(– --stable
)来防止这些行相互sorting,并保持它们在input中出现的相对顺序。
(那些想要对这些关系进行更多控制的人可能会考虑--key
选项。)
为什么问题的尝试解决scheme失败(awk line-rebuilding):
有趣的是要注意区别:
echo "hello awk world" | awk '{print}' echo "hello awk world" | awk '{$1="hello"; print}'
他们分别产量
hello awk world hello awk world
(gawk's)手册的相关部分仅仅提到awk将在重新创build$ 0(基于分隔符等)时更改一个字段。 我猜这不是疯狂的行为。 它有这样的:
“最后,有时可以用awk来重build整个logging,使用字段和OFS的当前值,方法是使用看起来无害的任务:”
$1 = $1 # force record to be reconstituted print $0 # or whatever else with $0
“这迫使awk重buildlogging。”
testinginput包括一些等长的行:
aa A line with MORE spaces bb The very longest line in the file ccb 9 dd equal len. Orig pos = 1 500 dd equal len. Orig pos = 2 ccz cca ee A line with some spaces 1 dd equal len. Orig pos = 3 ff 5 dd equal len. Orig pos = 4 g
试试这个命令:
awk '{print length, $0}' your-file | sort -n | cut -d " " -f2-
如果你真的想使用awk
,那么neillb的AWK解决scheme是非常棒的,这也解释了为什么它很麻烦,但是如果你想要快速完成工作而不关心你在做什么,一种解决scheme是使用Perl的sort()
函数和自定义的caparison例程遍历input行。 这里是一个class轮:
perl -e 'print sort { length($a) <=> length($b) } <>'
你可以把它放在你需要的地方,无论是接收STDIN(从cat
还是shellredirect),或者把文件名作为另一个参数,让它打开文件。
在我的情况下,我需要最长的行首,所以我换了$a
和$b
比较。
纯粹的Bash:
declare -a sorted while read line; do if [ -z "${sorted[${#line}]}" ] ; then # does line length already exist? sorted[${#line}]="$line" # element for new length else sorted[${#line}]="${sorted[${#line}]}\n$line" # append to lines with equal length fi done < data.csv for key in ${!sorted[*]}; do # iterate over existing indices echo -e "${sorted[$key]}" # echo lines with equal length done
length()
函数包含空格。 我会对你的pipe道做一些小的调整(包括避免UUOC )。
awk '{ printf "%d:%s\n", length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]*://'
sed
命令直接删除由awk
命令添加的数字和冒号。 或者,保持您的格式从awk
:
awk '{ print length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]* //'
用POSIX Awk:
{ c = length m[c] = m[c] ? m[c] RS $0 : $0 } END { for (c in m) print m[c] }
例
如果您的文件包含以数字开头的行,我发现这些解决scheme将不起作用,因为它们将与所有计数行一起按数字sorting。 解决的办法是sort
-g
(通用数字sorting)标志而不是-n
(数字sorting):
awk '{ print length, $0 }' lines.txt | sort -g | cut -d" " -f2-
这是一个多字节兼容的方法,按行长sorting。 这个需要:
-
wc -m
可用(macOS有)。 - 您当前的语言环境支持多字节字符,例如,通过设置
LC_ALL=UTF-8
。 你可以在.bash_profile中设置它,或者简单地在下面的命令之前加上它。 -
testfile
有一个符合你的语言环境的字符编码(例如,UTF-8)。
以下是完整的命令:
cat testfile | awk '{l=$0; gsub(/\047/, "\047\"\047\"\047", l); cmd=sprintf("echo \047%s\047 | wc -m", l); cmd | getline c; close(cmd); sub(/ */, "", c); { print c, $0 }}' | sort -ns | cut -d" " -f2-
部分解释:
-
l=$0; gsub(/\047/, "\047\"\047\"\047", l);
←使awkvariablesl
的每一行的副本成为一行,并且双重转义每一行'
因此行可以安全地作为shell命令回显(\047
是以八进制表示法的单引号)。 -
cmd=sprintf("echo \047%s\047 | wc -m", l);
←这是我们要执行的命令,它将转义线回显到wc -m
。 -
cmd | getline c;
←执行命令并将返回的字符计数值复制到awkvariablesc
。 -
close(cmd);
←closurespipe道到shell命令,以避免在一个进程中打开文件的数量限制系统。 -
sub(/ */, "", c);
←由wc
返回的字符计数值修整空白。 -
{ print c, $0 }
←打印行的字符计数值,空格和原始行。 -
| sort -ns
| sort -ns
←数字(-n
)sorting行(通过字符计数值的前置),并保持稳定的sorting顺序(-s
)。 -
| cut -d" " -f2-
| cut -d" " -f2-
←删除前缀字符计数值。
速度很慢(在Macbook Pro上只有每秒160行),因为它必须为每一行执行一个子命令。
或者,只需要使用gawk
(从版本3.1.5开始,gawk可以识别多字节),这样做会快得多。 执行所有转义和双引号以安全地通过来自awk的shell命令传递行,这是很麻烦的,但是这是我能find的唯一不需要安装额外软件的方法(gawk默认情况下不可用苹果系统)。
基准testing结果
另外,另一个Perl解决scheme:
perl -ne 'push @a, $_; END{ print sort { length $a <=> length $b } @a }' file
实验运行使用:
- 在一台快速机器上连续运行10次,取平均值
- Perl 5.24
- awk 3.1.5(gawk 4.1.0倍〜2%快)
- input文件是一个550MB,600万行的怪物(英国国家语料库txt)
结果:
- fgm bash解决scheme要比awk解决scheme长400倍(使用截断的100000行testing用例)。 它工作正常,只需要很长时间。
- 乔纳森awk解决了25秒
- anubhava awk解决scheme耗时24秒
- neillb awk解决scheme#2花了23秒钟
- neillb awk解决scheme#1耗时20秒
- 我的Perl解决scheme花了11.6秒
- caleb的perl soution花了11.2秒