VB6を使用しています。バイト配列を試しています。
今まではバイナリファイルからGet#で一文字づつ読んでデータの頭のタグ、ターミネーターのタグをを探し、そこからの相対位置から文字列、Long,Dblなどの変数を読み取っていました。(タグ間のバイト数は一定)正確に動くのですが遅いという欠点がありました。
Dim VInt as integer
Dim VLong as Long
Dim VDbl as Double
Dim sName as String * 16 (1バイト文字)
Dim i,i2
・
・
Get #1,i,sName
Get #1,i + 12, VDbl
Get #1,i + 16, VLng
Get #1,i + 20, VInt
のようにしていました。
これをバイト配列で一挙に読むように
Dim buf() As Byte
Dim LByte As Long
Open sFILE For Binary As #1
LByte = LOF(1)
If LOF(1) Then
ReDim buf(1 To LOF(1))
Get #1, , buf
End If
Close #1
のようにしてみました。この中からsNameを切り出すのに
sName = ""
For i2 = 1 to 16
sName = sName & Chr(buf(i + i2))
Next i2
のようにしてみましたが何かスマートな方法がありそうな気がして質問させていただきました。 よろしくお願いします。
ユーザー定義型 を使ってみたら如何でしょうか?
http://madia.world.coocan.jp/cgi-bin/VBBBS/wwwlng.cgi?print+200804/08040016.txt
8月さまありがとうございます。
ユーザー定義型というものを使ったことがないのでこれから勉強します。コードを眺めながらの疑問なのですが読もうとするバイナリファイルには解読できていない各種のデータが含まれており、その中から特定の頭のタグとターミネーターを探し出してこの間にある素性のわかったデータだけ取り出そうとしていますがこのような場合でもご紹介いただいたサンプルのように処理できるのでしょうか? テストしていないでの再質問ですがよろしくお願いします。
1レコードの中身がどうなっているか、すなわち
データ構造が解析されていないと無理かも知れませんね。
しかし元のバイナリファイルがデタラメにデータを書き出している
とも思えないので、解析をしてみたら如何ですか?
Get #1,i,sName
Get #1,i + 12, VDbl
Get #1,i + 16, VLng
Get #1,i + 20, VInt
...として読み込んでいたと言う事は1レコードのサイズと
データの中身が規則的に出力されていると言うことの様に思えるのですが。
Get #1,i,sName
Get #1,i + 12, VDbl
Get #1,i + 16, VLng
Get #1,i + 20, VInt
..として読み込んだデータの1レコード分を16進数でダンプで
提示してみて下さい。
その上で
>その中から特定の頭のタグとターミネーターを探し出してこの間に
>ある素性のわかったデータだけ取り出そうとしていますが
サンプルとしてのデータの中の
「特定の頭のタグ」と「ターミネーター」を書いて見て下さい。
8月さま、クイックレスポンスに感謝します。
元のバイナリデータはもちろんデタラメではないと思いますがサイズなど数多くのケースがあり周期性などが分からず、確実に分かったのはタグ(頭と終わり)とその間のデータの構造です。 これを探すのに
Dim i as Integer などなどの変数
i=0
do while i < Lof(i) − 30
I = I + 1
Get #1, I, B1
If B1 = 84 Then '頭を探す
Get #1, i + 25, B1
If B1 = 1 Then 'ターミネーターを探す(確認のため)
のようにして必要とするデータ区間を確定してからデータを取り出していました。質問時のコード i は頭の位置です。
8月さま。お世話になります。メールが時間的に行き違ったようで失礼しました。
現在はデータのあり場所はかなり正確に把握できています。そしてデータ部分の読み取りであれば各データのオフセットは分かっております。従って従来通りのGet #1,i,VLng でやればLong(例)を取れる状態にあります。現在わからないのはバイト配列からVLngなどを切り出す方法ということを質問させてもらったものです。例えば
sName = ""
For i2 = 1 to 16
sName = sName & Chr(buf(i + i2))
Next i2
D = "&H"
for i2 = 4 to 1 step -1
D = D & Hex(Buf(i+i2)
next i2
VLng = Val(D)
みたいにやれば取得できるのですが何ともダサいやり方ではないかと思いました。
実際のデータは
(String) 54 41 42 43 44 45 46 00 (54が頭で00が尻尾 ABCDEF)
(Long) 下記3データブロック
42 51 18 40 bc 4a 61 00 01 26 59 89 48 00 00 c0
6f 51 18 c0 6a 4c 61 00 01 52 5a 89 48 00 00 c0
90 51 18 00 44 4e 61 00 01 7e 5b 89 48 00 00 00
----------- ** ----------- ** **
-----------
** = マーカー
---------- = Long
というような構造です。よろしくお願いします。
ユーザー定義型を使うには1レコードの長さが固定である必要があります。
バイナリーファイルのサイズが1レコードの長さで割り切れますか?
ヘッダー部があるならそのサイズを引いた残りのサイズが1レコードの長さで割り切れますか?
ちなみに現在処理仕様としているバイナリーファイルのサイズは?
何レコードあると思われていますか?
> 実際のデータは
>(String) 54 41 42 43 44 45 46 00 (54が頭で00が尻尾 ABCDEF)
上記は下記の「3データブロック」の中には含まれていませんが
どう様に関連しているのでしょうか?
下記の先頭についている??
>(Long) 下記3データブロック
> 42 51 18 40 bc 4a 61 00 01 26 59 89 48 00 00 c0
> 6f 51 18 c0 6a 4c 61 00 01 52 5a 89 48 00 00 c0
> 90 51 18 00 44 4e 61 00 01 7e 5b 89 48 00 00 00
上記で3レコード分と言う事でしょうか?
VB6だと
Get #1,i,sName これは何バイト?
Get #1,i + 12, VDbl 8バイト
Get #1,i + 16, VLng 4バイト
Get #1,i + 20, VInt 2バイト...かと思うのですが。
私が書いて欲しかったのは例えば下記の様に
1レコード分のデータ構造です。
90 51 18 00 44 4e 61 00 01 7e 5b 89 48 00 00 00
----------------- ========== *********** +++++
sName VDbl VLng VInt
どなたか他の詳しい方にフォローしてもらった方が良いかも知れませんね。
先に書きましたのは下記の意味です。
41 42 43 44 45 46 =>String
90 51 18 00 =>Long
44 4e 61 00 =>Long
7e 5b 89 48 =>Long
Dim sName as String * 16 (1バイト文字x16)
オフセットが分かっていますので Get #1,i + 16, VLng などとやれば取得できています。 これをバイト配列で一括読み込みにしたいと思っていたものです。
>>>>>>>>> 正確に動くのですが遅いという欠点がありました。
>>>>>>>>> Dim sName as String * 16 (1バイト文字)
>>>>>>>>> sName = ""
>>>>>>>>> For i2 = 1 to 16
>>>>>>>>> sName = sName & Chr(buf(i + i2))
>>>>>>>>> Next i2
? そのような書き方はできないはずですが…。
固定長文字列型である以上、
Dim sName As String * 16
sName = ""
sName = sName & "A"
sName = sName & "B"
のようなコードを書いても、その結果は Space(16) になってしまうでしょうし。
>>> (String) 54 41 42 43 44 45 46 00 (54が頭で00が尻尾 ABCDEF)
これをバイナリ全体から探し出すために、InStrB は使えないでしょうか。
'54 41 42 43 44 45 46 00
Dim search() As Byte
search = StrConv("TABCDEF" & vbNullChar, vbFromUnicode)
Dim pos As Integer
pos = InStrB(1, buf, search, vbBinaryCompare)
>>> メールが時間的に行き違ったようで失礼しました。
掲示板以外にも、メールでのやりとりがあったのでしょうか?
>> ユーザー定義型を使うには1レコードの長さが固定である必要があります。
それは、ランダムアクセスの場合では無いでしょうか。
バイナリアクセスの場合は、そのような制限は無いと思います。
魔界の仮面弁士さん、
私には今までのやりとりでは
1レコードが固定長なのか、可変長なのか判断がつきかねます。
>>> メールが時間的に行き違ったようで失礼しました。
> 掲示板以外にも、メールでのやりとりがあったのでしょうか?
いえ、この掲示板以外のメールでのやり取り有りません。
ここでの書き込みを「メール」と誤記している様です。
>> ユーザー定義型を使うには1レコードの長さが固定である必要があります。
> それは、ランダムアクセスの場合では無いでしょうか。
> バイナリアクセスの場合は、そのような制限は無いと思います。
私のスキル不足のようです。失礼しました。
8月さま、魔界の仮面弁士さま レスをありがとうございます。
>>> メールが時間的に行き違ったようで失礼しました。
> 掲示板以外にも、メールでのやりとりがあったのでしょうか?
一生懸命考えて書いている間に8月さまからご回答をいただいてたということです。メールではなく書き込みです。失礼しました。
改めて最初の質問にもどさせていただきますと現在はバイト配列を下記のようにしてStringやLongにしようとしていたのですが1バイトづつ取り出して処理するという方法以外に何か上手い方法があったら教えていただきたいと質問したものです。
Dim sName as String (本来求めるStringは可変長なのですが最初の質問時に固定長としたのはGet #1,i,sNameという風に書いてsName = Left(sName, LStr)として取り出そうと考えたものです)
Dim D as String
Dim VLng as Long
Dim LStr as Integer
sName = ""
LStr = 8
For i2 = 1 to LStr
sName = sName & Chr(buf(i + i2))
Next i2
D = "&H"
for i2 = 4 to 1 step -1
D = D & Hex(Buf(i+i2)
next i2
VLng = Val(D)
私の知らないだけで世の中には1行で書けるような関数でもあるのではないかと思ったのですがなかなか難しそうだと実感しました。
バイト配列から文字列を探すInstrB関数は便利そうなので今後使っていきたいと思います。 ありがとうございました。
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Type L_Long
lng As Long
End Type
Private Sub Command1_Click()
Dim src(10) As Long
Dim dest As L_Long
src(0) = &H64
src(1) = 0
src(2) = 0
src(3) = 0
Call CopyMemory(dest, src(0), 4)
Debug.Print dest.lng
End Sub
こういう話でいいのかな?
#普段使ってるわけではないので間違ってたらごめん。
訂正
>Dim src(10) As Long
>Dim dest As L_Long
Dim src(10) As Byte
Dim dest As Long '構造体にしなくても別に良かったですね
Debug.Print dest
と 様。 色々とありがとうございました。最初はsrc(1)、src(2)などをいじっても数値が変わりませんでしたが下記のように src(10) as Byte にしてみたらおかげさまでLONGが得られました。これでLONGについてはOKですので同じ要領でDoubleなどを試してみます。オフセットがわかっていますのでこれで一発で答が出るようになるものと思います。 ありがとうございました。
'Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Sub Command1_Click()
Dim src(10) As Byte 'Long
Dim dest As Long
src(0) = &HA9
src(1) = &H2
src(2) = 0
src(3) = 0 '正解は681
Call CopyMemory(dest, src(0), 4)
Debug.Print dest
End Sub
追伸: バイト配列にして実装したところ以前のバイト毎の拾い読みに較べて比較にならないほど高速になりました。 皆様ありがとうございました。
編集 削除