如何统计文件中唯一字符的数量?

6

如果有一个UTF-8编码的文件,其中包含多种语言的字符,如何计算它所包含的唯一字符数量,同时排除一些特定符号(例如:“!”,“@”,“#”,“。”)的计数?


1
你不能只用bash来做这件事。你需要用bash编写一个完整的程序。在这种情况下,最好使用一种编程语言。 - Shiplu Mokaddim
1
你必须使用“bash”的特定原因吗? - paulsm4
@fbernardo:那肯定是一些奇怪的作业 :) - Niklas B.
如果有一个时间测试的话,这将会更加有趣。 - tchrist
9个回答

8
这里有一个bash解决方案。 :)
bash$ perl -CSD -ne 'BEGIN { $s{$_}++ for split //, q(!@#.) }
                     $s{$_}++ || $c++ for split //;
                     END { print "$c\n" }' *.utf8

这是一个BASH解决方案......开个玩笑,不是真的 ;) - paulsm4
tchrist,-CSD 的脚本等效语句是什么? - Joel Berger
1
@JoelBerger 的脚本等价于 -CSDuse open qw(:std :utf8) - tchrist

5
在Python中:
import itertools, codecs

predicate = set('!@#.').__contains__
unique_char_count = len(set(itertools.ifilterfalse(
                      predicate, itertools.chain.from_iterable(codecs.open(filename, encoding="UTF-8")))))

当你迭代文件时,你会得到行。`chain`将它们连接在一起,因此遍历它时,你会得到字符。`ifilterfalse`消除符合条件的字符,条件定义为不允许字符集的成员。
没有使用itertools:
import codecs
disallowed = set('!@#.')
unique_char_count = len(set(char for line in codecs.open(filename, encoding="UTF-8") for char in line 
                              if char not in disallowed))

使用集合操作:

import codecs
unique = set()
any(unique.update(line) for line in codecs.open(filename, encoding="UTF-8"))
unique.difference_update('!@#.')
unique_char_count = len(unique)

3
我认为你需要使用 "*" 或 ".from_iterable" 才能使链式操作正常工作。 - DSM
1
@DSM 谢谢,我漏掉了 .from_iterable。我也加上了非 itertools 版本。 - agf
1
你可能想在那个 open 调用中指定编码。 - Niklas B.
1
@NiklasB。@tchrist 我使用了 codecs.open 并指定了 UTF-8。 - agf
1
+1. 我认为第三种方法是最好的选择。简单而且可能具有最佳性能。 - John La Rooy
显示剩余3条评论

3

使用集合的 Ruby:

require 'set'
string = 'ababbbababbabcdcccdbbaaba'
ignore = 'c'
(Set.new(string.chars) - Set.new(ignore.chars)).count
# => 3 
  • string 是一个输入字符串
  • ignore 是要忽略的字符组成的字符串
  • string.chars 是字符串中字符的列表
  • Set.new 用于生成一个集合
  • - 表示两个集合之间的差异
  • count 是结果集合中元素的数量

2
ignore.chars 就足够了,无需转换为集合。 - steenslag

3

另一个 Ruby 示例:

#encoding: utf-8
string = '@étude#@étude ฒณ!'
ignore = '!@#.'
p string.chars.to_a.uniq.join.delete(ignore).size #=>8

2

使用Perl单行命令:

echo -e "aba\ncfg!ഡ.#g" | perl -C7 -ne 'for(split(//)){if ($_ !~ /[!@#.]/) { print $_."\n"}}' | sort | uniq | wc -l

输出 7

如果您想忽略换行符:

echo -e "aba\ncfg!ഡ.#g" | perl -C7 -ne 'for(split(//)){if ($_ !~ /[!@#.\n]/) { print $_."\n"}}' | sort | uniq | wc -l

输出结果 6


1
你是指在 Perl + Bash 中的一行命令吗? - John La Rooy
1
@gnibbler 我说的是使用一行代码。你是对的,那是一个措辞错误 - 写了一件事情意味着另一件事情。我已经更正了。谢谢你指出。 - torrential coding

2
我来提供一种不需要语言的选择,以增加可选性:

我只是为了增加选择而提供这个不需要语言的选项:

sed 's/[!@#.]//g' /path/to/file | sed 's/./\0\n/g' | sort -u | wc -l

1
这甚至远远不能在我的系统上处理UTF-8文件。 在'wc'之前,有许多像'0n0n0n0n','0n0n0n0n0n0n0n0n'等的行。 是的,LANG ='en_US.UTF-8'。 你没有使用POSIX标准的 'sed',是吗? - tchrist
@Andrew Kandels - 我觉得这是一个很好的解决方案。如果原始文件是16位Unicode,你可以使用iconv:iconv -f utf-16 -t ascii sourcefile | sed 's/[!@#.]//g' /path/to/file | sed 's/./\0\n/g' | sort -u | wc -l - paulsm4
抱歉,我忘记了UTF-8部分。@paulsm4的添加应该解决这个问题。 - Andrew Kandels
@AndrewKandels 我不这样认为。你不能将Unicode转换为ASCII而不会失去所有的Unicode。此外,试图告诉sed使用\0表示整个模式甚至使用\n表示文字换行符是不可移植的,因为它们不是POSIX标准的一部分。还有Unicode的问题没有解决。 所以不需要语言就能做到这点,真是太棒了! :( 另一方面,像使用perl -CS -pe 's/(.)/$1\n/g'这样的替换方法是可移植的,因为与sed有许多彼此不兼容的版本不同,perl的版本并不多。 - tchrist
我不需要它来处理UTF-8 + 谷歌把我带到这里 + 这对我来说是最好的解决方案 = 点赞(但我在第一部分使用了 tr -d :)) - msb

2

经过三个小时的研究,我用Python完成了这个任务。

fname = "temp.txt"
num_lines = 0
num_words = 0
num_chars = 0
num_uniq  = 0
a = []
exclude = ",.@#$"
with open(fname, 'r') as f:
    for line in f:
        words = line.split()
        for word in words:
                char = list(word)
                a = a + char
        num_lines += 1
        num_words += len(words)
        num_chars += len(line)
print "Lines:%s\nWords:%s\nChars:%s" % (num_lines, num_words, num_chars)
num_uniq =  len(set(a)-set(exclude))
print "Unique Characters:%d" % (num_uniq)

这里是输出结果

Lines:6
Words:74
Chars:385
Unique Characters:26

1
使用Python中的集合。 假设您想查找文件url.txt中的唯一字符。
f=open('url.txt')
a=''
for x in f:
    x=x.split(' ')
    for y in x:
     a+=y
unique=set(a)-set('@!#.')  #add the characters that you wanna neglect in the second set
print(unique)
print('unique characters : ',len(unique))

假设url.txt包含:

Google --!  google.com  --!  coolest search engine 

facebook --!  facebook.com  --!  biggest social network 

yahoo --!  yahoo.com  --!  biggest web portal 

输出将是:

{'a', 'G', 'm', '\n', 'n', 'c', 'b', 'e', 'g', 'f', 'i', 'h', 'k', '-', 'l', 'o', 'p', 's', 'r', 't', 'w', 'y'}
unique characters :  22

我的答案已经给出了几个本质上相同的Python版本。此外,你的字符串构建器的性能非常糟糕。添加字符串很慢——如果你必须去掉空格并连接行,应该使用像''.join(''.join(x.split()) for x in f)这样的东西,它会更快。请参阅我的答案以了解如何在不构建长字符串的情况下完成它。 - agf

0

一个替代方案:

filename='/somewhere/my-file-in-utf8'

iconv -f UTF8 -t UTF16 $filename | tail -c +3 |  \
perl -pi -e "s/\x00\@//g; s/\x00\!//g; s/\x00\#//g; s/\x00\.//g;" | \
od | cut -b 8- | xargs -n 1 | sort | uniq | wc -l

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接