我正在为我正在进行的项目执行CSV导入工具。 客户端需要能够在excel中输入数据,将它们导出为CSV并将它们上传到数据库。 例如,我有这个CSV记录:
1, John Doe, ACME Comapny (the typo is on purpose)
当然,这些公司被保存在一个单独的表中并与外键相关联,因此我需要在插入之前发现正确的公司ID。 我计划通过将数据库中的公司名称与CSV中的公司名称进行比较来实现此目的。 如果字符串完全相同,则比较应返回0,并返回一些随着字符串变得更大而变大的值,但strcmp不会在此处删除它,因为:
“Acme Company”和“Acme Comapny”应该有一个非常小的差异指数,但是 “Acme公司”和“Cmea Mpnyaco”应该有很大的差异指数 或“Acme Company”和“Acme Comp。”即使字符数不同,也应该有一个小的差异索引。 此外,“Acme Company”和“Company Acme”应返回0。
因此,如果客户在输入数据时输入类型,我可以提示他选择他最想插入的名称。
是否有已知算法可以做到这一点,或者我们可以发明一个:) ?
答案 0 :(得分:17)
您可能希望查看Levenshtein Distance算法作为起点。它将评估两个单词之间的“距离”。
This SO thread实施Google风格的“你的意思是......?”系统也可以提供一些想法。
答案 1 :(得分:9)
我不知道您编写的是哪种语言,但如果是PHP,则应考虑以下算法:
levenshtein():返回必须替换,插入或删除的最小字符数,以将一个字符串转换为另一个字符串。
soundex():返回单词的四个字符的soundex键,该键应与任何类似发音单词的键相同。
metaphone():类似于soundex,可能对您更有效。它比soundex()更准确,因为它知道英语发音的基本规则。 metaphone生成的密钥长度可变
similar_text():与levenshtein()类似,但它可以返回百分比值。
答案 2 :(得分:2)
我使用Levenshtein Distance算法取得了一些成功,还有Soundex。
你在用什么语言实现这个?我们或许可以指出具体的例子
答案 3 :(得分:2)
我实际上已经实现了类似的系统。我使用了Levenshtein距离(正如其他海报已经提出的那样),并进行了一些修改。未经修改的编辑距离(应用于整个字符串)的问题在于它对单词重新排序很敏感,因此“Acme Digital Incorporated World Company”与“Digital Incorporated World Company Acme”的匹配性很差,并且此类重新排序在我的数据中非常常见。
我对它进行了修改,以便如果整个字符串的编辑距离太大,算法会回到相互匹配的单词以找到一个好的单词到单词匹配(二次成本,但是如果存在则有截止是太多的话,所以它工作正常。)
答案 4 :(得分:2)
我采用了SoundEx,Levenshtein,PHP相似性和双元电话,并在String中的一组扩展方法中将它们打包在C#中。
答案 5 :(得分:0)
有多种算法可以做到这一点,大多数数据库甚至默认包含一个。这实际上是一个非常普遍的问题。
如果它只是英文单词,例如SQL Server包含SOUNDEX,可用于比较单词的结果声音。
http://msdn.microsoft.com/en-us/library/aa259235%28SQL.80%29.aspx
答案 6 :(得分:0)
我正在用PHP实现它,我现在正在编写一段代码,它将用单词分解2个字符串,并使用levenshtein将第一个字符串中的每个单词与第二个字符串的单词进行比较并接受降低可能的值。我做完之后就发布了。
非常感谢。
更新:这是我提出的:
function myLevenshtein( $str1, $str2 )
{
// prepare the words
$words1 = explode( " ", preg_replace( "/\s+/", " ", trim($str1) ) );
$words2 = explode( " ", preg_replace( "/\s+/", " ", trim($str2) ) );
$found = array(); // array that keeps the best matched words so we don't check them again
$score = 0; // total score
// In my case, strings that have different amount of words can be good matches too
// For example, Acme Company and International Acme Company Ltd. are the same thing
// I will just add the wordcount differencre to the total score, and weigh it more later if needed
$wordDiff = count( $words1 ) - count( $words2 );
foreach( $words1 as $word1 )
{
$minlevWord = "";
$minlev = 1000;
$return = 0;
foreach( $words2 as $word2 )
{
$return = 1;
if( in_array( $word2, $found ) )
continue;
$lev = levenshtein( $word1, $word2 );
if( $lev < $minlev )
{
$minlev = $lev;
$minlevWord = $word2;
}
}
if( !$return )
break;
$score += $minlev;
array_push( $found, $minlevWord );
}
return $score + $wordDiff;
}