- PR -

バイナリファイルを構造体に読み込むには

1
投稿者投稿内容
mal
会議室デビュー日: 2005/10/12
投稿数: 6
投稿日時: 2005-10-13 19:12
こんばんは、
初歩的な質問で申し訳ありません。
C#でバイナリファイルを構造体に書き込むには
どの様にしたら良いか考えています。

Cだと↓の様な感じにしたいのですが・・・。

typedef struct t_meibo
{
  char name[ 10 ];
char add[30];
  int tel;
};

FILE *fp = fopen( "meibo.bin", "rb" );

t_meibo meibo;
fread( &meibo, 1, sizeof(t_meibo), fp );

fclose( fp );

BinaryReaderを使って読むしか方法はないのでしょうか?
どなたかご存知の方教えてください。
宜しくお願いします。
一郎
ぬし
会議室デビュー日: 2002/10/11
投稿数: 1081
投稿日時: 2005-10-13 20:24
構造体に書き込むというのは.NETの"構造体"のことではなくて、C言語で言う構造体のイメージですよね?

う〜ん、unsafeとか使うとできるのかなぁ。
どうしてそうしたいのかお聞きしたい所です。
甕星
ぬし
会議室デビュー日: 2003/03/07
投稿数: 1185
お住まい・勤務地: 湖の見える丘の上
投稿日時: 2005-10-13 21:14
unsafeコードを使えば出来るはずだけど、ここはマネージドコードの流儀に従うことをお勧めします。

System.IO.BinaryReaderを使って、↓な感じ
コード:
    System.IO.BinaryReader binRead;
    rec.name = binRead.ReadChars(20);
    rec.add  = binRead.ReadChars(30);
    rec.tel  = binRead.ReadInt32();


#コンパイルエラーになっても、突っ込み禁止
sia
常連さん
会議室デビュー日: 2004/05/02
投稿数: 38
投稿日時: 2005-10-13 21:33
画像フォーマットの解析などでC構造体の読み込みを
行ったことはありますがStructLayoutAttributeで構造体パッキング規則
にあわせた小細工をしないといけないので
甕星さんが仰っているように、
BinaryReaderを使った方が断然楽(というか無難)だと思います。




[ メッセージ編集済み 編集者: sia 編集日時 2005-10-13 21:40 ]
mal
会議室デビュー日: 2005/10/12
投稿数: 6
投稿日時: 2005-10-14 09:22
おはようございます。malです。
みなさまお返事ありがとうございます。

引用:

どうしてそうしたいのかお聞きしたい所です。


実は構造体の項目が70近く存在するのです。
これをBinaryReaderを使用して、ちょこちょこやるのはなぁ・・・。
と思い、調査していたのですがどうすることもできず
投稿させて頂いた次第です。


甕星さんの方法は、完璧でした。
ありがとうございます。


只今、「Marshal.PtrToStructure」を使用して
どうにかできないか調査中でございます。
_________________
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2005-10-15 15:50
こんにちは。

BinaryFormatter を使うというのはどうでしょうか。
バイナリファイルが別の方法で作成されているのならば、無理ですが。

Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2005-10-15 18:07
予め断っておきますが、全くお薦めしない方法です。こういうやり方もあるんだな、程度に聞き流してください。

Win32APIのReadFile関数はファイルハンドルを受け取って指定されたポインタにバイト列を書き込みます。
.NETのFileStreamクラスにはHandleプロパティ(.NET 2.0ではSafeFileHandleプロパティ)があり、これはそのままWindowsのファイルハンドルを表します。
この二つを組み合わせれば、構造体に直接値を書き込むことができます。
もちろん、Packに注意したり(.NETでのデフォルトのパッキングサイズは8だったと思います)、固定長配列をMarshalAs属性で表現したり、と気を配ることも多いですが。
参考:
アンマネージ DLL 関数の処理
FindFile のサンプル

Stream/FileStreamのメンバにはポインタ型を受け取って読み取るメソッドはないので、Marshal.PtrToStructureを使うにせよunsafeコンテキストを使うにせよ、一旦byte[]でバッファリングする必要があります。
Marshal.PtrToStructureを使う場合、バッファであるbyte[]からさらにポインタ(.NETではIntPtr型で表します)を作る必要があります。
Marshal.AllocCoTaskMemなどを使用して独自にメモリ領域を作り、そこにMarshal.Copyするという手段もありますが、それよりはGCHandleクラスAllocメソッドを使用して配列を固定し、AddrOfPinnedObjectメソッドでアドレスを取得する、という手段の方がスムーズでしょう。

unsafeコンテキストを使うのは、.NET 1.0/1.1では難しいものがあります。構造体内の固定長配列を表現する手段が極めて限定的だからです。
//.NET 2.0では、unsafeつきの構造体に限り、fixedキーワードを使用することで固定長配列を表現できるようになりました。
なおMarshalAs属性はあくまでアンマネージドとのマーシャリングに使われるもので、unsafeコンテキストとは関係のない属性です。
//Marshal.PtrToStructureはアンマネージドとの相互運用のためという前提の元で変換するため、マーシャリングを行うのでMarshalAs属性は有効です。
一応、必要な数だけフィールドを並べたり
コード:
Cの構造体
typedef struct {
    char name[2];
} TEST;
C#の構造体
public struct Test {
    public byte name0;
    public byte name1;
    //引数付きプロパティがこういうときだけは欲しい……
}


別構造体にしてインデクサを実装してみたり、といった回避手段はありますが、面倒です。
これをなんとか定義したのなら、unsafe内でバッファのbyte[]をfixedステートメントで固定したあとはCと同じノリでかけるでしょう。

以上、だらだら書き連ねてみましたが、初めに言ったとおり全くお薦めはしません。
.NETでなら.NETの流儀に従うのが自然かと思います。


ところで、Cのchar型はC#ではbyteになりますが大丈夫ですか? charは2バイトですよ?


//リンクを無意味に多用してみるテスト
mal
会議室デビュー日: 2005/10/12
投稿数: 6
投稿日時: 2005-10-17 10:07
囚人さん、Hongliangさんありがとうございます。

一応「Marshal.PtrToStructure」を
使う方法で試した結果うまく行きました。
サンプルソースは書きです。
−−−−−−−−−−−−−−−−−−−−−−−−−
[StructLayout(LayoutKind.Sequential, Pack=1)]
 public struct t_logdt
 {
  public short key;
  public int logdat;
 }

 public static t_logdt Read( string FileName )
 {
  using( Stream s = new FileStream( FileName, FileMode.Open ) )
  {
   byte[] buf = new byte[ Marshal.SizeOf( typeof(t_logdt) ) ];
s.Read( buf, 0, buf.Length );

   IntPtr ptr = Marshal.AllocHGlobal( buf.Length );
   try
   {
Marshal.Copy( buf, 0, ptr, buf.Length );
t_logdt logdt =
           (t_logdt)Marshal.PtrToStructure( ptr, typeof(t_logdt) );
return logdt;
   }
   finally
   {
Marshal.FreeHGlobal( ptr );
   }
  }
 }
−−−−−−−−−−−−−−−−−−−−−−−−−
今日は、囚人さんのBinaryFormatterを使用する方法と
Hongliangさんの
引用:

それよりはGCHandleクラスのAllocメソッドを使用して配列を固定し、AddrOfPinnedObjectメソッドでアドレスを取得する、という手段の方がスムーズでしょう。


を試したいと思います。

引用:

以上、だらだら書き連ねてみましたが、初めに言ったとおり全くお薦めはしません。
.NETでなら.NETの流儀に従うのが自然かと思います。


そうですよねぇ・・・・。
後から、痛い目見るのは自分なんですけど・・・。
とりあえず、こんなこともできるんだ程度に試したいと思います。

1

スキルアップ/キャリアアップ(JOB@IT)