【问题标题】:Find difference between two Strings查找两个字符串之间的差异
【发布时间】:2012-08-23 06:40:29
【问题描述】:

假设我有两个长字符串。它们几乎相同。

String a = "this is a example"
String b = "this is a examp"

以上代码只是举例。实际的字符串很长。

问题是一个字符串比另一个字符串多 2 个字符

如何检查这两个字符是哪一个?

【问题讨论】:

    标签: java string comparison


    【解决方案1】:

    您可以使用StringUtils.difference(String first, String second)

    他们是这样实现的:

    public static String difference(String str1, String str2) {
        if (str1 == null) {
            return str2;
        }
        if (str2 == null) {
            return str1;
        }
        int at = indexOfDifference(str1, str2);
        if (at == INDEX_NOT_FOUND) {
            return EMPTY;
        }
        return str2.substring(at);
    }
    
    public static int indexOfDifference(CharSequence cs1, CharSequence cs2) {
        if (cs1 == cs2) {
            return INDEX_NOT_FOUND;
        }
        if (cs1 == null || cs2 == null) {
            return 0;
        }
        int i;
        for (i = 0; i < cs1.length() && i < cs2.length(); ++i) {
            if (cs1.charAt(i) != cs2.charAt(i)) {
                break;
            }
        }
        if (i < cs2.length() || i < cs1.length()) {
            return i;
        }
        return INDEX_NOT_FOUND;
    }
    

    【讨论】:

    • 据我所知,这不会返回不同的字符,而是返回字符串不再匹配的整个字符串......
    • @brimborium 所以也许你应该澄清你的问题。我认为这个答案非常适合您最初的问题。
    • @alcor 我不是 OP。 ;) 我不同意。原标题有点误导,但问题本身总是明确表示他要提取奇数字符...
    【解决方案2】:

    要找出 2 个字符串之间的差异,您可以使用 StringUtils 类和 difference 方法。它比较两个字符串,并返回它们不同的部分。

     StringUtils.difference(null, null) = null
     StringUtils.difference("", "") = ""
     StringUtils.difference("", "abc") = "abc"
     StringUtils.difference("abc", "") = ""
     StringUtils.difference("abc", "abc") = ""
     StringUtils.difference("ab", "abxyz") = "xyz"
     StringUtils.difference("abcde", "abxyz") = "xyz"
     StringUtils.difference("abcde", "xyz") = "xyz"
    

    【讨论】:

      【解决方案3】:

      如果不遍历字符串,您只能知道 它们是不同的,而不是 where - 只有当它们的长度不同时。如果你真的需要知道不同的字符是什么,你必须逐个遍历两个字符串并比较对应位置的字符。

      【讨论】:

        【解决方案4】:

        下面的 Java sn-p 有效地计算了一组最小的字符,这些字符必须从相应的字符串中删除(或添加到)以使字符串相等。这是一个动态规划的例子。

        import java.util.HashMap;
        import java.util.Map;
        
        public class StringUtils {
        
            /**
             * Examples
             */
            public static void main(String[] args) {
                System.out.println(diff("this is a example", "this is a examp")); // prints (le,)
                System.out.println(diff("Honda", "Hyundai")); // prints (o,yui)
                System.out.println(diff("Toyota", "Coyote")); // prints (Ta,Ce)
                System.out.println(diff("Flomax", "Volmax")); // prints (Fo,Vo)
            }
        
            /**
             * Returns a minimal set of characters that have to be removed from (or added to) the respective
             * strings to make the strings equal.
             */
            public static Pair<String> diff(String a, String b) {
                return diffHelper(a, b, new HashMap<>());
            }
        
            /**
             * Recursively compute a minimal set of characters while remembering already computed substrings.
             * Runs in O(n^2).
             */
            private static Pair<String> diffHelper(String a, String b, Map<Long, Pair<String>> lookup) {
                long key = ((long) a.length()) << 32 | b.length();
                if (!lookup.containsKey(key)) {
                    Pair<String> value;
                    if (a.isEmpty() || b.isEmpty()) {
                        value = new Pair<>(a, b);
                    } else if (a.charAt(0) == b.charAt(0)) {
                        value = diffHelper(a.substring(1), b.substring(1), lookup);
                    } else {
                        Pair<String> aa = diffHelper(a.substring(1), b, lookup);
                        Pair<String> bb = diffHelper(a, b.substring(1), lookup);
                        if (aa.first.length() + aa.second.length() < bb.first.length() + bb.second.length()) {
                            value = new Pair<>(a.charAt(0) + aa.first, aa.second);
                        } else {
                            value = new Pair<>(bb.first, b.charAt(0) + bb.second);
                        }
                    }
                    lookup.put(key, value);
                }
                return lookup.get(key);
            }
        
            public static class Pair<T> {
                public Pair(T first, T second) {
                    this.first = first;
                    this.second = second;
                }
        
                public final T first, second;
        
                public String toString() {
                    return "(" + first + "," + second + ")";
                }
            }
        }
        

        【讨论】:

        • 这完美解决了我的拼写检查问题!这是最通用的解决方案。
        • 如果您需要知道 哪里 不匹配的字符位于:可以轻松修改此代码以返回您喜欢的任何类型的结果,而不是一对string -- 只需更改返回结果,IDE 将突出显示您接下来要做的事情。这里有一些关于该方法的理论:dzone.com/articles/the-levenshtein-algorithm-1
        【解决方案5】:

        要直接只获取更改的部分,而不仅仅是结尾,可以使用谷歌的Diff Match Patch

        List<Diff> diffs = new DiffMatchPatch().diffMain("stringend", "stringdiffend");
        for (Diff diff : diffs) {
          if (diff.operation == Operation.INSERT) {
            return diff.text; // Return only single diff, can also find multiple based on use case
          }
        }
        

        对于 Android,添加:implementation 'org.bitbucket.cowwoc:diff-match-patch:1.2'

        这个包比这个功能强大得多,主要用于创建diff相关的工具。

        【讨论】:

          【解决方案6】:
          String strDiffChop(String s1, String s2) {
              if (s1.length > s2.length) {
                  return s1.substring(s2.length - 1);
              } else if (s2.length > s1.length) {
                  return s2.substring(s1.length - 1);
              } else {
                  return null;
              }
          }
          

          【讨论】:

          • 如果diff不在字符串后面而在中间怎么办?
          • @brunoais 如果您需要找到中间字符串的差异,请参阅 JRL 的答案。根据对问题的不同解释,这是一个更简单的答案。
          【解决方案7】:

          Google 的 Diff Match Patch 很好,但安装到我的 Java maven 项目中很痛苦。只是添加一个 Maven 依赖项是行不通的; eclipse 刚刚创建了目录并添加了 lastUpdated 信息文件。最后,在第三次尝试时,我在我的 pom 中添加了以下内容:

          <dependency>
              <groupId>fun.mike</groupId>
               <artifactId>diff-match-patch</artifactId>
              <version>0.0.2</version>
          </dependency>
          

          然后我手动将 jar 和源 jar 文件从https://search.maven.org/search?q=g:fun.mike%20AND%20a:diff-match-patch%20AND%20v:0.0.2 放入我的 .m2 存储库中

          毕竟,下面的代码起作用了:

          import fun.mike.dmp.Diff;
          import fun.mike.dmp.DiffMatchPatch;
          
          DiffMatchPatch dmp = new DiffMatchPatch();
          LinkedList<Diff> diffs = dmp.diff_main("Hello World.", "Goodbye World.");
          System.out.println(diffs);
          

          结果:

          [Diff(DELETE,"Hell"), Diff(INSERT,"G"), Diff(EQUAL,"o"), Diff(INSERT,"odbye"), Diff(EQUAL," World.")]
          

          显然,这最初并不是编写(甚至完全移植)到 Java 中的。 (diff_main?我能感觉到C在我的眼睛里燃烧:-)) 尽管如此,它仍然有效。对于处理长而复杂的字符串的人来说,它可能是一个有价值的工具。

          【讨论】:

            【解决方案8】:

            要查找两行中不同的单词,可以使用以下代码。

                String[] strList1 = str1.split(" ");
                String[] strList2 = str2.split(" ");
            
                List<String> list1 = Arrays.asList(strList1);
                List<String> list2 = Arrays.asList(strList2);
            
                // Prepare a union
                List<String> union = new ArrayList<>(list1);
                union.addAll(list2);
            
                // Prepare an intersection
                List<String> intersection = new ArrayList<>(list1);
                intersection.retainAll(list2);
            
                // Subtract the intersection from the union
                union.removeAll(intersection);
            
                for (String s : union) {
                    System.out.println(s);
                }
            

            最后,您将获得一个列表,其中包含两个列表中不同的单词。人们可以轻松地对其进行修改,以便在第一个列表或第二个列表中简单地使用不同的单词,而不是同时使用。这可以通过仅从 list1 或 list2 而不是并集中删除交集来完成。

            可以通过将拆分列表中每个单词的长度相加(以及拆分正则表达式)或简单地执行 String.indexOf("subStr") 来计算确切位置。

            【讨论】:

            • 那么“这是一个测试”和“这是一个测试”是否相等?
            • 是的。然而,操作似乎只对字符的差异而不是顺序感兴趣。一旦你有了不同的单词,就可以在字符级别以类似的方式比较它们以找到额外的字符......
            • Google 的 Diff Match Patch 很好,但在我的 maven 项目中使用起来很痛苦。仅添加依赖项不起作用。最后,在我的 pom 中,我添加了以下内容:
            【解决方案9】:

            另一个用于发现字符串之间差异的优秀库是位于https://github.com/java-diff-utils 的 DiffUtils。我使用了 Dmitry Naumenko 的叉子:

            public void testDiffChange() {
                final List<String> changeTestFrom = Arrays.asList("aaa", "bbb", "ccc");
                final List<String> changeTestTo = Arrays.asList("aaa", "zzz", "ccc");
                System.out.println("changeTestFrom=" + changeTestFrom);
                System.out.println("changeTestTo=" + changeTestTo);
                final Patch<String> patch0 = DiffUtils.diff(changeTestFrom, changeTestTo);
                System.out.println("patch=" + Arrays.toString(patch0.getDeltas().toArray()));
            
                String original = "abcdefghijk";
                String badCopy =  "abmdefghink";
                List<Character> originalList = original
                        .chars() // Convert to an IntStream
                        .mapToObj(i -> (char) i) // Convert int to char, which gets boxed to Character
                        .collect(Collectors.toList()); // Collect in a List<Character>
                List<Character> badCopyList = badCopy.chars().mapToObj(i -> (char) i).collect(Collectors.toList());
                System.out.println("original=" + original);
                System.out.println("badCopy=" + badCopy);
                final Patch<Character> patch = DiffUtils.diff(originalList, badCopyList);
                System.out.println("patch=" + Arrays.toString(patch.getDeltas().toArray()));
            }
            

            结果准确地显示了发生了什么变化(从零开始计数):

            changeTestFrom=[aaa, bbb, ccc]
            changeTestTo=[aaa, zzz, ccc]
            patch=[[ChangeDelta, position: 1, lines: [bbb] to [zzz]]]
            original=abcdefghijk
            badCopy=abmdefghink
            patch=[[ChangeDelta, position: 2, lines: [c] to [m]], [ChangeDelta, position: 9, lines: [j] to [n]]]
            

            【讨论】:

              【解决方案10】:

              对于像这样的简单用例。您可以检查字符串的大小并使用拆分功能。以你为例

              a.split(b)[1]
              

              【讨论】:

                【解决方案11】:

                除了在其他答案中看到的使用StringUtils.difference(String first, String second) 之外,您还可以使用StringUtils.indexOfDifference(String first, String second) 来获取字符串开始不同处的索引。例如:

                StringUtils.indexOfDifference("abc", "dabc") = 0
                StringUtils.indexOfDifference("abc", "abcd") = 3
                

                其中 0 用作起始索引。

                【讨论】:

                  【解决方案12】:

                  你可以试试这个

                  String a = "this is a example";
                  String b = "this is a examp";
                  
                  String ans= a.replace(b, "");
                  
                  System.out.print(now);      
                  //ans=le
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-10-08
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-03-10
                    相关资源
                    最近更新 更多