KEN_ALL.CSVをEmEditorでゴニョゴニョしてみる

##機能
- KEN_ALL.CSV（日本郵便で公開されている、郵便番号データ）を元に、町域名（半角カタカナ/漢字）部分をスッキリさせる（複数行にわたる括弧部分を削除とか、"以下を除く"等を削除）
- 変換後のファイルを出力する（必要な列のみ出力するようにすることも簡単な修正で可能）

##実行環境
-Windows10
-EmEditor Professional (64-bit)　Version 19.8.5 にて確認
##使い方
1. 日本郵便の郵便番号データダウンロードページで公開されている郵便番号データ（全国一括）を取得し、"KEN_ALL.CSV"ファイルを準備する(*1)
1. EmEditorで"KEN_ALL.CSV"ファイルを開き、下記マクロを実行する。
1. KEN_ALL.CSVと同じディレクトリにKEN_ALL_cnv.txtファイルが作成される
1. (KEN_ALL_LOG.txtファイルも出力される)

(*1)「読み仮名データの促音・拗音を小書きで表記しないもの」と「読み仮名データの促音・拗音を小書きで表記するもの」と２種類になっているが、"・・・小書きで表記するもの"でテストしています。変換処理の正規表現を変えることにより、"・・・小書きで表記しないもの"でも使えると思われます
##マクロ

```javascript:郵便番号データ変換_v2.jsee
var startTime = new Date();
/*
□マクロ名：郵便番号データ変換.jsee
□バージョン：2.0
　2020 05 16 クラスで実装
　
■開くファイル
　郵便番号データダウンロードから取得した住所の郵便番号（CSV形式）
　(ken_all.zipを展開したKEN_ALL.CSV。シフトJIS、改行コードは0x0D,0x0A)
　https://www.post.japanpost.jp/zipcode/dl/kogaki-zip.html
■作成ファイル
　レイアウト同じで町域名の内容を簡略化したファイル（ファイル名=拡張子の前に"_cnv"を追加したテキストファイル）
■手順(例)
　KEN_ALL.CSVを開く
　郵便番号データ変換_v2.jseeを実行
　同一ディレクトリに変換後のファイル(UTF-8)が作成される
*/

//■■■■■■■■■■■■■■■■■■■■■■■■■■■
//クラス定義
//■■■■■■■■■■■■■■■■■■■■■■■■■■■

//■■■■■■■■■■■■■■■■■■■■■■■■■■■
//■■　ログ情報クラス
//■■■■■■■■■■■■■■■■■■■■■■■■■■■
//■■■■■■■■■■■■■■■■■■■■■■■■■■■
//【名称】ログ情報：コンストラクタ
//【引数】なし
//【返却】なし
//【処理】ログ情報を管理する配列を作成する
//■■■■■■■■■■■■■■■■■■■■■■■■■■■
Log = function(){
	OutputBar.writeln("○Log.コンストラクタ(ログ情報:start)");
	//コンストラクタ
	this.arrLog = [];
	OutputBar.writeln("○Log.コンストラクタ(ログ情報:end)");
};

//■■■■■■■■■■■■■■■■■■■■■■■■■■■
//【名称】ログ情報：追加
//【引数】ログ種類、郵便番号、ログ内容
//【返却】なし
//【処理】this.arrLogにログ情報を追加する
//■■■■■■■■■■■■■■■■■■■■■■■■■■■
Log.prototype.push = function(
	logType,//ログ種別
	postalCode,//郵便番号
	logContent//ログ内容
	){
//	OutputBar.writeln("○Log.push(ログ情報.追加:start)");
	tmpArr = [];
	tmpArr[0] = logType;//ログ種別
	tmpArr[1] = postalCode;//郵便番号
	tmpArr[2] = logContent;//ログ内容
	this.arrLog.push(tmpArr);
//	OutputBar.writeln("○Log.push(ログ情報.追加:end)");
};

//■■■■■■■■■■■■■■■■■■■■■■■■■■■
//【名称】ログ情報：OutputBar出力
//【引数】なし
//【返却】なし(OutputBarに出力するのみなので)
//【処理】OutputBarにログ情報を出力する
//■■■■■■■■■■■■■■■■■■■■■■■■■■■
Log.prototype.toString = function(){
	OutputBar.writeln("○Log.toString(ログ情報.OutputBar出力:start)");
	for (var i = 0; i < this.arrLog.length; i++){
		OutputBar.writeln("this.arrLog[" + i + "]="
			+ this.arrLog[i][0] + ","
			+ this.arrLog[i][1] + ","
			+ this.arrLog[i][2]
		);
	}
	OutputBar.writeln("○Log.toString(ログ情報.OutputBar出力:end)");
};

//■■■■■■■■■■■■■■■■■■■■■■■■■■■
//【名称】ログ情報：書き出し
//【引数】出力ファイルの完全パス+ファイル名
//【返却】なし
//【処理】ログ情報をまとめたthis.arrLogをファイルに書き出す
//■■■■■■■■■■■■■■■■■■■■■■■■■■■
Log.prototype.fWrite = function(sLogFilePathName){
	OutputBar.writeln("○Log.fWrite(ログ情報.書き出し:start)");
	sLog = '';
	for (var i = 0; i < this.arrLog.length; i++){
		sLog +=  this.arrLog[i][0] + ","
			+ this.arrLog[i][1] + ","
			+ this.arrLog[i][2] + "\r\n";
	}
	editor.NewFile();//新規にファイルを作成
	docLog = editor.ActiveDocument;//現在開いている Document オブジェクトを返します。
	docLog.write( sLog );//現在のカーソル位置に文字列を挿入、または上書き
	docLog.Encoding = eeEncodingUTF8;//次に保存する時に使用されるエンコードを設定(UTF-8)
	docLog.UnicodeSignature = false;//次に保存する時に Unicode サイン (BOM) を付けない
	docLog.Save( sLogFilePathName );//文書を保存します。
	editor.ExecuteCommandByID(4120);//docNoWriteを保存しないで閉じます。■DELETE■
	OutputBar.writeln("○Log.fWrite(ログ情報.書き出し:end)");
};

//■■■■■■■■■■■■■■■■■■■■■■■■■■■
//■■　郵便番号管理クラス
//■■■■■■■■■■■■■■■■■■■■■■■■■■■
//■■■■■■■■■■■■■■■■■■■■■■■■■■■
//【名称】郵便番号管理：コンストラクタ
//【引数】なし
//【返却】なし
//【処理】郵便番号管理を管理するdocumentオブジェクトを作成する
//■■■■■■■■■■■■■■■■■■■■■■■■■■■
PostalCodeManage = function(docInFile,log){
	OutputBar.writeln("○PostalCodeManage.コンストラクタ(郵便番号管理:start)");

	docInFile.ExtractColumns(":1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13,:14,:15");
	this.docPCI = editor.ActiveDocument;

	docInFile.Activate();//文書をアクティブにします。
	editor.ExecuteCommandByID(4120);//ファイルを保存しないで閉じます

	this.log = log;
	OutputBar.writeln("○PostalCodeManage.コンストラクタ(郵便番号管理:end)");
};

//■■■■■■■■■■■■■■■■■■■■■■■■■■■
//【名称】郵便番号管理：書き出し
//【引数】なし
//【返却】なし
//【処理】this.docPCIを書き出す
//【めも】fn=ファイル名、celn=書き出す列
//■■■■■■■■■■■■■■■■■■■■■■■■■■■
PostalCodeManage.prototype.fWrite = function(fn,celn){
OutputBar.writeln("○PostalCodeManage.fWrite(郵便番号管理.書き出し:start)");

//●書き出し
	this.docPCI.ExtractColumns(celn);
	this.docPCI.Activate();//文書をアクティブにします。
	editor.ExecuteCommandByID(4120);//ファイルを保存しないで閉じます

	this.docWrite = editor.ActiveDocument;
	this.docWrite.Encoding = eeEncodingUTF8;//保存時のエンコード：UTF-8
	this.docWrite.UnicodeSignature = false;//保存時に(BOM)を付けない
	this.docWrite.Save( fn );//文書を保存
	this.docWrite.close();//文書を閉じる

OutputBar.writeln("○PostalCodeManage.fWrite(郵便番号管理.書き出し:emd)");
};

//■■■■■■■■■■■■■■■■■■■■■■■■■■■
//【名称】郵便番号管理：チェック１
//【引数】なし
//【返却】なし
//【処理】this.docPCIの町域名(漢字)の重複を削除
//【めも】
//■■■■■■■■■■■■■■■■■■■■■■■■■■■
PostalCodeManage.prototype.cnv1a = function(){
	OutputBar.writeln("○PostalCodeManage.cnv1(郵便番号管理.チェック１:start)");

//■町域の複数行に渡るかっこ書きを削除
//郵便番号の列を取得
this.docPCI.selection.SetActivePoint( eePosCellLogical, 3, 1 );
editor.ExecuteCommandByID(4461);//CSV文書で現在の列をヘディング無しで箱型選択モードで選択
sTmp = "\r\n" + this.docPCI.selection.Text;
aPostalCode = new Array();
aPostalCode = sTmp.split("\r\n");

//町域の列を取得
//this.docPCI.Activate();//文書をアクティブにします。
this.docPCI.selection.SetActivePoint( eePosCellLogical, 9, 1 );
editor.ExecuteCommandByID(4461);//CSV文書で現在の列をヘディング無しで箱型選択モードで選択
sTmp = "\r\n" + this.docPCI.selection.Text;//値を[1]以降に格納
aTyou = new Array();
aTyou = sTmp.split("\r\n");
sTmp = null;

this.docPCI.selection.Collapse();//選択状態をキャンセルします。
OutputBar.writeln("aPostalCode.length=" + aPostalCode.length);
OutputBar.writeln("aTyou.length=" + aTyou.length);

//最終行から開始行へ向かって処理を行う
fOkikae = false;//フラグ置き換えモード：終わり
re1 = new RegExp("^\"[^（]*）\"$");//'（'が無く、右端の'）'を検索(両端は'"')
re2 = new RegExp("^(\".+)（([^）]*)(\")");//'（'があり'）'が無い行を検索($1:開く括弧の前、$3:'"')
for(i = aTyou.length - 1; i >= 1; i--){
	if( ( aPostalCode[i] == aPostalCode[i-1] ) || fOkikae){//郵便番号が同じor置き換えモードの場合
		this.docPCI.selection.SetActivePoint( eePosCellLogical, 9, i );
		if( fOkikae ){
			if(aTyou[i].match(re2)){//'（'を検索
				this.log.push('i',this.docPCI.GetCell( i, 3, eeCellIncludeNone ),RegExp.$1 + RegExp.$3);
				this.docPCI.SetCell( i , 9, RegExp.$1 + RegExp.$3, eeDontQuote );//"（"以降の部分を削除
				fOkikae = false;//フラグ置き換えモード：終わり
			}else{
				//'（'の行と'）'の行の間の行(行をブックマーク)
				this.docPCI.selection.SelectLine();
				this.docPCI.selection.SetBookmark();
			}
		} else {
			if(aTyou[i].match(re1)){//'）'を検索
				this.docPCI.selection.SelectLine();
				this.docPCI.selection.SetBookmark();
				fOkikae = true;//フラグ置き換えモード：開始
			}
		}
	}
}

editor.ExecuteCommandByID(4589);//この文書のすべてのブックマークされた行を削除

OutputBar.writeln("○PostalCodeManage.cnv1(郵便番号管理.チェック１:end)");
};

//■■■■■■■■■■■■■■■■■■■■■■■■■■■
//【名称】郵便番号管理：チェック２
//【引数】なし
//【返却】なし
//【処理】this.docPCIの町域名(漢字)の置き換え
//【めも】
//■■■■■■■■■■■■■■■■■■■■■■■■■■■
PostalCodeManage.prototype.cnv2 = function(){
	OutputBar.writeln("○PostalCodeManage.cnv2(郵便番号管理.チェック２:start)");

//■町域の文字列を置き換え
this.docPCI.selection.SetActivePoint( eePosCellLogical, 9, 1 );//カーソル位置を設定
editor.ExecuteCommandByID(4461);//CSV文書で現在の列をヘディング無しで箱型選択モードで選択します。

//★文字列決め打ちで削除
this.docPCI.selection.Replace("以下に掲載がない場合","",eeFindReplaceSelOnly | eeReplaceAll,0);
this.docPCI.selection.Replace("(\".*)（(その他|丁目|番地|次のビルを除く|地階・階層不明|.*[、～].*|.*以上|.*以下)）(\")","\\1\\3",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0);

//★名駅ミッドランドスクエア（高層棟）（地階・階層不明）
this.docPCI.selection.Replace("(\".*)（高層棟）(.*\")","\\1\\2",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0);

//★三田市の次に番地がくる場合
this.docPCI.selection.Replace("\".*の次に番地がくる場合\"","\"\"",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0);

//★土樋（１丁目「１１を除く」） => 土樋（１丁目）
//★切畑（長尾山「その他」） => 切畑（長尾山）
this.docPCI.selection.Replace("(\".*（.*)「(.*を除く|その他)」(）\")","\\1\\3",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0);

//★音江町（国見その他） => 音江町（国見）
this.docPCI.selection.Replace("(\".*（.*)その他(）\")","\\1\\2",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0);

//★花田町官有地（無番地を除く）
//★芦田町福田（３７６－１０を除く）
//★津島町下畑地（乙を除く）
this.docPCI.selection.Replace("(\".*)（.*を除く）(\")","\\1\\2",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0);

//★厚内（全域） => 厚内
this.docPCI.selection.Replace("(\".*)（全域）(\")","\\1\\2",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0);

//■町域のかっこ"（" => "　"、"）" => ""へ
document.selection.Replace("(\".*)（(.*)）(\")","\\1　\\2\\3",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0);

OutputBar.writeln("○PostalCodeManage.cnv2(郵便番号管理.チェック２:end)");
};

//■■■■■■■■■■■■■■■■■■■■■■■■■■■
//【名称】郵便番号管理：チェック３
//【引数】なし
//【返却】なし
//【処理】this.docPCIの町域名(半角カタカナ)の置き換え
//【めも】
//■■■■■■■■■■■■■■■■■■■■■■■■■■■
PostalCodeManage.prototype.cnv3 = function(){
	OutputBar.writeln("○PostalCodeManage.cnv3(郵便番号管理.チェック３:start)");

//■町域の文字列を置き換え
this.docPCI.selection.SetActivePoint( eePosCellLogical, 6, 1 );//カーソル位置を設定
editor.ExecuteCommandByID(4461);//CSV文書で現在の列をヘディング無しで箱型選択モードで選択します。

//★開き括弧のみの場合、括弧以降を削除
re2 = new RegExp("^(\".+)\\(([^\\)]*)(\")");//'('があり')'が無い行を検索($1:開く括弧の前、$3:'"')
nLine = this.docPCI.GetLines();
for(i = 1; i < nLine ; i++){
	if(this.docPCI.GetCell( i, 6, eeCellIncludeQuotes ).match(re2)){//'('を検索
		this.docPCI.SetCell( i , 6, RegExp.$1 + RegExp.$3, eeDontQuote );//"（"以降の部分を削除
	}
}

//★文字列決め打ちで削除
this.docPCI.selection.Replace("ｲｶﾆｹｲｻｲｶﾞﾅｲﾊﾞｱｲ","",eeFindReplaceSelOnly | eeReplaceAll,0);
this.docPCI.selection.Replace("(\".*)\\((ｿﾉﾀ|ﾁｮｳﾒ|ﾊﾞﾝﾁ|ﾂｷﾞﾉﾋﾞﾙｦﾉｿﾞｸ|ﾁｶｲ･ｶｲｿｳﾌﾒｲ|.*[､\\-].*|.*ｲｼﾞｮｳ|.*ｲｶ)\\)(\")","\\1\\3",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0);

//★名駅ミッドランドスクエア（高層棟）
this.docPCI.selection.Replace("(\".*)\\(ｺｳｿｳﾄｳ\\)(.*\")","\\1\\2",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0);

//★三田市の次に番地がくる場合
this.docPCI.selection.Replace("\".*ﾉﾂｷﾞﾆﾊﾞﾝﾁｶﾞｸﾙﾊﾞｱｲ\"","\"\"",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0);

//★ﾂﾁﾄｲ(1ﾁｮｳﾒ<11ｦﾉｿﾞｸ>) => ﾂﾁﾄｲ(1ﾁｮｳﾒ)
//★ｷﾘﾊﾀ(ﾅｶﾞｵｻﾝ<ｿﾉﾀ>) => ｷﾘﾊﾀ(ﾅｶﾞｵｻﾝ)
this.docPCI.selection.Replace("(\".*\\(.*)<(.*ｦﾉｿﾞｸ|ｿﾉﾀ)>(\\)\")","\\1\\3",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0);

//★ｵﾄｴﾁｮｳ(ｸﾆﾐｿﾉﾀ) => ｵﾄｴﾁｮｳ(ｸﾆﾐ)
this.docPCI.selection.Replace("(\".*\\(.*)ｿﾉﾀ(\\)\")","\\1\\2",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0);

//★ﾊﾅﾀﾞﾁｮｳｶﾝﾕｳﾁ(ﾑﾊﾞﾝﾁｦﾉｿﾞｸ)
//★ｱｼﾀﾞﾁｮｳﾌｸﾀﾞ(376-10ｦﾉｿﾞｸ)
//★ﾂｼﾏﾁｮｳｼﾓﾊﾀｼﾞ(ｵﾂｦﾉｿﾞｸ)
this.docPCI.selection.Replace("(\".*)\\(.*ｦﾉｿﾞｸ\\)(\")","\\1\\2",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0);

//★ｱﾂﾅｲ(ｾﾞﾝｲｷ) => ｱﾂﾅｲ
this.docPCI.selection.Replace("(\".*)\\(ｾﾞﾝｲｷ\\)(\")","\\1\\2",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0);

//■町域(半角カタカナ)のかっこ"(" => " "、")" => ""へ
document.selection.Replace("(\".*)\\((.*)\\)(\")","\\1 \\2\\3",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0);

OutputBar.writeln("○PostalCodeManage.cnv3(郵便番号管理.チェック３:end)");
};

//■■■■■■■■■■■■■■■■■■■■■■■■■■■
//処理開始
//■■■■■■■■■■■■■■■■■■■■■■■■■■■

var startTime = new Date();
OutputBar.Visible = true;
OutputBar.Clear();
OutputBar.writeln("○(処理:start)");
Redraw = false;//ウィンドウの再描画:しない

docInFile = editor.ActiveDocument;
//文書を含むディレクトリのパスをファイル名を付けないで取得します。
sInFilePath = docInFile.Path;
//文書のファイル名をパスを付けないで取得します。
aInFileName = docInFile.Name.split(".");
//変換後書き出すファイル名
sOutFilePathName = sInFilePath + "\\" + aInFileName[0] + "_cnv.txt";
//ログ用ファイル名を設定
sLogFilePathName = sInFilePath + "\\" + aInFileName[0] + "_LOG.txt";

//CSV モードで列の数を取得します。文書が CSV モードでない場合は、0 を返します。
columnN = docInFile.GetColumns();
if(columnN != 15){
	alert( "列数エラー。列数は、" + columnN + "です。" );
	Quit();
}
//●ログ管理オブジェクトを作成
var log = new Log();
//●郵便番号管理オブジェクトを作成
var pcManage = new PostalCodeManage(docInFile,log);
//●町域名が複数行に渡る場合の処理
pcManage.cnv1a();//配列使用版
//pcManage.cnv1b();//match使用版
//●町域名（漢字）の置き換え
pcManage.cnv2();
//●町域名(半角カタカナ)の置き換え
pcManage.cnv3();

//●変換後のファイルを書き出す
pcManage.fWrite(sOutFilePathName,":1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13,:14,:15");
//pcManage.fWrite(sOutFilePathName,":3,:7,:8,:9"); //必要な列のみ指定する場合

//●ログファイル書き出し
log.fWrite(sLogFilePathName);

var endTime = new Date();
Redraw = true;//ウィンドウの再描画:する
alert("処理終了：" + (endTime - startTime)/1000 + "sec");
Quit();
```

##補足
- 町域名(半角カタカナ)を利用しないのであれば、"pcManage.cnv3();"の処理は不要です。
- ログファイル部分の処理は変換処理には関係ありませんので、削除しても問題ありません。
- 出力ファイルの文字コードや改行コードなどを変更する場合は、"pcManage.fWrite()"を変更すればよいです。
- EmEditorでのcsvファイル操作のメモ書きとして

##参考情報
- [とほほのJavaScriptリファレンス](http://www.tohoho-web.com/js/)
- [EmEditor ヘルプ](http://www.emeditor.org/ja/macro_index.html)
- [Google流JavaScriptにおけるクラス定義の実現方法(ES6以前)](https://www.yunabe.jp/docs/javascript_class_in_google.html)