我正在寻找一种算法甚至算法空间来处理验证短文本(电子邮件)与已知模板匹配的问题。编码可能是python或perl,但这很灵活。
问题在于:
有权访问生产数据的服务器需要能够发送到达Internet的电子邮件:
Dear John Smith,
We received your last payment for $123.45 on 2/4/13. We'd like you to be aware of the following charges:
$12.34 Spuznitz, LLC on 4/1
$43.21 1-800-FLOWERS on 4/2
As always, you can view these transactions in our portal.
Thank you for your business!
显然,一些电子邮件内容会有所不同 - 称呼(“约翰史密斯”),“2013年2月4日的123.45美元”,以及打印出的交易行。其他部分(“我们收到您的最后一笔付款”)非常静态。我希望能够匹配文本的静态部分并量化动态部分在一定的合理限度内(我可能知道要打印的大多数事务行是5,例如)。
因为我担心数据泄露,所以我想确保与此模板不匹配的电子邮件永远不会消失 - 我想检查电子邮件并隔离任何看起来不像我期望的内容。所以我需要自动化这个模板匹配并阻止任何远离匹配的电子邮件。
所以问题是,我在哪里寻找过滤机制?贝叶斯过滤试图验证特定消息和非特定语料库之间的足够相似性,这是一种相反的问题。像Perl的模板模块这样的东西是紧密匹配 - 但是对于输出,不是输入或比较。简单的“差异”类型比较不能很好地处理有限的动态信息。
如何测试这些外发电子邮件是否“像鸭子一样嘎嘎叫”?
答案 0 :(得分:4)
您可以使用语法进行紧密匹配。可以在语法中组织正则表达式以便于抽象:http://www.effectiveperlprogramming.com/blog/1479
或者您可以使用专用语法引擎Marpa。
如果您想要更加统计的方法,请考虑n-grams。首先,标记文本并用有意义的占位符替换变量块,例如CURRENCY
和DATE
。然后,构建 n-gram 。现在,您可以使用Jaccard index来比较两个文本。
这是一个Pure-Perl实现,适用于三元组:
#!/usr/bin/env perl
use strict;
use utf8;
use warnings;
my $ngram1 = ngram(3, tokenize(<<'TEXT1'));
Dear John Smith,
We received your last payment for $123.45 on 2/4/13. We'd like you to be aware of the following charges:
$12.34 Spuznitz, LLC on 4/1
$43.21 1-800-FLOWERS on 4/2
As always, you can view these transactions in our portal.
Thank you for your business!
TEXT1
my $ngram2 = ngram(3, tokenize(<<'TEXT2'));
Dear Sally Bates,
We received your last payment for $456.78 on 6/9/12. We'd like you to be aware of the following charges:
$123,43 Gnomovision on 10/1
As always, you can view these transactions in our portal.
Thank you for your business!
TEXT2
my %intersection =
map { exists $ngram1->[2]{$_} ? ($_ => 1) : () }
keys %{$ngram2->[2]};
my %union =
map { $_ => 1 }
keys %{$ngram1->[2]}, keys %{$ngram2->[2]};
printf "Jaccard similarity coefficient: %0.3f\n", keys(%intersection) / keys(%union);
sub tokenize {
my @words = split m{\s+}x, lc shift;
for (@words) {
s{\d{1,2}/\d{1,2}(?:/\d{2,4})?}{ DATE }gx;
s{\d+(?:\,\d{3})*\.\d{1,2}}{ FLOAT }gx;
s{\d+}{ INTEGER }gx;
s{\$\s(?:FLOAT|INTEGER)\s}{ CURRENCY }gx;
s{^\W+|\W+$}{}gx;
}
return @words;
}
sub ngram {
my ($size, @words) = @_;
--$size;
my $ngram = [];
for (my $j = 0; $j <= $#words; $j++) {
my $k = $j + $size <= $#words ? $j + $size : $#words;
for (my $l = $j; $l <= $k; $l++) {
my @buf;
for my $w (@words[$j..$l]) {
push @buf, $w;
}
++$ngram->[$#buf]{join(' ', @buf)};
}
}
return $ngram;
}
您可以将一个文字用作模板,并将其与电子邮件进行匹配。 检查String::Trigram以获得有效的实施。 Google Ngram Viewer是一个很好的资源来说明 n-gram 匹配。
答案 1 :(得分:3)
如果您希望将预先存在的模板与例如控制流量元素如{% for x in y %}
对应它的假定输出,你将不得不解析模板语言 - 这似乎很多工作。
另一方面,如果您准备编写第二个模板用于验证目的 - 例如:
Dear {{customer}},
We received your last payment for {{currency}} on {{full-date}}\. We'd like you to be aware of the following charges:
( {{currency}} {{supplier}} on {{short-date}}
){,5}As always, you can view these transactions in our portal\.
...这只是正则表达式语法的一个简单扩展,它可以非常简单地将一些东西组合在一起来验证它:
import re
FIELDS = {
"customer": r"[\w\s\.-]{,50}",
"supplier": r"[\w\s\.,-]{,30}",
"currency": r"[$€£]\d+\.\d{2}",
"short-date": r"\d{,2}/\d{,2}",
"full-date": r"\d{,2}/\d{,2}/\d{2}",
}
def validate(example, template_file):
with open(template_file) as f:
template = f.read()
for tag, pattern in FIELDS.items():
template = template.replace("{{%s}}" % tag, pattern)
valid = re.compile(template + "$")
return (re.match(valid, example) is not None)
上面的例子无论如何都不是有史以来最伟大的Python代码,但它足以让我们了解整体想法。
答案 2 :(得分:2)
我会选择&#34;最长的常见子序列&#34;。标准实现可以在这里找到:
http://en.wikibooks.org/wiki/Algorithm_implementation/Strings/Longest_common_subsequence
如果你需要一个更好的算法和/或许多额外的想法来不精确匹配字符串,标准参考是这本书:
http://www.amazon.com/Algorithms-Strings-Trees-Sequences-Computational/dp/0521585198
不要被标题所迷惑。计算生物学主要是关于大型长串数据库(也称为DNA序列)的匹配。