ランダムファイルをアクセスする方法を記載します
FileStreamによるアクセス
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
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とうにメモリーコピーする必要が出てくる。
ランダムアクセス関数例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
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); } } |
呼び出し側
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
[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[]から文字列型に変換する必要が出たりする。