ランダムファイルをアクセスする方法を記載します
FileStreamによるアクセス
string FilePath = @"C:\work\myrfile.txt"; int RecodeLength = 15; int RecNo = 1; byte[] buffer = new byte[RecodeLength]; System.IO.FileStream fs = new System.IO.FileStream(FilePath, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite); //レコード数取得(Rows) long RecodeCount = fs.Length / RecodeLength; //レコード読み込み(Get) fs.Position = RecodeLength * (RecNo - 1); //Seek()でも出来る fs.Read(buffer, 0, RecodeLength); //レコード書き込み(Put) fs.Position = RecodeLength * (RecNo - 1); fs.Write(buffer, 0, RecodeLength); fs.Close();
・FileStreamの場合byteで扱うので、その後の処理をするとき、byte[]をstructとうにメモリーコピーする必要が出てくる。
ランダムアクセス関数例
class RandomFile : IDisposable { private FileStream _FStream = null; //ファイルストリーム private int _RecodeLength = 1; //レコード長(デフォルト) private Type _Type = null; //構造体タイプ(構造体IO用) private byte[] _buffer; //IO用中間バッファ(構造体IO用) /// <summary> /// コンストラクタ /// </summary> public RandomFile() { } private bool disposed = false; //重複する呼び出しの制御 private void Dispose(bool disposing) { if (disposed) //既に解放済みかチェック return; if (disposing) { } Close(); disposed = true; //解放済みフラグを立てる } public virtual void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// <summary> /// デストラクタ /// </summary> ~RandomFile() { Dispose(false); } /// <summary> /// ファイルオープン /// </summary> /// <param name="fpath">ファイル名</param> /// <param name="RecodeLength">レコード長</param> /// <returns></returns> public void Open(string fpath, int RecodeLength) { _FStream = new System.IO.FileStream(fpath, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite); if (0 < RecodeLength) //レコード長 { _RecodeLength = RecodeLength; } } /// <summary> /// ファイルオープン構造体IO用 /// </summary> /// <param name="fpath"></param> /// <param name="obj"></param> public void Open(string fpath, object obj) { _FStream = new System.IO.FileStream(fpath, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite); _RecodeLength = Marshal.SizeOf(obj); _buffer = new byte[_RecodeLength]; _Type = obj.GetType(); } /// <summary> /// ファイルクローズ /// </summary> /// <returns></returns> public void Close() { if (_FStream != null) { _FStream.Close(); _FStream = null; } _buffer = null; _Type = null; } /// <summary> /// /// </summary> /// <returns></returns> public long Rows() { long count = -1; if (_FStream != null) { count = _FStream.Length / (long)_RecodeLength; } return (count); } /// <summary> /// 1レコード読み込む /// </summary> /// <param name="buffer">読み込むバッファ</param> /// <param name="RecordNumber">レコード番号</param> /// <returns></returns> public void Get(byte[] buffer, long RecordNumber) { if (0 < RecordNumber) { _FStream.Position = _RecodeLength * (RecordNumber - 1); } else { _FStream.Position = 0; //先頭行にする } _FStream.Read(buffer, 0, _RecodeLength); } /// <summary> /// 1レコード書き込む /// </summary> /// <param name="buffer">書き込むバッファ</param> /// <param name="RecordNumber">レコード番号</param> /// <returns></returns> public void Put(byte[] buffer, long RecordNumber = 0) { if (0 < RecordNumber) { _FStream.Position = _RecodeLength * (RecordNumber - 1); } else { _FStream.Position = _RecodeLength * this.Rows(); //最終行にする } _FStream.Write(buffer, 0, _RecodeLength); } /// <summary> /// 構造体を1レコード読み込む /// </summary> /// <param name="objType">構造体タイプ</param> /// <param name="RecordNumber">レコード番号</param> /// <returns></returns> public object Get(long RecordNumber) { this.Get(_buffer, RecordNumber); GCHandle gch = GCHandle.Alloc(_buffer, GCHandleType.Pinned); object obj = Marshal.PtrToStructure(gch.AddrOfPinnedObject(), _Type); gch.Free(); } /// <summary> /// 構造体を1レコード書き込む /// </summary> /// <param name="obj">構造体</param> /// <param name="RecordNumber">レコード番号</param> /// <returns></returns> public void Put(object obj, long RecordNumber = 0) { GCHandle gch = GCHandle.Alloc(_buffer, GCHandleType.Pinned); Marshal.StructureToPtr(obj, gch.AddrOfPinnedObject(), false); gch.Free(); this.Put(_buffer, RecordNumber); } }
呼び出し側
[StructLayout(LayoutKind.Sequential, Pack = 1)] unsafe struct Person { public fixed byte name[10]; public fixed byte Age[5]; } または [StructLayout(LayoutKind.Sequential, Pack = 1)] //メンバーを定義順に格納する public struct Person { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] //byte[10]で格納 public byte[] name; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] //byte[5]で格納 public byte[] Age; } string FilePath = @"C:\work\myrfile.txt"; using (RandomFile rf = new RandomFile()) { rf.Open(FilePath, new Person()); Person pos = (Person)rf.Get(2); string s = System.Text.Encoding.GetEncoding(932).GetString(pos.name); rf.Put(pos); rf.Close(); }
・長さを固定するために構造体の書き方に工夫がいる、メモリ中では、速度を上げるために、バイト単位で確保するより、レジスタ単位で確保したほうが速度が速くなるため、コンパイラ等が最適な並びで確保するので、並び等を指定する必要が出てくる。のでMarshalAs、StructLayoutAttribute、UnmanagedTypeで指定する。
・構造体から値を取り出す場合にはByte[]から文字列型に変換する必要が出たりする。