last updated:2021/08/02

←前へ 次へ→ 機能別Q&A 質問番号順Q&A


[HMM0077A]

●マクロでの文字列の比較

Q.
マクロのif文で文字列の比較はできますか? 単純に、if ($a < $b)などと比較すると、時々比較結果が変に感じるのですが…。

A.

できます。

ただし、比較のルールは文字コード順ではなく、辞書引き順となります。

大小比較をする場合は注意が必要です。辞書引き順のルールは以下のようになっているようです。

(以下は、本Q&A執筆者が独自に調査した結果を基に記述しています。このルールに基づかない結果になることがあるかもしれません)

例に示す式はすべて真です。

(1-a)

文字種全体の関係は、記号 < 英字 < かな < 漢字 となる。
英字同士の比較は大文字/小文字、全角/半角の区別なく、英字順に大きいと判断される。

かな同士の比較は、ひらがな/カタカナ、全角/半角の区別なく、五十音順に大きいと判断される。濁点、半濁点はないものと見なす。

例) "a" < "ア"(半角カタカナ)
例) "a" < "B"
例) "アイ"(半角カタカナ) > "アア"
例) "カク" < "カ゛ケ"(半角カタカナ)
例) "0" < "1"

(1-b)

文字数の違う文字列同士の比較で、短い方の文字列の長さまでが(1-aのルールで) 一致している場合、長いほうが大きいと判断される。
※ 長さはバイト数ではなく文字数でカウントする。
例) "ABC" < "abc1"

ここまでで決着がつかなかった場合に、次のルールを適用します。

(2)

 英字の同一文字の大文字と小文字の比較では、大文字のほうが大きい。
 かなの同一文字の比較では、次のような関係となる。

 濁点ナシ < 濁点付き < 半濁点付き

 半角カナ < 全角カタカナ < 全角ひらがな

 ※ 濁点の有無のほうが優先順位が高い。

 例) "ABC" > "abc"
 例) "アイ"(半角カタカナ) < "アイ"
 例) "カク" < "カ゛ク"(半角カタカナ)

ここまでで2つの文字列に違いが見られない場合、2つの文字列は同じであるという結果になります。

例) "バ"=="ハ゛" 漢字や記号など言及していない部分もあります(一部、例2も参照ください)が、ここで挙げていくときりがないので、ご自身で確かめてみましょう。


(例1)
次のマクロは、編集中のテキストのカーソル位置から、英大文字で始まる単語を取得して1単語ずつダイアログボックスに表示させようとしたものです。

ファイルの最後に達するまで繰り返します。

gowordtop;
#x1 = x;
#y1 = y;
while( 1 ) {
    wordright;
    if( result == false ) {
        break;
    }
    $str = gettext(#x1, #y1, x, y);
    $s1 = leftstr($str, 1);
    if( $s1 >= "A" && $s1 <= "Z" ) { // *1
        message $str;
    }
    #x1 = x;
    #y1 = y;
}

英単語かどうかの判断に文字列の比較を使用しています (*1 の行)。
("A"〜"Z"で始まる単語を英単語であると判断) このマクロは英大文字で始まる単語のみを表示する目的で作成したものですが、前述したルールにより英小文字で始まる単語も表示してしまいます。

正しく英大文字で始まる単語のみを表示するには、*1 の行を次のように変更します。

if( ascii($s1) >= 'A' && ascii($s1) <= 'Z' ) { // *1

比較対象を文字(列)のままではなく文字コードに変換して比較します。
※ AとZの囲みがシングルクォートに変わっていることに注意。


(例2)
例1の例を含めて、秀丸エディタの文字列比較(<, ==, >の演算子で行うもの)はWin32API の lstrcmp()を使っているので、その通りのルールになっています。
そのため、比較結果が以下のようなちょっと凝った?ものになります。

"パ"=="ハ゛゛"
"⑳"<"∞" //(Shift_JIS, UNICODEコード順と逆)
"℃"=="C゜"
"一"=="一〇〇〇〇"
"◎゛"=="●"
"㈱ヽ"=="㈱㈱"
"山ゞ"=="山山゛"

厳密に(単純に)同じ文字列か比較を行いたい場合は、以下のような方法があります。

$a = input ("1個め", "");
$b = input ("2個め", "");
// 秀丸エディタの比較演算子による文字列比較 (WIN32 API のlstrcmp()と同じ)
if ($a == $b) {
    message "$a == $b";
} else if ($a > $b) {
    message "$a > $b";
} else {
    message "$a < $b";
}
// 厳密な比較例
if (($a == "") && ($b == "")) {

    // 例外的に strstr("", "") == -1 になります。
    message "$a と $b は共にヌルストリングです。";
} else if ((strlen($a) == strlen($b)) && (strstr($a, $b) == 0)) {
    message "本当に $a と $b は同じ文字列です。";
} else {
    message "$a と $b は違う文字列です。";
}
endmacro;

厳密な(単純な)大小関係を知りたい場合は、ascii関数を使って1文字ずつ比較する方法があります。

[解説]
Win32API の lstrcmp() についての詳細は以下を参照してください。
lstrcmp 関数

辞書引き順の詳細は、JISの日本語文字列照合順番 X4061-1996で定義されています。


←前へ 次へ→ 機能別Q&A 質問番号順Q&A