第III部〜秀丸マクロのいろはにほへと HMJRE.DLLを使う場合の注意点

hidemaru Editor

Hidemaru Q and A

第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を使う場合、

  1. SetUnicodeIndexAutoConvert関数を、引数に「1」を指定して呼び出し、自動変換を有効にする。
  2. 各関数を呼び出す。

という手順になります。

確実に「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を使う場合には、

  1. NotifyEncode関数に、対象のエンコード値を指定して呼び出す。
  2. 各関数を呼び出す。

という手順になります。


目次に戻る