Bash 文档中唯一的单词数
我有一个非常大的txt文件(500GiB),我想得到它唯一的单词数。我试过了,但它似乎很慢,因为它确实排序:Bash 文档中唯一的单词数,bash,text,grep,Bash,Text,Grep,我有一个非常大的txt文件(500GiB),我想得到它唯一的单词数。我试过了,但它似乎很慢,因为它确实排序: grep -o -E '\w+' temp | sort -u -f | wc -l 有什么更好的方法吗?awk救命 $ awk -v RS=" " '{a[$0]++} END{for(k in a) sum++; print sum}' file 更新: 最好使用tr进行预处理,并让awk经济地进行计数。您可能希望用空格或新行分隔单词 例如: $ tr ':;,?!\"' ' '
grep -o -E '\w+' temp | sort -u -f | wc -l
有什么更好的方法吗?
awk
救命
$ awk -v RS=" " '{a[$0]++} END{for(k in a) sum++; print sum}' file
更新:
最好使用tr
进行预处理,并让awk
经济地进行计数。您可能希望用空格或新行分隔单词
例如:
$ tr ':;,?!\"' ' ' < file | tr -s ' ' '\n' | awk '!a[$0]++{c++} END{print c}'
$tr':\“'
排序的一个重要特性是它具有区域设置意识,因此在C以外的任何区域设置中都要昂贵得多。由于您并不真正关心此处的顺序,您最好使用LC\u ALL=C sort-u-f
告诉sort忽略区域设置。如果您的区域设置为其他内容,则可能会减少执行离子时间减半
此答案的原始版本建议您仅在不关心非ascii字符的情况下执行此操作。但是,如果您使用的是Gnu coreutils,结果表明这些内容在UTF-8语言环境中都不起作用。而Gnusort
将在任何语言环境中执行语言环境感知字符串比较(使用strxfrm
标准库函数),sort-f
仅在单字节区域设置中进行区域设置感知大小写折叠。Gnuuniq-i
也有同样的问题。而tr
仅翻译单字节字符(根据设计,afaik);理论上[:alpha:]
可识别语言环境,但仅适用于可表示为单个字节的字符
简而言之,如果您想使用sort-u-f
,您还可以指定C
区域设置。对于非英语字母来说,这同样是不完整的,但至少不浪费时间
Gnu
awk
的tolower()
函数显然在多字节区域设置上有效。因此,如果需要在UTF-8区域设置中使用此函数,请查看awk
的一个答案。您可以依靠awk
的默认行为通过运行空格将行拆分为单词,并使用其关联数组:
awk '{ for (i=1; i<=NF; ++i) a[tolower($i)]++ } END { print length(a) }' file
- 关联数组
以一种计算输入中遇到的每个不同单词出现次数的方式构建,首先转换为小写,以便忽略大小写差异-如果不想忽略大小写差异,只需删除a
调用即可。tolower()
- 警告:Mawk和BSD Awk似乎不支持区域设置,因此
无法正确处理非ASCII字符tolower()
- 警告:Mawk和BSD Awk似乎不支持区域设置,因此
- 处理完所有单词后,
的元素数等于唯一单词数。a
- 注意:
的POSIX兼容重新格式为:打印长度(a)
用于(a中的k)+计数;打印计数
- 注意:
以上内容将适用于GNU Awk、Mawk(1.3.4+)和BSD Awk,即使它不是严格符合POSIX的(POSIX定义的
length
函数仅用于字符串,而不是数组)。您是否有500 GiB的备用磁盘空间?请尝试Awk
(将单词放入Awk关联数组)看看是否足够快。如果不是,我会用C++编写一个自定义程序(或类似的东西)。很抱歉,我刚才的想法没有更快。bash uniq程序似乎无法解决这个问题,但为什么不能呢?这是一个很有希望的方法,但是通过将RS
设置为”
(一个空格),您错误地将行尾\n
包含在每一行的最后一个字中,并且您还计算了多个相邻空格之间的空字。+1表示优雅的awk
脚本,但是,考虑到文件的大小和性能问题,最好只调用一个tr
并使用一个字段相反,在awk
中循环。还要注意的是OP不区分大小写。考虑到管道的工作方式,我认为大小并不重要。而且tr
的速度非常快。如果所有的唯一字都超过了内存的容量,这可能是个问题。这很好,但awk字段通常与grep字不同。\w
是[[:alnum]]iirc,所以我认为期望是“不!不?不,不”将包含一个唯一的单词,而不是四个。@rici足够简单。将FS
设置为[^[:alnum:][]+
,尽管类似于I
(事实上,如果这是一个单词)和带连字符的单词(可以只将-
添加到char类中,但仍取决于是否有-
未被伪装为em-dash的空格包装)这样会有问题。@4ae1e1:谢谢;我添加了一个基于'[^[:alnum:][]+'
的解决方案(这也需要忽略空字段)并添加了一个注释。@mklement0我想说的是不要检查空字段(这将导致每个字段的分支,并且肯定会对性能造成影响-至少在理论上如此),只需从最终结果中减去一个。@4ae1e1:这一点很好;虽然您不能盲目地进行减法,但在END
块中简单检查a
中是否存在空字符串键比检查每个已更新的输入字段更可取。
awk -F '[^[:alnum:]_]+' '{ for (i=1; i<=NF; ++i) { a[tolower($i)]++ } }
END { print length(a) - ("" in a) }' file