在Java中生成格式化的diff输出

时间:2008-11-26 01:25:27

标签: java diff

是否有任何Java库可以接受两个字符串,并根据* nix diff命令返回带格式化输出的字符串?

e.g。喂入

test 1,2,3,4
test 5,6,7,8
test 9,10,11,12
test 13,14,15,16

test 1,2,3,4
test 5,6,7,8
test 9,10,11,12,13
test 13,14,15,16

作为输入,它会给你

test 1,2,3,4                                                    test 1,2,3,4
test 5,6,7,8                                                    test 5,6,7,8
test 9,10,11,12                                               | test 9,10,11,12,13
test 13,14,15,16                                                test 13,14,15,16

就像我将文件传递给diff -y expected actual

一样

我发现了this question,它为一般库提供了一些很好的建议,可以为你提供编程输出,但我想要直接的字符串结果。

我可以直接调用diff作为系统调用,但是这个特定的应用程序将在unix和windows上运行,我无法确定环境实际上是否有diff可用。

5 个答案:

答案 0 :(得分:14)

java-diff-utils

  

用于计算的DiffUtils库   差异,应用补丁,generationg   Java中的并排视图

     

Diff Utils库是一个OpenSource   用于执行比较的库   文本之间的操作:计算   差异,应用补丁,生成   统一差异或解析它们,   生成差异输出以方便将来   显示(如并排视图)   等等。

     

构建此库的主要原因是   缺乏易于使用的库   你需要的所有常用的东西   使用diff文件。原来它   受到了JRCS图书馆的启发   漂亮的diff模块设计。

     

主要功能

     
      
  • 计算两个文本之间的差异。
  •   
  • 能够提供比普通ascci更多的东西。数组或任何类型的列表   实现hashCode()和equals()   正确的可以受到   差异化使用此库
  •   
  • 使用给定的补丁修补和取消文本
  •   
  • 解析统一差异格式
  •   
  • 产生人类可读的差异
  •   

答案 1 :(得分:6)

我最后自己动手了。不确定它是否是最好的实现,并且它很丑陋,但它会通过测试输入。

它使用java-diff进行重度差异提升(任何apache公共StrBuilder和StringUtils而不是库存Java StringBuilder)

public static String diffSideBySide(String fromStr, String toStr){
    // this is equivalent of running unix diff -y command
    // not pretty, but it works. Feel free to refactor against unit test.
    String[] fromLines = fromStr.split("\n");
    String[] toLines = toStr.split("\n");
    List<Difference> diffs = (new Diff(fromLines, toLines)).diff();

    int padding = 3;
    int maxStrWidth = Math.max(maxLength(fromLines), maxLength(toLines)) + padding;

    StrBuilder diffOut = new StrBuilder();
    diffOut.setNewLineText("\n");
    int fromLineNum = 0;
    int toLineNum = 0;
    for(Difference diff : diffs) {
        int delStart = diff.getDeletedStart();
        int delEnd = diff.getDeletedEnd();
        int addStart = diff.getAddedStart();
        int addEnd = diff.getAddedEnd();

        boolean isAdd = (delEnd == Difference.NONE && addEnd != Difference.NONE);
        boolean isDel = (addEnd == Difference.NONE && delEnd != Difference.NONE);
        boolean isMod = (delEnd != Difference.NONE && addEnd != Difference.NONE);

        //write out unchanged lines between diffs
        while(true) {
            String left = "";
            String right = "";
            if (fromLineNum < (delStart)){
                left = fromLines[fromLineNum];
                fromLineNum++;
            }
            if (toLineNum < (addStart)) {
                right = toLines[toLineNum];
                toLineNum++;
            }
            diffOut.append(StringUtils.rightPad(left, maxStrWidth));
            diffOut.append("  "); // no operator to display
            diffOut.appendln(right);

            if( (fromLineNum == (delStart)) && (toLineNum == (addStart))) {
                break;
            }
        }

        if (isDel) {
            //write out a deletion
            for(int i=delStart; i <= delEnd; i++) {
                diffOut.append(StringUtils.rightPad(fromLines[i], maxStrWidth));
                diffOut.appendln("<");
            }
            fromLineNum = delEnd + 1;
        } else if (isAdd) {
            //write out an addition
            for(int i=addStart; i <= addEnd; i++) {
                diffOut.append(StringUtils.rightPad("", maxStrWidth));
                diffOut.append("> ");
                diffOut.appendln(toLines[i]);
            }
            toLineNum = addEnd + 1; 
        } else if (isMod) {
            // write out a modification
            while(true){
                String left = "";
                String right = "";
                if (fromLineNum <= (delEnd)){
                    left = fromLines[fromLineNum];
                    fromLineNum++;
                }
                if (toLineNum <= (addEnd)) {
                    right = toLines[toLineNum];
                    toLineNum++;
                }
                diffOut.append(StringUtils.rightPad(left, maxStrWidth));
                diffOut.append("| ");
                diffOut.appendln(right);

                if( (fromLineNum > (delEnd)) && (toLineNum > (addEnd))) {
                    break;
                }
            }
        }

    }

    //we've finished displaying the diffs, now we just need to run out all the remaining unchanged lines
    while(true) {
        String left = "";
        String right = "";
        if (fromLineNum < (fromLines.length)){
            left = fromLines[fromLineNum];
            fromLineNum++;
        }
        if (toLineNum < (toLines.length)) {
            right = toLines[toLineNum];
            toLineNum++;
        }
        diffOut.append(StringUtils.rightPad(left, maxStrWidth));
        diffOut.append("  "); // no operator to display
        diffOut.appendln(right);

        if( (fromLineNum == (fromLines.length)) && (toLineNum == (toLines.length))) {
            break;
        }
    }

    return diffOut.toString();
}

private static int maxLength(String[] fromLines) {
    int maxLength = 0;

    for (int i = 0; i < fromLines.length; i++) {
        if (fromLines[i].length() > maxLength) {
            maxLength = fromLines[i].length();
        }
    }
    return maxLength;
}

答案 2 :(得分:0)

Busybox有一个非常精简的差异实现,不应该很难转换为java,但你必须添加两列功能。

答案 3 :(得分:0)

http://c2.com/cgi/wiki?DiffAlgorithm我在Google上发现了这一点,它提供了一些很好的背景和链接。如果你关心的算法不仅仅是做项目,还有一本关于动态编程的基本算法的书或者只是一本书。算法知识总是很好:)

答案 4 :(得分:0)

您可以使用Apache Commons Text库来实现此目的。该库基于“ Eugene W. Myers的非常高效的算法”提供“ diff”功能。

这使您能够创建自己的访问者,以便您可以按所需的方式处理差异,并可以将其输出到控制台或HTML等。这是一篇使用Apache Commons Text遍历nice & simple example to output side by side diff in HTML format的文章库和简单的Java代码。