VBで作成されたデータ保存用ファイルの内容をDelphiで読み込もうとしているのですが、なかなかうまくいかなくて質問させていただきました。
データの内容は以下のようになっています。
Type Person
a as string*12
b as string*5
・
・
e as string*8
f(6) as single
g(6) as single
end Type
文字列のデータは引き出せたのですが、配列型の数値の値が見当違いな値になってしまって困ってます。
type
dat=Record
a:array[1..12] of char;
b:array[1..5] of char;
・
・
e:array[1..12] of char;
f:array[1..6] of single;
g:array[1..6] of single;
end;
var
data1:file of dat;
data2:dat;
begin
AssignFile(data1,'ファイル名');
Reset(data1);
Read(data1,data2);
CloseFile(data1);
showmessage(floattostr(data2.f[0]));
end;
実際には30と出るはずが、156987E19などでたらめな値ででます。色々調べてみましたが、わからずこちらで質問さしていただきました。簡単なアドバイスでもいいので、賢明な方のご助言お願いします。
> e as string*8
> e:array[1..12] of char;
長さがあってないのでは?
> showmessage(floattostr(data2.f[0]));
f:array[1..6] of single;
と定義されていて、f[0]としているのは?
すみません、書き込み時にタイプミスしていました。
type
dat=Record
a:array[1..12] of char;
b:array[1..5] of char;
・
・
e:array[1..8] of char;
f:array[1..6] of single;
g:array[1..6] of single;
end;
var
data1:file of dat;
data2:dat;
begin
AssignFile(data1,'ファイル名');
Reset(data1);
Read(data1,data2);
CloseFile(data1);
showmessage(floattostr(data2.f[1]));
end;
f(6) as single
ならば、
f:array[0..6] of single;
か、
f:array[1..7] of single;
では?
もう1つ。
アラインメントはどうなってます?
構造体の途中が省略されていて解りませんが、Delphi側を、packed recordにしてみては?
にしのさん、返信ありがとうございます。
f(6) as singleの場合は配列数を7個用意しないと駄目なのですか?
また、アラインメントについては私の勉強不足でよくわかりません。アラインメント自体の意味がわかっていないので・・・。
ちなみに、packed recordにしてみましたが、値は変化しましたけど、得たい値ではありませんでした。
VBの場合の配列宣言は、宣言がなければ0から始まります。
F(6)はF(0)から始まり、F(6)まででは無いでしょうか?
実際のファイルをみるか、デバッグで内容をみればよく分かると思います。
VBの場合の配列宣言は、宣言がなければ0から始まります。
F(6)はF(0)から始まり、F(6)まででは無いでしょうか?
実際のファイルをみるか、デバッグで内容をみればよく分かると思います。
HOtaさん、返信ありがとうございます。
言われたことを調べてみたら、私の勘違いだったみたいですね。
配列数を7個にしたらきちんと動作しました!
どうもありがとうございます。
さしでがましいでしょうが、もうひとつ問題にぶつかってしまいました。
このファイルには、上述のデータが何個も入っています。1つぶんのデータを取り出すことはできましたが、2つ、3つと順番に取り出すにはどのようにすればよいでしょうか?改行などでデータが分かれていないのでどのように区別すればいいのか思案中です。
単純なシーケンシャルですよね。
今はどのように読み込んでいますか?
また、どのように書き込んでいますか?
通常は、書き込み時に
Dim wPerson As Person
・・・
Open ファイル名 For Random As #1 Len = Len(wPerson)
・・・
Put#1, レコード番号(1〜), wPerson
・・・
Put#1, レコード番号(1〜), wPerson
・・・
Close #1
とします。
あとは、Delphi側の読み込みは、
var
fs: TFileStream;
wPerson: Person;
begin
fs := nil;
try
fs := TFileStream.Create(ファイル名, fmOpenRead);
while fs.Read(wPerson, SizeOf(Person))>0 do
begin
end;
finally
if Assigned(fs) then FreeAndNil(fs);
end;
end;
というようにします。
アラインメントは、構造体の各要素の区切りの位置です。
たとえば、
type
TestRec1 = packed Record
a: char;
b: single;
c: char;
d: double;
end;
TestRec2 = Record
a: char;
b: single;
c: char;
d: double;
end;
という定義をして、
ShowMessage(IntToStr(SizeOf(TestRec1)));
ShowMessage(IntToStr(SizeOf(TestRec2)));
としてみてください。
それぞれ、14,24と出たと思います。
これは、
a: char; 1バイト
b: single; 4バイト
c: char; 1バイト
d: double; 8バイト
ですが、packed指定があると、各要素の間を詰められるため、14となります。
packed指定がないと、ワード境界、ダブルワード境界まで隙間ができます。
a〜bが4バイト。
b〜cが4バイト。
c〜dが8バイト。
確認するには、
Memo1.Lines.Add(IntToStr(Integer(PChar(@a.b)-PChar(@a.a))));
Memo1.Lines.Add(IntToStr(Integer(PChar(@a.c)-PChar(@a.b))));
Memo1.Lines.Add(IntToStr(Integer(PChar(@a.d)-PChar(@a.c))));
というように、ポインタの差を見れば解ります。
にしのさん、返信ありがとうございます。
VBのほうの書き込みはにしのさんの書かれているのと同じです。
読み込みは最初にも書きましたが
type
dat=Record
a:array[1..12] of char;
b:array[1..5] of char;
・
・
e:array[1..12] of char;
f:array[1..7] of single;
g:array[1..7] of single;
end;
var
data1:file of dat;
data2:dat;
i:integer;
begin
AssignFile(data1,'ファイル名');
Reset(data1);
for i:=1 to 10 do
begin
Read(data1,data2);
showmessage(floattostr(data2.f[1]));
end;
CloseFile(data1);
end;
このようになっています。
1つ目は読めるのですが、1つ目のデータの終わりから2つ目のデータの始めまでに空白がかなりあります。
その空白領域を読み込む変数を構造体に追加して無理やりやってみたのですが、最後のデータにはその空白がないために、「ファイルの末尾以降を読み込みました」でエラーになります。TFileStreamなどの使い方をまだよく理解していないのでこんなやりかたになっています。
アライメントの話、ありがとうございました。
よく読んで勉強さしていただきます。
Reset手続きの第2引数を省略していますが、構造体の大きさは128で良いのでしょうか。
省略すると128ですよ。
失礼しました。型つきFILEでしたね。
こちらで試してみても再現しません。正しく読み込めます。
構造体のサイズは本当にあっていますか?
VB側で1つのデータだけ出力し、そのファイルサイズを調べてみてください。
次に、delphi側で、ShowMessage(IntToStr(SizeOf(Person)));などとして、構造体のサイズを調べてみてください。
同じでなければ、何かが違います。ここが同じにならなければ正しく読み込めません。
にしのさん、返信送れてすいません。
構造体のサイズは1つ目のデータはきちんと取り込めているのであっていると思います。ですが、1つのデータだけ出力した時のファイルサイズを調べるとVBでは構造体のサイズより何バイトか大きくでます。これが前述した空白といっていた部分です。
[1つ目のデータ] [2つ目のデータ] [3つ目のデータ]
このような感じです。また、この空白部分は最後のデータの後には入っていないのです。最後のデータの場合だけ、例外的に処理しようかなと考えていますが、何かいい方法あるでしょうか?
出力の時、
Open ファイル名 For Random As #1 Len=構造体のサイズ
としているんですよね?
# Lenが肝です
1つめのデータが取れているのは、開始位置が0で同じだからです。
2つめのデータでずれていくと言うことは、出力サイズと取得サイズが違うからです。
これでは合っているとは思えません。
まずは、先に投稿したとおり、構造体のサイズを合わせてください。
合っていれば、そんなに難しい物ではありません。
隙間がほしいのであれば、構造体自体に隙間分の変数(ダミーの)を用意するべきです。
>この空白部分は最後のデータの後には入っていないのです。
構造体のサイズがあってないんじゃなくて、本当に隙間なのでは?
隙間であれば、明示的に出力した方がよいですね。
たぶんですが、OpenのときにLenが指定されておらず、たとえば
Len=10, 構造体のサイズ5のとき、
ABCDE_____FGHIJ
# _が隙間
というように、構造体のサイズは正しいが、次レコードとの間に隙間が発生しているということだと思います。
ちゃんとサイズ確認しました?
2レコード出力して、1レコードの倍のサイズになってます?
こういうときは、出来ることから確認しないと。
あれから教えていただいたことを参考にやってみたところ、なんとか最後のデータ以外は取り出せるようになりました。なぜか最後のデータだけ、隙間の部分がないために、構造体のサイズが異なり取り出せません。
今回は、プログラムではなく別の方法でなんとかすることになったのでこれで解決とさせていただきます。
ご教授してくださいましたみなさま、ありがとうございました。
チェック忘れてました
ツイート | ![]() |