Bash 文档中唯一的单词数

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 ':;,?!\"' ' '

我有一个非常大的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 ':;,?!\"' ' ' < 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语言环境中都不起作用。而Gnu
sort
将在任何语言环境中执行语言环境感知字符串比较(使用
strxfrm
标准库函数),
sort-f
仅在单字节区域设置中进行区域设置感知大小写折叠。Gnu
uniq-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似乎不支持区域设置,因此
      tolower()
      无法正确处理非ASCII字符
  • 处理完所有单词后,
    a
    的元素数等于唯一单词数。
    • 注意:
      打印长度(a)
      的POSIX兼容重新格式为:
      用于(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