ランダムファイルをアクセスする方法を記載します
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[]から文字列型に変換する必要が出たりする。