uniqueSortedDiff.html

 リリース対象ファイル名リストと、リリース先への展開結果の
ファイル名一覧を照合し、間違いないことを確認する手間を少し
楽にするツールを作りましたのでご紹介いたします。

 もしよろしかったら、使ってみてください。

【ツールの機能】
 ・2組の文字列のリストを相互に比較します。
 ・リストの要素(各行)が同等であることがよくわかるように表示します。
 ・同等性の判定では、重複を無視し、順序も無視して、集合としてすべての要
素が一致するかどうかで決定します。
 ・空白のみからなる行は無視します。
 ・末尾の空白の違いは無視します。
 ・HTMLとCSSでレイアウトしているので、文字サイズや色はかんたんに変更で
きます。

【使い方】
 (1)このメールに添付しているHTMLファイルをブラウザで開いてください。
    (JavaScript機能を有効にしてください。)
      動作確認済のブラウザは次のとおりです。
       Microsoft Internet Explorer 6.0 SP2
       Mozilla FireFox 1.0.4

 (2)左右ふたつのテキストエリアにそれぞれ、
    リリース対象ファイル名のリストと、
    リリース済ファイル名のリストを貼り付けます。

 (3)ボタンをクリックします。

 色分けして差分(があれば)表示します。
 ・すべて緑色に表示されていれば、一致しています。
 ・左右の片方が赤く、他方が黒く表示されている行が表示されるときは、
  赤い表示のファイルが他方に含まれていません。

【製作の背景】
CGIライブラリ管理作業としてリリース等を行う場合、
リリース対象ファイルの一覧と同等(もれなく、余計な追加なく、数学的な集合
として一致する)であることを確認する手順があります。

 従来、両方のファイル名のリストを一枚のExcelシート上にそれぞれ一列
(計2列)に貼り付けてから、さらにもうひとつの列に検証用の計算式(VLOOKUP
関数)を入力した上、検証対象の列と同じ行数分ドラッグし、計算式の結果の
表示が検証対象と同じファイル名文字列として表示されていればOK、
そうでなく、「FALSE」と表示されればNG、という判断をしていました。

 毎回このようないくつもの手順を踏むのは煩雑ですし、間違える可能性も
はらみます。時間もそれなりにかかります。また、表示結果もパッと見た
だけではわかりにくく、正確に読み取るには注意を要しました。

 今回のツールは、こうした不便さと危うさを解消することを目指して
作りました。

 HTMLを採用したため色や文字サイズを使った多彩な出力表現ができる
ことと、ブラウザへの貼り付けとボタンクリックだけで済むという操作の
かんたんさを提供できるようになりました。

====ソースコード(HTML)====

<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=Shift_JIS" />
<title>二組のリストが一致することを確認</title>
<style type="text/css">
body         {background-color:#eeeeff; line-height:1.2em;}
h1           {font-family: "MS 明朝"; text-align:center;}
label        {font-family: "MS ゴシック"; font-weight:bold;}
textarea     {font-size:9pt; background-color:#ffffff;}
span.caution {color:#ff0000;}
table.diff   {background-color:#333333;}
.same        {color:#333333; background-color:#99ffcc;}
.only        {color:#cccccc; background-color:#ff3333;}
.miss        {color:#cccccc; background-color:#333333;}
</style>
</head>
<body>
<h1>二組のリストの一致確認</h1>
<div id="output">
</div>
<blockquote>
<pre>
	<strong>【凡例】</strong>
	<span class="same"> x </span>:両方に同じ値<i>x</i>がある  <span class="only"> x </span>:こちらにだけ行の値<i>x</i>がある  <span class="miss">   </span>:こちらには行の値がない
</pre>
</blockquote>
<form name="ui">
<label for="input">
下の二つのテキストエリアに比較する一対のリストをそれぞれ貼り付けてから「一致を確認する」ボタンを押してください。<br />
<span class="caution">二組の行のリストが順番、重複、末尾の空白を除いて一致することを確認します。<br />
空白だけの行(空行)は無視します。<br /></span>
</label><br />
<input type="button" value="一致を確認する" onClick="javascript:checkDiff(ui.input1.value, ui.input2.value);" /><br />
<table>
<tr><td><textarea name="input1" cols="60" rows="30"></textarea></td><td><textarea name="input2" cols="60" rows="30"></textarea></td></tr>
</table>
</form>
</body>
</html>
<script language="JavaScript">
/** テキストエリア内のテキストを解析し、わかりやすい表示内容のHTMLをid属性がoutputである要素に設定します。 */
function checkDiff(list1, list2) {
	var table = makeTable(noEmptyLine(unique(split(list1, "\n").sort())), noEmptyLine(unique(split(list2, "\n").sort())));
	var html = "<center>" + table + "</center>";
	document.all.item("output").innerHTML = html;
}

/** 与えられた2組の配列の集合としての比較に基づいて差異を確認するためのHTML table 要素の文字列を生成します。 */
function makeTable(array1, array2) {
	var referenceList = noEmptyLine(unique(array1.concat(array2).sort()));
	var tableHtml = new Array();
	var length = referenceList.length;

	tableHtml.push("<table class=\"diff\">");
	var i = 0;
	var html = "";
	for (i = 0; i < length; i++) {
		var v = referenceList[i];
		var left  = contains(array1, v) ? v : "";
		var right = contains(array2, v) ? v : "";
		if      (left != "" && right != "") {html = sameTd(left) + sameTd(right);}
		else if (left != "" && right == "") {html = onlyTd(left) + missTd(right);}
		else if (left == "" && right != "") {html = missTd(left) + onlyTd(right);}
		else if (left == "" && right == "") {html = missTd(left) + missTd(right);}
		tableHtml.push("<tr>" + html + "</tr>");
	}
	tableHtml.push("</table>");
	return tableHtml.join("\n");
}

/** 与えられた値 value を特定のCSSクラス名を指定したHTML td 要素で包みます。 */
function htmlTd(cssClass, value) {return "<td class=\"" + cssClass + "\">" + escapeHtml(value) + "</td>";}

/** 双方ともに存在する値のために、与えられた値 value をCSSクラス名 same を設定した td 要素で包みます。 */
function sameTd(value) {return htmlTd("same", value);}

/** 片方にだけ存在する値のために、与えられた値 value をCSSクラス名 only を設定した td 要素で包みます。 */
function onlyTd(value) {return htmlTd("only", value);}

/** 一方には存在しない値のために、与えられた値 value をCSSクラス名 miss を設定した td 要素で包みます。 */
function missTd(value) {return htmlTd("miss", value);}

/** HTML中に含まれるとそのまま表示されない文字(「&」、「<」、「>」)を文字実体参照で置換します。 */
function escapeHtml(value) {
	var escaped = new Array();
	for (i = 0; i < value.length; i++) {
		var v = value.charCodeAt(i);
		if      (v == "&".charCodeAt(0)) {escaped.push("&amp;");}
		else if (v == "<".charCodeAt(0)) {escaped.push("&lt;");}
		else if (v == ">".charCodeAt(0)) {escaped.push("&gt;");}
		else                             {escaped.push(String.fromCharCode(v));};
	}
	return escaped.join("");
}

/**
 * 与えられた配列 array の要素群のなかに値 value が含まれる場合にのみ true を返します。
 * そうでなければ false を返します。
 */
function contains(array, value) {
	var result = false;
	for (i = 0; i < array.length; i++) {
		if (array[i] == value) {result = true;}
	}
	return result;
}

/** 空白のみ、もしくは空の要素を取り除いた結果の配列を返します。*/
function noEmptyLine(array) {
	var result = new Array();
	for (i = 0; i < array.length; i++) {
		var v = array[i];
		if (trim(v) != "") {result.push(v); previous = v;}
	}
	return result;
}

/**
 * 重複する要素を取り除いた結果の配列を返します。
 * ただし、配列はあらかじめソート済(重複する要素は連続して出現する)と想定します。
 */
function unique(array) {
	var result = new Array();
	var previous = undefined;
	for (i = 0; i < array.length; i++) {
		var v = array[i];
		if (v != previous) {result.push(v); previous = v;}
	}
	return result;
}

/** 文字列の先頭と末尾の空白類を削除します。 */
function trim(s) {
	return s.replace(/^\s*/, "").replace(/\s*$/, "");
}

/** 文字列の末尾の空白類を削除します。 */
function chomp(s) {
	return s.replace(/\s*$/, "");
}

/** 与えられた文字列 text を指定された区切り文字列 delim で区切った結果を要素とする文字列の配列を返します。 */
function split(text, delim) {
	var splitted = text.split(delim);

	//IE6(SP2)では String.split 関数の結果に区切り文字がついてくるので削除します。
	var result = new Array();
	for (i = 0; i < splitted.length; ++i) {
		result.push(chomp(splitted[i]));
	}
	return result;
}
</script>