【问题标题】:How to match 2 strings by X% (i.e. >90% matching)如何通过 X% 匹配 2 个字符串(即 >90% 匹配)
【发布时间】:2012-06-22 07:12:08
【问题描述】:

例如:

S1: "some filename contains few words.txt"
S2: "some filename contains few words - draft.txt"
S3: "some filename contains few words - another draft.txt"
S4: "some filename not contains few words.txt"

需要注意的是,我可以为第一个字符串获取 S2 或 S3,而其他字符串则可以匹配。

已编辑:我有“主”字符串,我需要找到匹配项。

假设在第一轮中,我发现了错别字。

现在我只需要匹配整个单词。

我希望能够确定 7 个单词中有 5 个匹配,或者 10 个单词中有 7 个匹配。“X out of Y”的确切数字不太重要。

重要的是如何找到X个单词的区别,不管它们在句子中的位置。

谢谢

【问题讨论】:

  • 添加适当的语言标签。
  • 不确定这是否可以使用 RegEx 完成,但请看一下 Levenshtein 距离算法:dotnetperls.com/levenshtein
  • 这并不适合正则表达式;更像是寻找共同的子串。
  • @dirkgentle 逻辑对我来说很重要。剩下的就是技术性了。我更喜欢 c#、javascript 或 java。
  • 您可以使用多种相似性度量。 Levinshtein 距离当然是一个受欢迎的候选者,但它可能与您对相似百分比的直觉不完全相符。您还可以查看使用的 n-gram 算法,例如用于语言识别; Google for TextCat。

标签: c# java javascript regex


【解决方案1】:

这不是正则表达式问题。

您无需指定语言,但如果您使用的是 java,则可以使用 StringUtils 的 getLevenshteinDistance 方法。来自 javadocs:

求两个字符串之间的 Levenshtein 距离。

这是将一个字符串更改为所需的更改次数 另一个,其中每个更改都是单个字符修改 (删除、插入或替换)。

用法:

int distance = StringUtils.getLevenshteinDistance(
    "some filename contains few words.txt",
    "some filename not contains few words.txt"
);

要匹配某个百分比,您必须确定哪个字符串是“主”字符串,因为输入字符串可以有不同的长度:distance 可能都是删除,所以"cat" 和"cataract" 有一个距离的5。定义“90% 匹配”应该是什么也有点困难。查看我们的cat 示例; 100% 的字符串“cat”出现在“cataract”中,但它们不是完全相同的字符串。您必须根据您的用例来决定这些规则。

更新

如果您的“差异”应该是基于单词的,那么在单词边界上拆分字符串并从结果单词到每个单词的计数构造一个 Map 会相对容易。比较每个字符串生成的地图应该会给你一个粗略的“相似性”测量。例如:

public HashMap<String, Integer> countWords(String str) {
    HashMap<String, Integer> counts = new HashMap<String, Integer>();
    for(String s : str.split("\\s+")) {
        if(!s.isEmpty()) {
            if(counts.containsKey(s)) {
                counts.put(s, counts.get(s) + 1);
            } else {
                counts.put(s, 1);
            }
        }
    }
    return counts;
}

// ...

String s1 = "some filename contains few words.txt";
String s2 = "some filename not contains few words.txt";
HashMap<String, Integer> s1Counts = countWords(s1);
HashMap<String, Integer> s2Counts = countWords(s2);
// assume s1 is "master" string, count the total number of words
int s1Total = 0, s2Total = 0;
for(Integer i : s1Counts.values()) {
    s1Total += i;
}
// iterate over words in s1, find the number of matching words in s2
for(Map.Entry<String, Integer> entry : s1Counts.entrySet()) {
    if(s2Counts.containsKey(entry.getKey())) {
        if(s2Counts.get(entry.getKey()) >= entry.getValue()) {
            s2Total += entry.getValue();
        } else {
            s2Total += s2Counts.get(entry.getKey());
        }
    }
}
// result
System.out.println(s2Total + " out of " + s1Total + " words match.");

【讨论】:

  • 我想说你应该在这里寻找公共子字符串而不是Levenshtein距离。
  • 感谢您的麻烦。在发布问题之前,我花了很多时间思考它。我知道“主”字符串,我需要猜测用户希望看到的“相似”。
  • 假设我有主字符串。可以说,差异是 2 个单词,在 2 个不同的地方。 “距离”会很高,但几句话就可以了。
【解决方案2】:

我觉得值得一提的是看一下Apache commons-text classJaroWinklerDistance

Find the Jaro Winkler Distance which indicates the similarity score between two CharSequences.
 distance.apply(null, null)          = IllegalArgumentException
 distance.apply("","")               = 0.0
 distance.apply("","a")              = 0.0
 distance.apply("aaapppp", "")       = 0.0
 distance.apply("frog", "fog")       = 0.93
 distance.apply("fly", "ant")        = 0.0
 distance.apply("elephant", "hippo") = 0.44
 distance.apply("hippo", "elephant") = 0.44
 distance.apply("hippo", "zzzzzzzz") = 0.0
 distance.apply("hello", "hallo")    = 0.88
 distance.apply("ABC Corporation", "ABC Corp") = 0.93
 distance.apply("D N H Enterprises Inc", "D & H Enterprises, Inc.") = 0.95
 distance.apply("My Gym Children's Fitness Center", "My Gym. Childrens Fitness") = 0.92
 distance.apply("PENNSYLVANIA", "PENNCISYLVNIA")    = 0.88

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-10
    • 2011-09-04
    • 1970-01-01
    • 2019-12-26
    • 2015-06-18
    相关资源
    最近更新 更多