こんにちは。
ボタンをクリックすると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;
>レコード型メンバの中でCommentしかデータに書き込まれていないようなので、
>レコード型のメンバを全部登録するにはどうすればいいのか教えてください。
> BlockWrite(F,Len,SizeOf(Len));//Lenをファイルに書き込み
> BlockWrite(F,PChar(dat.Comment)^,Len);
長さと dat.Comment 部分しか書き込んでいないので当然では?
こんにちは。
>レコード型メンバの中で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にどのように書けばいいのでしょうか。
複雑なことを考えるよりさきに、まず最低できなければならなことは、ファイルの
読み書きだと思います。それ用の関数を作ってみました。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;
こんばんは。
コード参考にしたいと思います。
型変換部分が分からないのですが、
レコード型を
--------------
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(★));
------------
★部分のサイズを調べるにはどの型を入れればいいのでしょうか。
-----------
★部分に入れるのは、型より変数。
>コード参考にしたいと思います。
の割には、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);
で良いのではないですか?
こんにちは。
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]:='コメント';
----------------
馬名が一致すれば読み込むときにコメントを表示させたい。
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;
------------------------------------------------------------------
途中で質問が変わってしまっていますね。
レコードが一つですから、上書きになります。
何を質問したいのでしょうか?
どういう状態にしたいのでしょうか?
こんにちは。
発言を変えて投稿し直したのですが、掲示板のエラー等で重なってしまいました。すみません。
TFileStreamを使って読み書きしたいのですが、レコードが一つなので上書きになるということなので、
--------------
TComment=record
Bamei:string;//馬名
Comment:string;//コメント
end;
--------------
このレコード型の設定に問題があるのでしょうか。
StringGridにコメントが書いてあれば、そのコメントを登録していきたいのです。
現在は以下のように2つデータがあるとすれば、登録したテキストファイルをみると、「さくら はやい」と上書きされている状態です。これを
「ナリタ つよい
サクラ はやい」
というようにデータを追加していきたいのです。
-----------
ナリタ:つよい。
--------------
サクラ:はやい。
-------------
チャーリーさん こんにちは
WriteCommen手続きで書き込んでいると思いますが、
どのようにしているのでしょうか?
こんにちは。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;
-----------------------------
チャーリーさん こんにちは
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;
こんにちは。
except以降は例外処理でファイルがない場合fmCreateが実行されるはずなのですが、手作業で書き込まれていないテキストファイル[Comment.txt]を用意しておかないと、fmOpenReadWrite部分でエラーとなってしまいます。
全くファイルがない状態で、fmCreateして、ファイルがある場合はfmOpenReadWriteさせるにはどうしたらいいのでしょうか?
・・・教えてもらったコードがそうなっていると思うのですが、エラーが出てしまうので。。
チャーリーさん、こんにちは。
このスレッドを読んでいて少々疑問に思ったのですが、レコード型のデータの管理は
どうされているのでしょうか?
TList型のオブジェクトなどは用意されていますか?
チャーリーさん こんにちは
try
FS:=TFileStream.Create(Filename,fmOpenReadWrite);
FS.Seek(FS.Size,soFromBeginning);
except
//ファイルが無い場合エラーになり、新規に作成する。
FS:=TFileStream.Create(Filename,fmCreate);
end;
チャーリーさん こんにちは
ありゃりゃ途中で送信してしまった。
ファイルをmOpenReadWriteで開き、エラーの場合は、fmCreateで開く
様に書いています。ファイルが無い場合は、エラーを起こして
except句へ飛びます。
FileExists 関数を使えば簡単
ふぐちゃんさん、HOtaさんこんにちは。
TList型のオブジェクトとか用意していません。
今後のことを考えれば、馬名のあいうえお順に並んでいた方がテキストを見たときに見やすいですし、配布しやすいなと思っていたりします。そうした場合、TFileStreamで読み書きするより別にいい方法ありますかね?文字数制限なしでデータを登録して。。
HOtaさん。
コンパイル中にエラーが出るのは正常だということですね。
コンパイル中エラーが出て止まってしまうので・・・エラーが出ない方法あるのかなと思ったのですが、コードはちゃんと例外部分書かれているので問題ないとおもいますし。いろいろ試してみます。また読み込みでも悩みそうです。
りりかさんが指摘されているように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さん。
エラー内容は、「プロジェクト○○○が、EFOpenErrorクラスの例外を生成しました。指定されたファイルが見つかりません。」というものです。
ふぐちゃんさん。
複数のデータをどういう形で保持するか。ソートさせたりテキストファイルで配布するというのを考えた場合、普通皆さんどのようになさるのでしょうか。
> ソートさせたりテキストファイルで配布するというのを考えた場合
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.
すみません、ファイル名が変でした。
'c:\TestBamei.txt'
にしてください。
あっ、それから馬名はともかく、もしコメントが2行以上、つまり改行がある場合は
ファイルアクセスをもう少し工夫する必要があります。
チャーリーさん こんにちは
「プロジェクト○○○が、EFOpenErrorクラスの例外を生成しました。指定されたファイルが見つかりません。」というエラーはコンパイル時に出るのですか?実行時ではないですか?
りりかさん。
現プログラムでの登録の仕方等が違うので悩んでしまいそうですが、参考にさせて頂きます。
HOtaさん。
F9で実行し、アプリケーションが立ち上がって書き込みしようとするときです。実行時です。
TFileStream読み込みの場合ですけれど、馬名が一致したらコメントを表示させる場合、TFileStreamのReadとSeekをどのように書けばいいのでしょうか。
>登録の仕方等が違うので
登録の仕方の違いなんて大した意味があるとは思いませんけど。
データのリストを管理し、ソートし、ファイルアクセスがテキストで出来る方が
ずっと重要だと思います。例のコードの TForm1.Button1Click() を見ると、
データの追加の仕方なんて簡単です。
>馬名が一致したらコメントを表示させる場合
これだって、リストを検索するのは簡単です。ファイルを先頭から舐めて検索する
のはいかにも非効率ですし。
チャーリーさん こんにちは
エラーは追記するファイルがないので起こります。ですから、
Try〜Except〜Endで対応しています。
コンパイルしたEXEファイルで試して下さい。
TFileStream読み込みの方法で馬名が一致したらコメントを表示させるのは、
Seekすると、一致させることは出来ません。一つ一つ調べないと分かりません。
一般的にこういうことを簡単にするのは、データーベースを使います。
質問の内容がどんどん変化していって、条件がころころ変わるのでもうこのスレッド
はヤメにして、質問の内容を具体的に小分けして新しいスレッドをつくることを提案
します。
ツイート | ![]() |