目录D包含.eml格式的几千封电子邮件。一些电子邮件是纯文本,其他电子邮件来自Outlook,其他电子邮件具有ASCII标头和HTML / MIME内容等等。存在一个字典文件F,其中包含要在D目录下面的文件中查找的有趣单词列表(即red \ nblue \ ngreen \ n ...)。 D目录有大量子文件夹,但没有上述.eml文件以外的文件。应使用这些规范列出重复出现的单词:
这将构建一个引用不同事件的索引和最常出现的有趣词汇的摘要。
输出应该在单个输出文件上,并且格式没有严格定义,只要包括上面的信息:有趣的单词,每个有趣单词出现的次数以及它的作用 - >文件/行/启动位置。
这不是一个家庭作业练习,而是我想要制作一个相当大的数据集的实际文本分析。我遇到的挑战是选择合适的过滤工具进行有效过滤。一个迭代的方法,单词/电子邮件/等的笛卡尔积,太慢了,并且希望为每个文件的每一行组合多个单词过滤。
我已经尝试从有趣的单词列表w1 | w2 | w3 | ...构建替代正则表达式,编译并运行每个电子邮件的每一行,但它仍然很慢,特别是当我需要时在一行内检查多次出现。
示例:
电子邮件E有一行包含文字:
^ ......等等......红苹果......蓝色蓝莓......红色,白色和蓝色标志。$ \ n
正则表达式正确地报告了红色(2)和蓝色(2),但是当使用真实的,非常大的有趣单词词典时它很慢。
我尝试的另一种方法是:
使用Sqlite数据库在解析时转储令牌,包括每个条目的(列,位置)信息,并在最后查询输出。批量插入对于使用适当的内存缓冲区有很大帮助,但会增加复杂性。
我还没有尝试过数据并行化,因为我不确定令牌/解析是否是首先要做的事情。也许一棵树更合适?
我对解决方案感兴趣,按优先顺序排列:
答案 0 :(得分:2)
一些评论:
(当然是伪代码)
result <- empty list
for each email e:
for each word w:
if is_interesting_word(w, string_data_structure):
add (filename, line_number, start_position, word) to results
我会减少你需要的处理量:没有正则表达式,没有高级解析;只需遍历电子邮件中的每个字符/行,并跟踪您的位置(行号,位置等)。最后一步,分析您的代码并优化它所受的伤害:)
答案 1 :(得分:2)
我假设你可以创建/找到一个eml-to-text转换器。那么这与你想要的非常接近:
find -type f | parallel --tag 'eml-to-text {} | grep -o -n -b -f /tmp/list_of_interesting_words'
输出格式不是100%格式化:
filename \ t line no:byte no(从文件开头):word
如果你有很多有趣的词在grep
是慢启动,因此,如果您可以创建邮件目录的解压版本,你可以使并行启动grep
次数越少“-f”:
find . -type f | parallel 'eml-to-text {} >/tmp/unpacked/{#}'
find /tmp/unpacked -type f | parallel -X grep -H -o -n -b -f /tmp/list_of_interesting_words
由于grep -f
的时间复杂度比线性更差,您可能希望将/ tmp / list_of_interesting_words切割成更小的块:
cat /tmp/list_of_interesting_words | parallel --pipe --block 10k --files > /tmp/blocks_of_words
然后并行处理块和文件:
find /tmp/unpacked -type f | parallel -j1 -I ,, parallel --arg-file-sep // -X grep -H -o -n -b -f ,, {} // - :::: /tmp/blocks_of_words
此输出的格式如下:
filename:行号:字节号(从文件开头):单词
要按word
分组,而不是文件名,请通过sort:
... | sort -k4 -t: > index.by.word
计算频率:
... | sort -k4 -t: | tee index.by.word | awk 'FS=":" {print $4}' | uniq -c
好消息是,这应该是相当快的,我怀疑你将能够使用Python达到相同的速度。
编辑:
grep -F在启动时速度更快,你需要-w用于grep(因此'gram'这个词与'diagram'不匹配);这也将避免临时文件,并且可能相当快:
find . -type f | parallel --tag 'eml-to-text {} | grep -F -w -o -n -b -f /tmp/list_of_interesting_words' | sort -k3 -t: | tee index.by.word | awk 'FS=":" {print $3}' | uniq -c
答案 2 :(得分:0)
的Python:
list = ['a', 'bunch', 'of', 'interesting', 'words']
linepos = 0
with open("file") as f:
for line in f:
linepos += 1
wordpos = 0
for word in line.split():
wordpos += 1
if word in list:
print "%s found at line %s, word %s" % (word, linepos, wordpos)