月刊プログラム!
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