第III部〜秀丸マクロのいろはにほへと
HMJRE.DLLを使う場合の注意点
●HMJRE.DLLを使う場合の注意点
【0】はじめに
【1】文字変数にShift_JISに含まれない、Unicode文字がある場合
【2】外国語を対象とする場合
はじめに
秀丸エディタの正規表現エンジンであるHMJRE.DLLは、マクロから呼び出すことが可能です。 (マクロからのdllfunc呼び出し)
文字変数や文字列に対して、正規表現を使った検索、置換が実行できますが、 使用する場合には、注意点があります。
- 文字変数にShift_JISに含まれない、Unicode文字がある場合
- 外国語を対象とする場合
秀丸エディタ本体で検索を行う場合は、秀丸エディタがつじつまを合わせてくれますが、 マクロで使う場合は、呼び出した側が注意しないといけない場合があります。
文字変数にShift_JISに含まれない、Unicode文字がある場合
Unicode文字が含まれる場合の問題点
秀丸エディタの内部的な事情を少し説明します。
- 秀丸エディタの内部コードはShift_JISがベース。
- Shift_JISに含まれないUnicode文字は、特殊な文字コードになっていて、1文字4バイトで構成されている。
- Shift_JISがベースになっているため、マクロでは、常に漢字は2バイトとして扱われる。
基本的にはShift_JISである前提で動作しています。
通常、外部DLL内の関数を dllfunc/dllfuncstr を使って呼び出す時は、 引数に指定する文字列に「Shift_JISに含まれないUnicode文字」が含まれると、 その文字を削除して、外部DLLに渡されます。
これは、DLLとのインターフェースが、マルチバイト文字(Shift_JIS)を前提としている事と、 「特殊な文字コード」を外部DLLが理解できないためです。 (dllfuncw/dllfuncstrw を使えば、Unicode文字列を外部DLLに渡す事は可能ですが、DLL側の対応が必要です)
HMJRE.DLLは、例外的に「 特殊な文字コード」 を理解できる為、 dllfunc/dllfuncstr 呼び出し時、文字列内に「Shift_JISに含まれないUnicode文字」があっても、 秀丸エディタ内部の「特殊な文字コード」がそのままHMJRE.DLLに渡されます。
HMJRE.DLLの関数自体は、マッチ位置やマッチした長さを、バイト数で返します。 「特殊な文字コード」は「1文字4バイト」なので、「常に漢字は2バイト」で扱うマクロ関数と一致しません。
以下のマクロは、文字変数 $a
の最後にある"a"の位置を返すマクロです。 マクロ関数とHMJRE.DLL内の関数の比較になります。
// マクロ1
// このマクロはUnicode(UTF-16LE)で保存する事
// 0 1
// 01234567890
$a = "hidemaru𠀋a";
#n1 = strrstr($a, "a");
// 見つけた場所から1文字切り出す
$b1 = midstr($a, #n1, 1);
loaddll "HMJRE.DLL";
if(!result){
message "DLLのロードに失敗しました。";
endmacro;
}
#n2 = dllfunc("FindRegular", "a$", $a, 0);
if(0 > #n2){
message "検索に失敗しました。";
endmacro;
}
#l2 = dllfunc("GetLastMatchLength");
// マッチした箇所を切り出す
$b2 = midstr($a, #n2, #l2);
message "場所(#n1) :" + str(#n1) + "\n" +
"場所(#n2) :" + str(#n2) + "\n" +
"取得文字1($b1):" + $b1 + "<--" + "\n" +
"取得文字2($b2):" + $b2 + "<--";
freedll;
endmacro;
「マクロでは、常に漢字は2バイトとして扱われる」 ので、最後の"a"は、10を期待します。
// ★
// 0 1
// 01234567890
$a = "hidemaru𠀋a";
しかし、マクロを実行した結果は、
場所(#n1) :10
場所(#n2) :16
取得文字1($b1):a<--
取得文字2($b2):<--
となり、strrstr関数は期待通り「10」を返しますが、FindRegular関数は、「16」を返します。
これは "𠀋"という文字が、UTF-16で表現するとサロゲートペアとなるため、Unicode文字2文字で表現されているからです。 「特殊な文字コード」は1文字4バイトなので、2文字分で8バイトになっています。
先ほどのマクロを変更して、マッチした長さを返すGetLastMatchLength関数を使用し、 "𠀋"を検索した時の、マッチした長さ(バイト数)を確認してみます。
// マクロ2
// このマクロはUnicode(UTF-16LE)で保存する事
// 0 1
// 01234567890
$a = "𠀋123456789";
#l1 = strlen($a);
loaddll "HMJRE.DLL";
if(!result){
message "DLLのロードに失敗しました。";
endmacro;
}
#n2 = dllfunc("FindRegular", "𠀋", $a, 0);
if(0 > #n2){
message "検索に失敗しました。";
endmacro;
}
#l2 = dllfunc("GetLastMatchLength");
// マッチした箇所を切り出す
$b2 = midstr($a, #n2, #l2);
message "文字列長(#l1):" + str(#l1) + "\n" +
"場所(#n2) :" + str(#n2) + "\n" +
"長さ(#l2) :" + str(#l2) + "\n" +
"取得文字($b2):" + $b2 + "<--";
freedll;
endmacro;
マクロ2の実行結果です。
文字列長(#l1):11
場所(#n2) :0
長さ(#l2) :8
取得文字($b2):𠀋123456<--
"長さ(#l2)"が、"𠀋" を検索しマッチしたバイト数になり、8である事が確認できます。
以上のことから、マクロ1実行時に、DLLに渡される $a
の中身は、以下の様なデータになっている為、 "FindRegular"の場合、最後の"a"の位置は 「16」 となります。 ("𠀋"は、"XXXX"と"YYYY" の様な、8バイトで表現される)
// 0 1 2
// 012345678901234567890
$a = "hidemaruXXXXYYYYa";
「マクロでは、常に漢字は2バイトとして扱われる」ということになっていますが、 HMJRE.DLLの関数使用時に「特殊な文字コード」が含まれると、 漢字1文字でも最大で8バイトになる事もあるため、マクロ関数と一致しません。 マクロ1、マクロ2で、"取得文字($b2)"がおかしいのは、この不一致が原因です。
HMJRE.DLL内の関数で戻り値を、マクロの関数で使用する場合は、ズレの有無をチェックし、 ズレていたら補正しないと、正確な位置、長さを取得出来ません。
対処方法
HMJRE.DLL Ver.3.13(秀丸エディタ Ver.8に添付)から、"SetUnicodeIndexAutoConvert関数"
が追加されています。
//#flg = 0 自動変換無効、#flg = 1 自動変換有効
#n = dllfunc("SetUnicodeIndexAutoConvert", #flg);
この関数を使用すると、HMJRE.DLLの各関数の戻り値が、マクロでそのまま使用できるような値に自動変換されます。
"SetUnicodeIndexAutoConvert関数" を使い自動変換を有効にしたマクロです。
//マクロ3
// このマクロはUnicode(UTF-16LE)で保存する事
// 0 1
// 01234567890
$a = "hidemaru𠀋a";
// $a に入っている長さ(バイト数)
#l = strlen($a);
// $a の最後から、"a" を探す
#n1 = strrstr($a, "a");
// 見つけた場所から1文字切り出す
$b1 = midstr($a, #n1, 1);
loaddll "HMJRE.DLL";
if(!result){
message "DLLのロードに失敗しました。";
endmacro;
}
// 引数に 1 を設定し、自動変換を有効にする(0 で無効化)
#x = dllfunc("SetUnicodeIndexAutoConvert", 1);
// $a から最後にある "a" の位置を取得
#n2 = dllfunc("FindRegular", "a$", $a, 0);
if(0 > #n2){
message "検索に失敗しました。";
endmacro;
}
// マッチした内容の長さを取得
#l2 = dllfunc("GetLastMatchLength");
// マッチした箇所を切り出す
$b2 = midstr($a, #n2, #l2);
//結果表示
message "文字列長(#l) :" + str(#l) + "\n" +
"場所 (#n1):" + str(#n1) + "\n" +
"場所 (#n2):" + str(#n2) + "\n" +
"長さ (#l2):" + str(#l2) + "\n" +
"取得文字($b1):" + $b1 + "<--" + "\n" +
"取得文字($b2):" + $b2 + "<--";
freedll;
endmacro;
上記マクロの実行結果です。
文字列長(#l) :11
場所 (#n1):10
場所 (#n2):10
長さ (#l2):1
取得文字($b1):a<--
取得文字($b2):a<--
"FindRegular"の戻り値を、そのまま使用しても、正常に取得できています。 ("SetUnicodeIndexAutoConvert" の自動変換が有効になっている。)
ということで、マクロからHMJRE.DLLを使う場合、
- SetUnicodeIndexAutoConvert関数を、引数に「1」を指定して呼び出し、自動変換を有効にする。
- 各関数を呼び出す。
という手順になります。
確実に「Shift_JISに含まれないUnicode文字」がないと判っているなら不要ですが、 HMJRE.DLLをマクロから使用する場合には、SetUnicodeIndexAutoConvert関数
を実行することを推奨します。
外国語を対象とする場合
マクロでは、基本的に日本語(Shift_JIS)を対象にしているため、日本語以外の言語使用する場合、 検索に失敗する場合があります。
Shift_JISの文字コードに関係する話になります。
Shift_JISの文字コードの割り当ては以下のようになっています。
- 0x00〜0x1f、0x7f
- この範囲は制御コードが割り当てられている。
- 0x20〜0x7e
- ASCII文字が割り当てられている。ASCII文字とは英数字と記号になります。
- 0xa1〜0xdf
- 半角カタカナです
- 0x81〜0x9f、0xe0〜0xef
- Shift_JISの上位バイト
- 0x40〜0x7e、0x80〜0xfc
- Shift_JISの下位バイト
欧文文字コード(CP1252)の場合、Shift_JISの上位バイトに当たる範囲に、文字が割り当てられています。 (0xe0〜0xef の 範囲には、アクセント記号付きの文字が割り当てられている)
その為、欧文文字を検索したくても、Shift_JISの上位バイトと認識され、正しく検索出来ない場合があります。 日本語以外の言語を使用する場合には、あらかじめ言語の情報をDLLに教えておく必要があります。
HMJRE.DLLのVer.5.01から、"NotifyEncode"関数が追加されています。 (秀丸エディタのVer.8.74以降から添付)
この関数を使用することで、対象のエンコードを指定できます。
//ファイルと同じ文字コードを使う場合には、引数に "encode"キーワードを指定する。
//ファイルと違う文字コードを使う場合には、"encode"キーワードの値を調べて指定する
#n = dllfunc("NotifyEncode", encode);
マクロから外国語を対象にして、HMJRE.DLLを使う場合には、
- NotifyEncode関数に、対象のエンコード値を指定して呼び出す。
- 各関数を呼び出す。
という手順になります。