型なしファイルでデータを書き込むには


チャーリー  2004-09-24 05:53:03  No: 11059

こんにちは。
ボタンをクリックするとMemoに書いているCommentを登録するというプログラムを作りたいと思っています。
データを読み込むときはYearなどを判定してCommentを読み込むようにします。
下のコードは書き込みのコードで、YearComment.datを見ると書き込めているようなのですが、レコード型メンバの中でCommentしかデータに書き込まれていないようなので、レコード型のメンバを全部登録するにはどうすればいいのか教えてください。
-------------------------------------------
procedure WriteComment(Year,Comment:string);
const
  csFile='YearComment.dat';
type
  TYearComment = record
    Year:array[0..3] of Char;//年
    Month:array[0..1]of Char;//月
    Day:array[0..1]of Char;//日    
Comment:string;
  end;
var
 F:file;//型なしファイル
  dat:TYearComment;
  sFolder:string;
  i,Len:integer;
begin
//フォルダを強制的に作成する
  sFolder:=GetTopFolder +sFolder;
  ForceDirectories(sFolder);
  //ファイル関連づけ
  AssignFile(F, sFolder+csFile);
  if (FileExists(sFolder+csFile)) then
    Reset(F)//ファイルオープン
  else
    Rewrite(F);//新規
  try
  //コメントが更新されていたらデータ書き込み
    if(Comment<>dat.Comment)then begin
      Len:=Length(dat.Comment);//dat.Commentの長さ(バイト数)を格納
      Seek(F,FilePos(F)-1);
      BlockWrite(F,Len,SizeOf(Len));//Lenをファイルに書き込み
      BlockWrite(F,PChar(dat.Comment)^,Len);
      Showmessage('登録完了');
    end else

  finally
    CloseFile(F);
  end;
end;


jok  2004-09-24 07:20:00  No: 11060

>レコード型メンバの中でCommentしかデータに書き込まれていないようなので、
>レコード型のメンバを全部登録するにはどうすればいいのか教えてください。

>      BlockWrite(F,Len,SizeOf(Len));//Lenをファイルに書き込み
>      BlockWrite(F,PChar(dat.Comment)^,Len);

長さと dat.Comment 部分しか書き込んでいないので当然では?


チャーリー  2004-09-24 23:21:38  No: 11061

こんにちは。
>レコード型メンバの中でCommentしかデータに書き込まれていないようなので、
>レコード型のメンバを全部登録するにはどうすればいいのか教えてください。

>      BlockWrite(F,Len,SizeOf(Len));//Lenをファイルに書き込み
>      BlockWrite(F,PChar(dat.Comment)^,Len);
コメント部分が更新されていなければ、レコードはそのままにする。メンバに値を代入してそれを登録したいのですが、
Year:=Format('%.4s',[dat.Year]);
Month:=Format('%.2s',[dat.Month]);
Day:=Format('%.2s',[dat.Day]);
メンバに値を代入してから、データに書き込む場合BlockWriteにどのように書けばいいのでしょうか。


jok  2004-09-25 00:34:26  No: 11062

複雑なことを考えるよりさきに、まず最低できなければならなことは、ファイルの
読み書きだと思います。それ用の関数を作ってみました。Year などは単純にする
ために Word 型にしています。参考にしてください。

type
TYearComment = record
  Year:Word;//年
  Month:Word;//月
  Day:Word;//日
  Comment:string;
end;

function WriteYC(Filename:string;YC:TYearComment):Boolean;
var
  FS:TFileStream;
  L:integer;
begin
  result := false;
  FS := TFileStream.Create(Filename,fmCreate or fmShareDenyWrite);
  try
    try
      FS.Write(YC.Year,SizeOf(Word));
      FS.Write(YC.Month,SizeOf(Word));
      FS.Write(YC.Day,SizeOf(Word));
      L := Length(YC.Comment);
      FS.Write(L,SizeOf(integer));
      FS.Write(YC.Comment[1],L);
    except
      exit;
    end;
  finally
    FS.Free;
  end;
  result := true;
end;

function ReadYC(Filename:string;var YC:TYearComment):Boolean;
var
  FS:TFileStream;
  L:integer;
begin
  result := false;
  FS := TFileStream.Create(Filename,fmOpenRead or fmShareDenyWrite);
  try
    try
      FS.Read(YC.Year,SizeOf(Word));
      FS.Read(YC.Month,SizeOf(Word));
      FS.Read(YC.Day,SizeOf(Word));
      FS.Read(L,SizeOf(integer));
      SetLength(YC.Comment,L);
      FS.Read(YC.Comment[1],L);
    except
      exit;
    end;
  finally
    FS.Free;
  end;
  result := true;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  YC:TYearComment;
  Year,Month,Day:Word;
begin
  DecodeDate(Now,Year,Month,Day);
  YC.Year := Year;
  YC.Month := Month;
  YC.Day := Day;
  YC.Comment := Memo1.Text;
  WriteYC('c:\Test.txt',YC);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  YC:TYearComment;
begin
  if ReadYC('c:\Test.txt',YC) then
  begin
    Memo2.Lines.Add(IntToStr(YC.Year));
    Memo2.Lines.Add(IntToStr(YC.Month));
    Memo2.Lines.Add(IntToStr(YC.Day));
    Memo2.Lines.Add(YC.Comment);
  end;
end;


チャーリー  2004-10-05 06:58:45  No: 11063

こんばんは。
コード参考にしたいと思います。
型変換部分が分からないのですが、
レコード型を
--------------
TYearComment = record
    Year:array[0..3] of Char;//年
    Month:array[0..1]of Char;//月
    Day:array[0..1]of Char;//日 
--------------
にしたい場合
-----------
try
    try
      FS.Read(YC.Year,SizeOf(★));
      FS.Read(YC.Month,SizeOf(★));
      FS.Read(YC.Day,SizeOf(★));
------------
★部分のサイズを調べるにはどの型を入れればいいのでしょうか。
-----------


花より団子  2004-10-05 23:33:23  No: 11064

★部分に入れるのは、型より変数。


bob  2004-10-06 21:45:06  No: 11065

>コード参考にしたいと思います。
の割には、Char配列にこだわるんですね。

レコード型を
--------------
>TYearComment = record
>    Year:array[0..3] of Char;//年
>    Month:array[0..1]of Char;//月
>    Day:array[0..1]of Char;//日 
定数で指定しているわけじゃないので、

>      FS.Read(YC.Year,  4);
>      FS.Read(YC.Month, 2);
>      FS.Read(YC.Day, 2);

で良いのではないですか?


チャーリー  2004-10-20 22:20:03  No: 11066

こんにちは。
StringGridのOption-goEditing,goAlwaysShowEditorで、StringGrid[2,i+1]文字を書き込み、そのセルに書き込んでいるデータを========================================
From: チャーリー
Date: 2004/10/20(水) 13:24:38

途中で切れてしまいました。続けて書き込みます。すみません。
-----------------------------------------------------
StringGridのOption-goEditing,goAlwaysShowEditorで、StringGrid[2,i+1]ni
文字を書き込み、そのセルに書き込んでいるデータをTFileStreamをつかって保存、読み込みしたい。
---------------
レコード形式で保存したい。
TComment=record
  Bamei:string;//馬名
  Comment:string;//コメント
end;
---------------
iはその日の出走頭数とする。
StringGrid1[1,i+1]:='馬名';
StringGrid2[2,i+1]:='コメント';
----------------
馬名が一致すれば読み込むときにコメントを表示させたい。


チャーリー  2004-10-26 08:06:10  No: 11067

jokさんの関数を参考にして書き込みのところを以下のようにしてみたのですが、上書きされてしまいます。上書きせずにデータを蓄積するにはどうすればいいのでしょうか。
------------------------------------------------------------------
procedure TForm3.ToolButton6Click(Sender: TObject);
var
  TCC:TComment;
  i,nTosu,Code:integer;
begin
  val(den1.TorokuTosu,nTosu,Code);
  for i:=0 to nTosu-1 do begin
    if(StringGrid2.Cells[10,i+1]<>'')then begin//コメントが空白でなければ
      TCC.Bamei:=StringGrid2.Cells[3,i+1];
      //Showmessage(TCC.Bamei);OK
      TCC.Comment:=StringGrid2.Cells[10,i+1];
      //Showmessage(TCC.Comment);OK
      WriteComment('D:\ソフトTXT\Comment.txt',TCC);
    end;
  end;
end;
------------------------------------------------------------------


HOta  2004-10-26 15:22:35  No: 11068

途中で質問が変わってしまっていますね。
レコードが一つですから、上書きになります。

何を質問したいのでしょうか?
どういう状態にしたいのでしょうか?


チャーリー  2004-10-26 21:58:06  No: 11069

こんにちは。
発言を変えて投稿し直したのですが、掲示板のエラー等で重なってしまいました。すみません。
TFileStreamを使って読み書きしたいのですが、レコードが一つなので上書きになるということなので、
--------------
TComment=record
  Bamei:string;//馬名
  Comment:string;//コメント
end;
--------------
このレコード型の設定に問題があるのでしょうか。

StringGridにコメントが書いてあれば、そのコメントを登録していきたいのです。
現在は以下のように2つデータがあるとすれば、登録したテキストファイルをみると、「さくら  はやい」と上書きされている状態です。これを
「ナリタ  つよい
  サクラ  はやい」
というようにデータを追加していきたいのです。
-----------
ナリタ:つよい。
--------------
サクラ:はやい。
-------------


HOta  2004-10-26 22:27:28  No: 11070

チャーリーさん  こんにちは
WriteCommen手続きで書き込んでいると思いますが、
どのようにしているのでしょうか?


チャーリー  2004-10-26 23:12:41  No: 11071

こんにちは。WriteComment手続きは以下のようにしています。
-----------------------------
function WriteComment(Filename:string;TCC:TComment):Boolean;
var
  FS:TFileStream;
  L:integer;
begin
  result := false;
  FS:=TFileStream.Create(Filename,fmCreate);
  //or fmShareDenyWrite);
  try
    try
      L := Length(TCC.Bamei);
      FS.Write(L,SizeOf(L));
      FS.Write(PChar(TCC.Bamei)^,L);
      L := Length(TCC.Comment);
      FS.Write(L,SizeOf(L));
      FS.Write(PChar(TCC.Comment)^,L);
      Showmessage('登録完了');
    except
      exit;
    end;
  finally
    FS.Free;//フリー
  end;
  result := true;
end;
-----------------------------


HOta  2004-10-27 02:17:35  No: 11072

チャーリーさん  こんにちは
WriteCommen手続きでTFileStreamを常にfmCreateで
Createしていますので、そのたびに新しいファイルとして
作成されて今までのデーターが消えています。
ファイルに追加する場合は、fmOpenReadWriteで開き、
TFileStreamの最後までseekしてから書き込みます。

  try
    FS:=TFileStream.Create(Filename,fmOpenReadWrite);
    FS.Seek(FS.Size,soFromBeginning);
  except
    FS:=TFileStream.Create(Filename,fmCreate);
  end;


チャーリー  2004-10-27 22:17:19  No: 11073

こんにちは。
except以降は例外処理でファイルがない場合fmCreateが実行されるはずなのですが、手作業で書き込まれていないテキストファイル[Comment.txt]を用意しておかないと、fmOpenReadWrite部分でエラーとなってしまいます。
全くファイルがない状態で、fmCreateして、ファイルがある場合はfmOpenReadWriteさせるにはどうしたらいいのでしょうか?
・・・教えてもらったコードがそうなっていると思うのですが、エラーが出てしまうので。。


ふぐちゃん  2004-10-27 22:45:43  No: 11074

チャーリーさん、こんにちは。
このスレッドを読んでいて少々疑問に思ったのですが、レコード型のデータの管理は
どうされているのでしょうか?
TList型のオブジェクトなどは用意されていますか?


HOta  2004-10-27 22:56:09  No: 11075

チャーリーさん  こんにちは

  try
    
FS:=TFileStream.Create(Filename,fmOpenReadWrite);
    FS.Seek(FS.Size,soFromBeginning);
  except
    //ファイルが無い場合エラーになり、新規に作成する。
    FS:=TFileStream.Create(Filename,fmCreate);    
  end;


HOta  2004-10-27 22:59:10  No: 11076

チャーリーさん  こんにちは
ありゃりゃ途中で送信してしまった。

ファイルをmOpenReadWriteで開き、エラーの場合は、fmCreateで開く
様に書いています。ファイルが無い場合は、エラーを起こして
except句へ飛びます。


りりか  2004-10-28 00:41:26  No: 11077

FileExists 関数を使えば簡単


チャーリー  2004-10-28 00:47:38  No: 11078

ふぐちゃんさん、HOtaさんこんにちは。
TList型のオブジェクトとか用意していません。
今後のことを考えれば、馬名のあいうえお順に並んでいた方がテキストを見たときに見やすいですし、配布しやすいなと思っていたりします。そうした場合、TFileStreamで読み書きするより別にいい方法ありますかね?文字数制限なしでデータを登録して。。

HOtaさん。
コンパイル中にエラーが出るのは正常だということですね。
コンパイル中エラーが出て止まってしまうので・・・エラーが出ない方法あるのかなと思ったのですが、コードはちゃんと例外部分書かれているので問題ないとおもいますし。いろいろ試してみます。また読み込みでも悩みそうです。


ふぐちゃん  2004-10-28 01:19:58  No: 11079

りりかさんが指摘されているようにFileExists関数を使う方法もあります。

var
  Mode: Word;
  FS: TFileStream;
begin
  if FileExists(FileName) then
    Mode := fmOpenReadWrite or fmShareDenyWrite
  else
    Mode := fmCreate or fmShareDenyWrite;

  FS := TFileStream.Create(FileName, Mode);
  try
    { 処理 }
  finally
    FS.Free;
  end;
end;

> TList型のオブジェクトとか用意していません。
そうなんですか。
そうしますと、複数のデータはどういう形で保持する仕様ですか?
入出力よりも先にまず仕様を明確にしておかれた方がよいのではないですか?


HOta  2004-10-28 05:18:29  No: 11080

チャーリーさん  こんにちは
実行時エラーの話です。
コンパイルエラーはどの様なエラーが出ていますか?


チャーリー  2004-10-28 08:01:59  No: 11081

HOtaさん。
エラー内容は、「プロジェクト○○○が、EFOpenErrorクラスの例外を生成しました。指定されたファイルが見つかりません。」というものです。

ふぐちゃんさん。
複数のデータをどういう形で保持するか。ソートさせたりテキストファイルで配布するというのを考えた場合、普通皆さんどのようになさるのでしょうか。


りりか  2004-10-28 08:41:31  No: 11082

> ソートさせたりテキストファイルで配布するというのを考えた場合

TComment=record
  Bamei:string;//馬名
  Comment:string;//コメント
end;

こんなデータなら、2行のテキストですから簡単です。レコード型ではなく
クラスにします。

TComment = class(TObject)
  Bamei:string;//馬名
  Comment:string;//コメント
end;

そして、TObjectList で管理します。追加。挿入、削除が簡単です。全リストを
示します。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Contnrs, StdCtrls;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    ol:TObjectList;
  end;

TComment = class(TObject)
  Bamei:string;//馬名
  Comment:string;//コメント
end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  ol := TObjectList.Create(true);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  ol.Free;
end;

// データ4つ追加
procedure TForm1.Button1Click(Sender: TObject);
var
  cm:TComment;
begin
  cm := TComment.Create;
  cm.Bamei := 'はるうらら';
  cm.Comment := 'はるが好き';
  ol.Add(cm);

  cm := TComment.Create;
  cm.Bamei := 'はいせいこー';
  cm.Comment := 'わからん';
  ol.Add(cm);

  cm := TComment.Create;
  cm.Bamei := 'とうしょうぼーい';
  cm.Comment := 'よくわからん';
  ol.Add(cm);

  cm := TComment.Create;
  cm.Bamei := 'あきうらら';
  cm.Comment := '秋がすき';
  ol.Add(cm);
end;

// 一覧表示
procedure TForm1.Button2Click(Sender: TObject);
var
  i:integer;
begin
  Memo1.Clear;
  for i := 0 to ol.Count-1 do
    with TComment(ol[i]) do
      Memo1.Lines.Add(Format(' 馬名 : %s    コメント : %s',[Bamei,Comment]));
end;

// 馬名でソート

function BameiSort(Item1, Item2: Pointer): integer;
begin
  result := AnsiCompareStr(TComment(Item1).Bamei,TComment(Item2).Bamei);
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  ol.Sort(BameiSort);
  Button2Click(Sender);
end;

// ファイルにセーブ
procedure TForm1.Button4Click(Sender: TObject);
var
  sl:TStringList;
  i:integer;
begin
  sl := TStringList.Create;
  try
    for i := 0 to ol.Count-1 do begin
      sl.Add(TComment(ol[i]).Bamei);
      sl.Add(TComment(ol[i]).Comment);
    end;
    sl.SaveToFile('c:TestBamei.txt');
  finally
    sl.Free;
  end;
end;

// ファイルからロード
procedure TForm1.Button5Click(Sender: TObject);
var
  sl:TStringList;
  i:integer;
  cm:TComment;
begin
  ol.Clear;
  sl := TStringList.Create;
  try
    sl.LoadFromFile('c:TestBamei.txt');
    for i := 0 to (sl.Count div 2) -1 do
    begin
      cm := TComment.Create;
      cm.Bamei := sl[i*2];
      cm.Comment := sl[i*2+1];
      ol.Add(cm);
    end;
  finally
    sl.Free;
  end;
  Button2Click(Sender);
end;

end.


りりか  2004-10-28 08:47:54  No: 11083

すみません、ファイル名が変でした。

'c:\TestBamei.txt'

にしてください。


りりか  2004-10-28 09:23:51  No: 11084

あっ、それから馬名はともかく、もしコメントが2行以上、つまり改行がある場合は
ファイルアクセスをもう少し工夫する必要があります。


HOta  2004-10-28 16:42:10  No: 11085

チャーリーさん  こんにちは
「プロジェクト○○○が、EFOpenErrorクラスの例外を生成しました。指定されたファイルが見つかりません。」というエラーはコンパイル時に出るのですか?実行時ではないですか?


チャーリー  2004-10-29 02:33:32  No: 11086

りりかさん。
現プログラムでの登録の仕方等が違うので悩んでしまいそうですが、参考にさせて頂きます。
HOtaさん。
F9で実行し、アプリケーションが立ち上がって書き込みしようとするときです。実行時です。
TFileStream読み込みの場合ですけれど、馬名が一致したらコメントを表示させる場合、TFileStreamのReadとSeekをどのように書けばいいのでしょうか。


りりか  2004-10-29 03:15:10  No: 11087

>登録の仕方等が違うので

登録の仕方の違いなんて大した意味があるとは思いませんけど。
データのリストを管理し、ソートし、ファイルアクセスがテキストで出来る方が
ずっと重要だと思います。例のコードの TForm1.Button1Click() を見ると、
データの追加の仕方なんて簡単です。

>馬名が一致したらコメントを表示させる場合

これだって、リストを検索するのは簡単です。ファイルを先頭から舐めて検索する
のはいかにも非効率ですし。


HOta  2004-10-29 03:37:44  No: 11088

チャーリーさん  こんにちは

エラーは追記するファイルがないので起こります。ですから、
Try〜Except〜Endで対応しています。
コンパイルしたEXEファイルで試して下さい。
TFileStream読み込みの方法で馬名が一致したらコメントを表示させるのは、
Seekすると、一致させることは出来ません。一つ一つ調べないと分かりません。
一般的にこういうことを簡単にするのは、データーベースを使います。


つっか  2004-10-29 05:58:01  No: 11089

質問の内容がどんどん変化していって、条件がころころ変わるのでもうこのスレッド
はヤメにして、質問の内容を具体的に小分けして新しいスレッドをつくることを提案
します。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加