月刊プログラム!
Visual Basic For Excel

Vol_18
2002.Feb.02

Presented by kouta_y
感想等は掲示板、苦情はメールへ。

にこちゃん
久しぶりに更新です。
改装(?)に忙しく、なかなか先へ進めませんでした。。。


文字列をバイト単位で扱ってみる。

さて、これはどういう事かというと、今まで「文字列型」で扱ってきた文字列を、バイト単位で操ってみようというものです。
最初に言いますが、正直これはVBでは何の役にも立ちません。
今回にも出てくる「API」を使う時か、将来C言語などVBでいう「文字列型」が存在しない言語をやる時の為に覚えておいてください。

VBで文字列をバイト配列で扱っても面倒にするだけです。(あぁ余計な事を・・・)


下準備

まず下準備をします。
下の宣言を「グローバル領域」に打ち込んでください。
グローバル領域とは、「Sub〜」や「Function〜」プロージャの外という意味です。

'配列から文字列型へ
Declare Function ArryToStr Lib "kernel32" Alias "RtlMoveMemory" ( _
    ByVal Destination As String, _
    Source As Any, _
    ByVal Length As Long _
) As Long

'文字列型から配列へ
Declare Function StrToArry Lib "kernel32" Alias "RtlMoveMemory" ( _
    Destination As Any, _
    ByVal Source As String, _
    ByVal Length As Long _
) As Long

最初に言いますが、これはVBの関数ではありません
いきなりよく分かりませんが、これは「kernel32.dll」というDLLファイルから直接関数呼び出す宣言文です。
この関数の事を「API関数」と呼びます。
APIについては、APIのページで簡単な説明をしています。
今回はAPIを使うのが目的ではないので、細かい説明は省かせてもらいます。

とりあえず下準備はここまでです。


文字列をバイト単位で扱うとは?

実は「文字列」というのは、「配列」なんです。
配列についてはこの辺でやりました。

例えば、「ABCDE」という5文字の文字列があった場合。
「1バイト領域の変数が、5つ並んでいる」という意味になります。



メモリ番地は1バイトステップで、104番地までの5バイトとなります。


で、文字の実際は「数値」だという事は何回か説明をしました。
だとすれば文字列はバイト型の配列変数だとも言い換えれます。

はたして本当にそうなのでしょうか?
はい。ではそういう事で今回の本題に入ります。(やっとかよとか言わない)


文字列をバイト配列に変換

ここでさっき打ち込んだAPI関数が登場です。
2つあるうちの、下の関数。「StrToArry」関数を使います。
この関数は、メモリ内容をコピーする関数です。
1番目の引数は「コピー先の変数」
2番目の引数は「コピー元の変数」
3番目の引数は「コピーするバイト数」
です。


では、下のコードを打ち込んでください。
まだ実行はしないでください。

Sub Test()
Dim B(10) As Byte
Dim
S     As String

S = "ABCDE"

'Sの内容をBにコピー
Call StrToArry(B(0), S, Len(S)) 'ここにブレークポイント

'バイト配列を文字列に変換
Debug.Print StrConv(B, vbUnicode)

End Sub 'ここにブレークポイント

「ここでブレークポイント」と書いてある行にブレークポイントを設定してください。
ブレークポイントとは、その行で一旦処理を停止させるデバッグの技です。
目的の行へカーソルを持っていき、F9を押して行全体が茶色くなれば設定された事になります。

さて、StrToArry関数を使うにあたって注意点がいくつか。

・ B変数の要素数(領域)は、関数を呼び出す前に設定する
・ B変数の要素数(領域)は、コピーするバイト以上にする事


です。
これをしないと「強制終了」というエラーとともに、エクセル自体が落ちます。

あと、第一引数にB(0)と渡してますが、これは「コピー先へのポインタ」になります。
この辺はあとで説明します。

最後に使っているStrConv関数は、バイト配列を文字列にして返すVBの関数です。

では長くなりましたが、一回実行してみましょう。


さっきブレークポイントを設定した行が黄色くなって処理が中断したはずです。
まずそこで変数Bの中身を覗いてみることにします。

「表示(V)」→「ローカルウィンドウ(S)」で、ローカルウィンドウを表示させてください。
で、Bの内容を見てみましょう。



全ての領域が0(ゼロ)で埋まっていると思います。

確認したら、F5キーを押して次に進んでみます。
「End Sub」で止まったら、もう一度Bの内容を確認してみてください。



こうなりましたか?
無事、上手く変数Sの値をコピー出来たようです。
イミデイトウィンドウにも"ABCDE"と表示されたと思います。

終わるにはもう一度F5キーを押して、終了させてください。



結果

今回の実験は「文字列はバイト配列と言えるのか?」という事でした。
上の実験により、

・ 配列に順番に文字コードが格納された
・ StrConv関数を使って「文字列」として復元できた

以上の事がわかり、結果としては「言える」になります。


バイト単位で遊んでみる

さて、上の実験で「文字列はただのバイト配列」という事がわかったので、ちょいと遊んでみましょう。

先ほど、「ポインタ」という言葉が出ましたが、これはどういう意味かというと、、、
B(0)
とやった場合、これは配列変数の先頭のアドレス(ポインタ)という意味です。
領域は0から始まるので、11バイトになります。
B(3)
とやれば、4バイト目のアドレス(ポインタ)なので、領域は4〜10、つまり8バイトという意味になります。

どういう事になるか、実験プログラムです。

Call StrToArry(B(3), S, Len(S))

上の実験コードと全く同じで、第一引数をB(3)とします。
「End Sub」の行でブレークポイントを設定してください。
んで実行します。



どうでしょう。
百聞は一見に如かず。です。

4バイト目からのアドレスを渡したので、B(0)〜B(2)の3バイトは無視された(というより無視した)んです。


Right関数

と言ってもRight関数を使うわけではありません。
全く同じ事をやってみようという事です。

まず、文字列を配列に変換します。

Call StrToArry(B(0), "ABCDE", 5)

で、次に、もう1個のAPI関数ArryToStrの登場です。
使い方は、StrToArryと逆なだけで全く一緒です。

S = Space(10)
Call ArryToStr(S, B(3), 2)
Debug.Print Trim$(S)

結果:
DE

上手く右から2文字を取り出せたようです。
後ろにスペースが入ってしまうので、Trim関数で消します。
S = Space(10)とやっているのは、領域を確保する為です。
API関数を使う場合は、領域の確保は必ずやらなければいけません。(作者はたまに忘れてヒドイ目にあいます)


他の文字列操作も大抵実現できます。
Left関数や、Mid関数の実現のコードを組んだモジュールは一番下にありますので参考にしてください。

だいたいこんな感じです。
所々ブレークポイントで止めて、配列の内容を確認するといいと思います。

最初にも言いましたが、VBで文字列をバイト配列として扱ってメリットありません。
コードがぐちゃぐちゃになるだけです。
ただ今回は「文字列はこういうもんだ」とだけ思ってもらえれば良いです。

あと、今回出てきた「StrToArry」と「ArryToStr」の両関数。
詳しく調べたい方は、この関数名で探しても絶対出てきません
今回の関数名は、作者が勝手に決めた関数名で、オリジナルとは異なります。
オリジナルは「MoveMemory」という名前です。
宣言の仕方も変えてあるので注意してください。


文字列については今回で終わりにしようと思います。

今回やった実験コード(遊び付)
Module1.txt