[program] diffのアルゴリズム
Java5で
import java.util.*; import java.io.*; public class Diff { private String oldText; private String newText; private ArrayListmatchList = new ArrayList (); public Diff(String oldText, String newText) { this.newText = newText; this.oldText = oldText; this.doCalc(); } public String diffText() { ArrayList result = new ArrayList ) result.toArray(new String[result.size()]); } public MatchedLineNo machedLineNoPair() { return (MatchedLineNo) matchList.toArray(new MatchedLineNo[matchList.size()]); } private void doCalc() { int oldLineNo = 0; int newLineNo = 0; while (oldLineNo < oldText.length && newLineNo < newText.length) { // collection matched lines if (lineEquals(oldText[oldLineNo], newText[newLineNo])) { matchList.add(new MatchedLineNo(oldLineNo, newLineNo)); //System.out.println("[matched] " + oldLineNo + ":" + newLineNo); oldLineNo++; newLineNo++; continue; } // find next matching line: cursor down each other boolean found = false; boolean cursorOldLine = true; int oldLineNoSearch = oldLineNo; int newLineNoSearch = newLineNo; while (oldLineNoSearch < oldText.length || newLineNoSearch < newText.length) { if (cursorOldLine) { oldLineNoSearch++; if (oldLineNoSearch < oldText.length) { if (lineEquals(oldText[oldLineNoSearch], newText[newLineNo])) { oldLineNo = oldLineNoSearch; found = true; break; } } } else { newLineNoSearch++; if (newLineNoSearch < newText.length) { if (lineEquals(oldText[oldLineNo], newText[newLineNoSearch])) { newLineNo = newLineNoSearch; found = true; break; } } } cursorOldLine = !cursorOldLine; } if (!found) { oldLineNo++; newLineNo++; } } } private boolean lineEquals(String oldLine, String newLine) { return oldLine != null ? oldLine.equals(newLine) : newLine == null; } // matched line pair container public static class MatchedLineNo { private int oldLineNo; private int newLineNo; public MatchedLineNo(int oldLineNo, int newLineNo) { this.newLineNo = newLineNo; this.oldLineNo = oldLineNo; } public int getOldLineNo() { return this.oldLineNo; } public int getNewLineNo() { return this.newLineNo; } } // test command line public static void main(String args) throws Exception { if (args.length != 2) { System.out.println("usage: java Diff oldFilename newFilename"); } String oldText = readFile(args[0]); String newText = readFile(args[1]); Diff diff = new Diff(oldText, newText); String diffTexts = diff.diffText(); for (String line: diffTexts) { System.out.println(line); } } private static String readFile(String fileName) throws Exception { BufferedReader reader = new BufferedReader(new FileReader(fileName)); ArrayList(); int oldLineNo = 0; int newLineNo = 0; for (MatchedLineNo matchedLineNo: matchList) { while (oldLineNo < matchedLineNo.getOldLineNo() && oldLineNo < oldText.length) { result.add("- " + oldText[oldLineNo]); oldLineNo++; } while (newLineNo < matchedLineNo.getNewLineNo() && newLineNo < newText.length) { result.add("+ " + newText[newLineNo]); newLineNo++; } if (oldLineNo >= oldText.length) break; result.add("= " + oldText[matchedLineNo.getOldLineNo()]); oldLineNo++; newLineNo++; } return (String result = new ArrayList ) result.toArray(new String[result.size()]); } }(); for (String line = reader.readLine(); line != null; line = reader.readLine()) { result.add(line); } return (String
ポイントはdiffとは「なにを見つける」処理なのか。新旧テキストからdiffで探すのは「同じ行内容の行番号のペア」のリスト。それが気が付けばあとは自明。
doCalc()メソッドでは以下のことを行い、行番号ペアを集めています:
- 1. 現在行どおしが同じ行内容となるペアを逐次貯めていく。
- 2. 違う行にあたったら、同じ行内容を探す
- 2.1. 片方を固定(現在行)したまま、片方を一時行として一時的に下げてみる。
- 2.2. 一時行下げを交互に行い、先に同じ行内容が見つかったほうに現在行をあわせて1へ継続。
- 2.3. 双方の一時行が末端まできて、見つからなければ双方の現在行を下げる。
- 3. 現在行がどちらかが末端に来たら終了。
簡単ですね。もちろん行番号を一旦集めなくても、ループ中に直接出力することも可能です。