场景

现在有两个词典,一个 1600万,一个 200万,里面的格式分别为:

1
2
3
4
5
1600万的格式:
101团 nr 0

200万的格式:
179 FC经理 x 0

领导说要统计一下其中长度大于 7 的词,以及对比两个词典中长度为 2 的词。

通常的,我们可能会写个程序来进行对比,但我觉得太麻烦了,是不是有更快捷的方式?毕竟,我们在服务器上进行日志分析的时候,往往也会用到这种文本分析的。

实现

抽取及统计

虽然我系统是Window,但不得不说,在命令行方面,还是linux用得爽得多,所以我这里用的是WSL2。日常连接服务器也是WSL2 + tmux + ssh,无缝切换,挺好。

linux下,常用的都有:grep,cat,less,awk这些文本方面的命令。其中最强大的就数awk了吧,尽管我基本用得很少,也不是很熟。

统计长度

我们先看下怎样用awk统计每行的长度:

1
awk '{print NR ":" length($0);}' file.txt

结果为:

1
2
26931:11
...

这种格式,这里说明一下各个参数:

print:表示直接打印;

NR:表示当前行,输出来就是行号;

length($0):这里因为是用的$0,所以是表示整行的长度,如果想要统计我需求里面的单个词,而不是整行,则需要使用$1,表示按空格或 table分割的第一个文本:

1
2
3
4
5
6
101团 nr 0
这个文本的参数对应关系:
$0: 101团 nr 0
$1: 101团
$2: nr
$3: 0

判断长度

现在可以统计长度了,那怎么判断只输出长度大于 7 的呢?

awk是支持判断、循环等分支操作的,同样很简单:

1
2
3
4
5
6
awk '{
    // 取到词的长度,这里用的是 $1,而不是 $0
    num = length($1); 
    // 判断词长度 >7 才输出
    if (num > 7 ) print NR ":" num;
}' file.txt

这样,我们就可以看到所有词长度大于 7 的,就输出了。但这个输出很多,压根看不过来;而且我们要的也是一个合计,而不是词。

统计词数量

拿到所有的词后,我们可以通过管道进行统计。没错直接加一个wc -l即可:

1
2
3
4
awk '{
    num = length($1); 
    if (num > 7 ) print NR ":" num;
}' file.txt | wc -l

这时,就只会输出整个结果的总数了。很快、很方便,不用写代码。

文本对比

既然可以统计长度了,那我们可以先把长度为 2 的写到另外的文件,因为两个词典的格式有区别,所以这里要分别写,主要就是分割后的词位置不同:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 大词典
awk '{
    num = length($1);
    if (num == 2 ) print $1;
}' big.txt > 2_big.txt

// 小词典
awk '{
    num = length($2);
    if (num == 2 ) print $2;
}' small.txt > 2_small.txt

这样,长度为 2 的所有词,就单独写到了不同的文件,我们只需要对比一下即可。

如果我想看在小词典(2_small.txt)有,大词典(2_big.txt)中没有的词,就这样写:

1
awk 'NR==FNR{ a[$1]=$1 } NR>FNR{ if(a[$1] == ""){ print $1}}' 2_big.txt 2_small.txt

相反,如果想看在大词典(2_big.txt)有,小词典(2_small.txt)中没有的词,就交换文件位置即可:

1
awk 'NR==FNR{ a[$1]=$1 } NR>FNR{ if(a[$1] == ""){ print $1}}' 2_small.txt 2_big.txt

如果输出太多,不方便看,还可以直接写到另外的文件:

1
awk 'NR==FNR{ a[$1]=$1 } NR>FNR{ if(a[$1] == ""){ print $1}}' 2_small.txt 2_big.txt > in_big.txt

总结

对于这种文本统计、对比、简单分析的工作,其实不需要去些代码,往往就有非常简单的实现。写代码还要去读取文件、编码,还要编译环境、编码工具等等,通用性也不高。

而通过这种使用命令的方式去实现,我们还能够从中学到知识。