Найдите разницу между двумя строками
Предположим, что у меня две длинные строки. Они почти такие же.
String a = "this is a example"
String b = "this is a examp"
Выше код - это только пример. Фактические строки довольно длинные.
Проблема состоит в том, что одна строка имеет еще 2 символа, чем другая.
Как я могу проверить, какие эти два символа?
Ответы
Ответ 1
Вы можете использовать StringUtils.difference (первая строка, вторая строка).
Вот как они это реализовали:
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;
}
Ответ 2
Чтобы найти разницу между двумя строками, вы можете использовать класс StringUtils и метод разница. Он сравнивает две строки и возвращает часть, в которой они отличаются.
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"
Смотрите: https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/StringUtils.html
Ответ 3
Без итерации по строкам вы можете только знать, что они разные, а не где - и только в том случае, если они имеют разную длину. Если вам действительно нужно знать, что представляют собой разные символы, вы должны пройти через обе строки в тандеме и сравнить символы в соответствующих местах.
Ответ 4
Следующий фрагмент кода Java эффективно вычисляет минимальный набор символов, которые должны быть удалены (или добавлены) из соответствующих строк, чтобы сделать строки равными. Это пример динамического программирования.
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 + ")";
}
}
}
Ответ 5
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;
}
}
Ответ 6
Чтобы найти слова, которые отличаются в двух строках, можно использовать следующий код.
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);
}
В конце концов, у вас будет список слов, которые отличаются в обоих списках. Можно легко изменить его, чтобы просто иметь разные слова в первом списке или во втором списке, а не одновременно. Это можно сделать, удалив перекресток только из списка1 или list2 вместо объединения.
Вычисление точного местоположения может быть выполнено путем суммирования длин каждого слова в списке разбиения (вместе с разделяющим регулярным выражением) или просто с помощью String.indexOf( "subStr" ).
Ответ 7
Чтобы получить только измененный раздел, а не только его конец, вы можете использовать Google 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.
Ответ 8
Еще одна замечательная библиотека для обнаружения различий между строками - это DiffUtils в https://github.com/java-diff-utils. Я использовал вилку Дмитрия Науменко:
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]]]
Ответ 9
Google Diff Match Patch - это хорошо, но установить его в мой проект Java maven было непросто. Простое добавление зависимости maven не сработало; Eclipse только что создал каталог и добавил последние информационные файлы. Наконец, с третьей попытки я добавил в свой pom следующее:
<dependency>
<groupId>fun.mike</groupId>
<artifactId>diff-match-patch</artifactId>
<version>0.0.2</version>
</dependency>
Затем я вручную поместил файлы jar и jar исходного кода в репозиторий .m2 из https://search.maven.org/search?q=g:fun.mike%20AND%20a:diff-match-patch%20AND%20v:0.0.2
После этого сработал следующий код:
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? Я чувствую, как С пылает в моих глазах :-))
Тем не менее, это работает. А для людей, работающих с длинными и сложными струнами, это может быть ценным инструментом.