是否有更有效的方法来实现这种类似差异的功能?

时间:2016-05-09 05:06:34

标签: c# .net diff

我有一个C#函数,可以逐行比较两个字符串oldStringnewString。每个比较仅在引号内考虑案例。例如,这两行是相同的:

If Foo Then: Debug.Print "Hello World!"
If foo Then: Debug.print "Hello World!"

这两个是不平等的:

If Foo Then: Debug.Print "Hello World!"
If Foo Then: Debug.Print "hello world!"

该函数返回一个由newString构造的新文件,但是如果行没有变化,则使用来自oldString的行。所以如果这是oldString

If Foo Then
    Debug.Print "Hello World!"
End If
Debug.Print "This line will be deleted soon."

这是newString

If foo Then
    Debug.print "Hello World!"
    Debug.print "This is a new line."
End If

然后该函数返回:

If Foo Then
    Debug.Print "Hello World!"
    Debug.print "This is a new line."
End If

目前我正在使用基于动态编程方法的代码来解决最常见的子串问题,并进行了一些简单的优化:

public static string FixCase(string oldString, string newString) {
    if (string.Equals(oldString, newString, StringComparison.InvariantCulture)) {
        return newString;
    }

    var oldLines = oldString.Split(new[] { "\r\n" }, StringSplitOptions.None).ToList();
    var newLines = newString.Split(new[] { "\r\n" }, StringSplitOptions.None).ToList();

    var headEqualLines = new List<string>();
    var minLineCount = oldLines.Count < newLines.Count ? oldLines.Count : newLines.Count;
    for (var i = 0; i < minLineCount; i++) {
        if (VbaLineEqual(oldLines[i], newLines[i])) {
            headEqualLines.Add(oldLines[i]);
        } else {
            break;
        }
    }
    oldLines.RemoveRange(0, headEqualLines.Count);
    newLines.RemoveRange(0, headEqualLines.Count);
    minLineCount -= headEqualLines.Count;
    var tailEqualLines = new List<string>();
    if (oldLines.Count > 0 && newLines.Count > 0) {
        for (var i = 1; i <= minLineCount; i++) {
            if (VbaLineEqual(oldLines[oldLines.Count - i], newLines[newLines.Count - i])) {
                tailEqualLines.Add(oldLines[oldLines.Count - i]);
            } else {
                break;
            }
        }
        tailEqualLines.Reverse();
        oldLines.RemoveRange(oldLines.Count - tailEqualLines.Count - 1, tailEqualLines.Count);
        newLines.RemoveRange(newLines.Count - tailEqualLines.Count - 1, tailEqualLines.Count);
    }

    var lengths = new int[oldLines.Count + 1, newLines.Count + 1];
    for (var i = 0; i < oldLines.Count; i++) {
        for (var j = 0; j < newLines.Count; j++) {
            if (VbaLineEqual(oldLines[i], newLines[j]))
                lengths[i + 1, j + 1] = lengths[i, j] + 1;
            else
                lengths[i + 1, j + 1] = lengths[i + 1, j] > lengths[i, j + 1] ? lengths[i + 1, j] : lengths[i, j + 1];
        }
    }
    var result = new List<string>();
    var x = oldLines.Count;
    var y = newLines.Count;
    while (x != 0 && y != 0) {
        if (lengths[x, y] == lengths[x - 1, y]) {
            x--;
        } else if (lengths[x, y] == lengths[x, y - 1]) {
            result.Add(newLines[y - 1]);
            y--;
        } else {
            result.Add(oldLines[x - 1]);
            x--;
            y--;
        }
    }
    while (y != 0) {
        result.Add(newLines[y - 1]);
        y--;
    }
    result.Reverse();
    return string.Join("\r\n", headEqualLines.Concat(result).Concat(tailEqualLines));
}

static bool VbaLineEqual(string oldLine, string newLine) {
    if (string.Equals(oldLine, newLine, StringComparison.InvariantCulture))
        return true;
    if (!string.Equals(oldLine, newLine, StringComparison.InvariantCultureIgnoreCase))
        return false;
    var quoting = false;
    for (var i = 0; i < oldLine.Length; i++) {
        if (quoting && oldLine[i] != newLine[i])
            return false;
        if (oldLine[i] == '"')
            quoting = !quoting;
    }
    return true;
}

但是当我针对一对5000行左右的字符串运行并且在开头和结尾附近进行更改时,性能和内存使用都非常可怕,可能是因为分配了5,000×5,000 int的数组

是否有任何可以处理此比较的预构建库,或者我可以使用的更有效的算法?请注意,我想在zlib许可下许可我的代码,因此GNU diffutils并不好。

1 个答案:

答案 0 :(得分:0)

Google Diff Match and Patch library可以为您解决此问题。