last updated:2021/08/02

Hidemaru Q and A

第III部〜秀丸マクロのいろはにほへと
 ビット演算を極める


ビット演算を極める

【0】ここに書いてあること

秀丸エディタのマクロの中でも、応用的な機能の中には、2進数の概念やビット演算を使用する事があります。

例えば、以下の物があります。

C言語など、他の言語でこれらの概念や演算子を理解されている方はまだしも、初めての方はとっつきにくい部分があります。この章では、ビット演算のしくみと秀丸マクロでの使い方について説明します。

【1】はじめに - 秀丸マクロで使えるビット演算子

秀丸マクロで使えるビット演算子は以下の3種類があります。

ビット演算子
& ビット毎のAND
| ビット毎のOR
^ ビット毎のXOR

と言われて、分かったという方は、以下の説明は多分必要ないでしょう。「ANDってどういうこと?」「ビット毎って何?」と思われる方もいるでしょう。ビット演算のしくみを説明するにあたって、まず関連の深い「2進数」「16進数」について説明します。その後、ビット演算子の意味と使い方について説明していきます。

[補足]
^ は、BASIC系の言語では、べき乗計算の演算子です。間違えないようにしましょう。

【2】2進数、16進数とは

10進数とは、0〜9の数字を1桁として表す数値の表現方法を指します。同様に考えれば、2進数とは、0〜1の数字を1桁として表す数値の表現です。

2進数は、一般にコンピュータで数値を表す時の基本となる表記方法です。スイッチの ON と OFF をそれぞれ 1と0に見立て、そのスイッチを複数集めることで、大きな値の桁も表します。

[例] 10進数、2進数、16進数の対応表。

対応表
10進数 2進数 16進数
0 0 0
1 1 1
2 10 2
3 11 3
4 100 4
5 101 5
6 110 6
7 111 7
8 1000 8
9 1001 9
10 1010 A
11 1011 B
12 1100 C
13 1101 D
14 1110 E
15 1111 F
16 10000 10

10進数では 1の桁 10の桁 100の桁 …と続きますが、言い換えると 100の桁 101の桁 102の桁 …となります。

2進数では 20の桁 21の桁 22の桁 …と続きます。

2進数4桁(10進数でいえば0〜15)を1桁にまとめた数字の表現が16進数になります。

[補足]
「2進数・10進数」という表記について。秀丸エディタヘルプに限らず、計算機関係の文献でこのような用語がよく出ますが、厳密には数自体にこうした種別が有るわけではありません。正しくは「二進(法によって)表記した数」などとすべきものですが、ここでは便宜上この表記を使って説明させてもらうことにします。

【3】秀丸マクロでの16進数の記述方法

秀丸マクロでは、2進数を直接記述する方法は有りませんので、10進数か16進数のどちらかの表記を使うことになります。

秀丸マクロ(および一般にC言語)では、16進数の数字には頭に 0x という文字を付け、10進数でいう10〜15の数値をあらわす文字として A〜F を割り当てます。(半角文字を使用、大文字/小文字を区別しません)

[例]

10進数で11の値を、16進数で記述し変数に代入する。

#a = 0x0b;
[補足]

もちろん、これは以下のように書くのと同じ意味です。

#a = 11;

秀丸エディタの側から見ると、どちらの方法で記述しても値としては同じであるため、結果は変わりません。逆にいえば、16進数表記は、ビット演算を行う場合に、コードを書く側の人間が、コードが理解しやすくなって便利な記述方法であるとも言えます。

ビットとは、2進数ではその1桁に相当します。 2進数8桁(16進数2桁)の単位をバイトといい、10進数では0〜255の範囲を表すことができます。

[補足]
一般には、最上位ビットをマイナス符号と考えて、値の表す範囲を変える符号付き表記も有ります。秀丸マクロの場合、数値と数値変数の表記は、4バイト(32ビット)の符号付き整数になります。1バイトや2バイト分の表記だと、符号無しと同様になります。

【4】ビット演算の概念

次にビット演算の概念を説明します。

AND、OR、XORは、それぞれ以下の結果になります。

[補足]
AND、OR、XORという名前の演算子は秀丸マクロには有りません。ここでは、演算の概念の説明のためにこの表記を使っています。

では、実際に秀丸マクロのビット演算子を使って確認してみましょう。以下のマクロを実行して、ダイアログに表示される演算結果を見てください。

#a = 1 & 1;
#b = 1 & 0;
#c = 0 & 1;
#d = 0 & 0;

message "1 AND 1 = " + str(#a);
message "1 AND 0 = " + str(#b);
message "0 AND 1 = " + str(#c);
message "0 AND 0 = " + str(#d);

#e = 1 | 1;
#f = 1 | 0;
#g = 0 | 1;
#h = 0 | 0;

message "1 OR 1 = " + str(#e);
message "1 OR 0 = " + str(#f);
message "0 OR 1 = " + str(#g);
message "0 OR 0 = " + str(#h);

#i = 1 ^ 1;
#j = 1 ^ 0;
#k = 0 ^ 1;
#l = 0 ^ 0;

message "1 XOR 1 = " + str(#i);
message "1 XOR 0 = " + str(#j);
message "0 XOR 1 = " + str(#k);
message "0 XOR 0 = " + str(#l);

【5】ビット演算の詳細

冒頭で、 &, | , ^ は「ビット毎の」と書きましたが、複数ビットからなる値同士を計算するとビット毎に演算が行われます。いくつか例を見てみましょう。

[例]
2進数 16進数 10進数
10110110 B6 182
AND) 10001000 88 136

10000000 80 128

2進数 16進数 10進数
10110110 B6 182
OR) 10001000 88 136

10111110 BE 190

2進数 16進数 10進数
10110110 B6 182
XOR) 10001000 88 136

00111110 3E 62

これを実際に秀丸マクロで記述すると以下のようになります。

#a = 0xb6 & 0x88;
#b = 0xb6 | 0x88;
#c = 0xb6 ^ 0x88;

message "0xb6 AND 0x88 = " + str(#a);
message "0xb6 OR 0x88 = " + str(#b);
message "0xb6 XOR 0x88 = " + str(#c);

【6】ビット演算でできること

これらの演算子を使って何ができるか。

これらの演算子を利用して、秀丸マクロの中では16進数の値(文字コード等)の分類ができたり、フラグの値として使用される値の一部ビットを操作したりできます。

【6-1】AND演算

AND演算を使うと、マスキング(=任意のビットを0にした値を出力)ができます。 0にしたいビットを0に、元の値を残したいビットを1にした値を作成してAND演算することで実現できます。

[例]

ANDで(2進数における)任意の桁が0か1かを調べる。

調べたい桁だけを1にし、それ以外を0にした数(この値のことをマスクなどともいいます)とAND演算して、結果がマスクと等しければ1で、等しくなければ0であると判ります。

2進数 16進数 10進数
00011110 1E 30
AND) 00001000 08 8

00001000 08 8

複数の桁を一気に判定することもできます。

2進数 16進数 10進数
00110110 36 54
AND) 10010000 90 144

00010000 10 16

【6-2】OR演算

OR演算を使うと、任意のビットを1に変更できます。

1にしたいビットを1に、元の値を残したいビットを0にした値を作成してOR演算することで実現できます。

[例]

20ビット〜23ビット(下位4ビット)を1にしたい場合。

2進数 16進数 10進数
10110110 B6 182
OR) 00001111 0F 15

10111111 BF 191

【6-3】XOR演算

XOR演算を使うと、任意のビットの値を反転できます。

[例]
20ビット〜27ビットをすべて反転したい場合。
2進数 16進数 10進数
10110110 B6 182
XOR) 11111111 FF 255

01001001 49 73

【7】秀丸マクロでの使用例

[例1] encode(charset)キーワードの操作

encode(charset)キーワード は、ビット0〜5までが文字コードを示し、ビット6〜7が改行コードを示します。

$encode[0]="新規作成直後";
$encode[1]="Shift-JIS";
$encode[2]="Unicode";
$encode[3]="EUC";
$encode[4]="JIS";
$encode[5]="UTF-7";
$encode[6]="UTF-8";
$encode[7]="Unicode (Big-Endian)";
$encode[8]="欧文";
$encode[9]="簡体字中国語";
$encode[10]="繁体字中国語";
$encode[11]="韓国語";
$encode[12]="韓国語(Johab)";
$encode[13]="中央ヨーロッパ言語";
$encode[14]="バルト語";
$encode[15]="ギリシャ語";
$encode[16]="キリル言語";
$encode[17]="シンボル";
$encode[18]="トルコ語";
$encode[19]="ヘブライ語";
$encode[20]="アラビア語";
$encode[21]="タイ語";
$encode[22]="ベトナム語";
$encode[23]="Macintosh";
$encode[24]="OEM/DOS";
$encode[25]="その他";
$encode[26]="バイナリモード";
$encode[27]="UTF-32";
$encode[28]="UTF-32 (Big-Endian)";

$newline[0]="CR+LF";
$newline[1]="LF";
$newline[2]="CR";
$newline[3]="CR+LF";

// encode の構造(上位2ビットが改行、下位6ビットがエンコードを示す)
// 76543210 ビット順
// XXxxxxxx 2進数

// エンコード部分のみ切り出し
//     XXxxxxxx:encodeキーワードの値
//AND) 00111111:0x3F
//--------------
//     00xxxxxx:文字コード
#encode = encode & 0x3F;

// 改行部分のみ切り出し
//     XXxxxxxx:encodeキーワードの値
//AND) 11000000:0xC0
//--------------
//     XX000000:改行コード
//     ついでにビットシフト代わりの割り算(6ビット右シフト)
#newline = (encode & 0xC0) / 64;

message "エンコードは「" + $encode[#encode] + "」です。\n" +
        "改行コードは「" + $newline[#newline] + "」です。";
endmacro;


[例2] .hilightファイル内の値

.hilightファイル内で、強調表示部分の設定を表示する。

//カーソルのある行を取り込む
$line = gettext2(0, lineno, linelen2, lineno);
#pos = strstr($line, ",");
if(#pos == -1) endmacro;
//値のみ切り出し
#check = val(leftstr($line, #pos));
//表示方法のみ切り出し
#hilifgt1 = #check & ((0x08 | 0x10 | 0x20) ^ 0xFFFFFFFF);
//検索方法のみ切り出し
#hilifgt2 = #check &  (0x08 | 0x10 | 0x20);
//結果を初期化
$hilight  = "";

//表示方法(値)をチェック
if     (#hilifgt1 == 0x001) $hilight = "強調1";
else if(#hilifgt1 == 0x041) $hilight = "強調2";
else if(#hilifgt1 == 0x081) $hilight = "強調3";
else if(#hilifgt1 == 0x0C1) $hilight = "強調4";
else if((#hilifgt1 == 0x101) || (#hilifgt1 == 0x103)) $hilight = "強調5";
else if((#hilifgt1 == 0x141) || (#hilifgt1 == 0x143)) $hilight = "強調6";
else if((#hilifgt1 == 0x181) || (#hilifgt1 == 0x183)) $hilight = "強調7";
else if((#hilifgt1 == 0x1C1) || (#hilifgt1 == 0x1C3)) $hilight = "強調8";
else if(#hilifgt1 == 0x005) $hilight = "行の強調1";
else if(#hilifgt1 == 0x045) $hilight = "行の強調2";
else if(#hilifgt1 == 0x085) $hilight = "行の強調3";
else if(#hilifgt1 == 0x0C5) $hilight = "行の強調4";
else if(#hilifgt1 == 0x003) $hilight = "特に強調1";
else if(#hilifgt1 == 0x043) $hilight = "特に強調2";
else if(#hilifgt1 == 0x083) $hilight = "特に強調3";
else if(#hilifgt1 == 0x0C3) $hilight = "特に強調4";

//検索方法(ビット)をチェック
if((#hilifgt2 & 0x008) == 0x008) $hilight = $hilight + ",大文字/小文字を区別しない";
if((#hilifgt2 & 0x010) == 0x010) $hilight = $hilight + ",正規表現";
if((#hilifgt2 & 0x020) == 0x020) $hilight = $hilight + ",単語";
//結果を表示
message $hilight;
endmacro;


[例3] searchoptionキーワードの操作

searchoption を取得し、必要な設定を行い検索する。

//検索オプション取得
#SEARCH_OPT  = searchoption;
//「大文字/小文字の区別」がOFFなら有効にする。
if((#SEARCH_OPT & 0x00000002) != 0x00000002) #SEARCH_OPT  = #SEARCH_OPT | 0x00000002;

//「置換かどうか」置換ならOFFにする。
if((#SEARCH_OPT & 0x00000004) == 0x00000004) #SEARCH_OPT  = #SEARCH_OPT ^ 0x00000004;

setsearch searchbuffer, #SEARCH_OPT;


[例4] existfile関数の戻り値判定

existfile関数は、第2引数に「1」を指定すると、ファイル属性を取得できます。その戻り値をチェックし、フォルダ(ディレクトリ)か判定しています。

#ATTRIBUTE_READONLY  = 0x00000001;
#ATTRIBUTE_HIDDEN    = 0x00000002;
#ATTRIBUTE_SYSTEM    = 0x00000004;
#ATTRIBUTE_DIRECTORY = 0x00000010;
#ATTRIBUTE_ARCHIVE   = 0x00000020;

$CHECK = macrodir;
#FILE_ATTRIBUTE = existfile($CHECK, 1);
if((#FILE_ATTRIBUTE & #ATTRIBUTE_DIRECTORY) == #ATTRIBUTE_DIRECTORY){
    message "[" + $CHECK + "]はフォルダです。";
}
endmacro;

目次に戻る