macoです。初の質問をさせてください。
AssingFileのヘルプに、
すでに開いているファイル変数に対しては,AssignFile を使用しないでください。
とあります。
var
fl : File;
begin
AssignFile(fi, ’D:\test.txt');
Rewrite(fl,1); // 新規
end;
↓
BlockWrite(fl,); // 追記
↓
var
fl : File;
begin
AssignFile(fi, ’D:\test.txt');
Rewrite(fl,1); // 新規
end;
とすると、下のAssingFileにてファイルが初期化されます。
この動作はこちらの望む状態なのですが、上記のヘルプ抜粋にあるようにAssignFileにて「すでに開いているファイル変数」に対して、再度AssignFileすることはNGなのでしょうか?
NGならば、どのような弊害が起きえるのか、ご存知の方、いらっしゃいますか?
上記サンプルについて修正します。
var
fl : File;
は、Privateに定義します。
たとえば、EXE起動時に、AssingFile。
その後、ボタンClickイベントにて、同じファイル(同じFile変数)にAssignFileする。
それで意図通り動いてはいるものの、問題がないのか知りたく質問しています。
> それで意図通り動いてはいるものの
こんなのがあります (問題に気づかない例です),
http://mrxray.on.coocan.jp/Delphi/Others/ArrayRangeCheck.htm#fig10
本当に問題ないのであれば,そのままでいいと思いますよ.
以下は参考用のテストコードです.
コピペしてテストできます コピペしてテスト可能であることが重要です.
maco さんが提示された BlockWrite(fl,); はコンパイルエラーとなります.
http://docwiki.embarcadero.com/RADStudio/Rio/ja/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%85%A5%E5%87%BA%E5%8A%9B%E3%81%AE%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
FTextFile : TextFile;
FFilePath : string;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
// 質問では型なしファイルになっているが,テキストファイルを扱っている
// そこでファイルの型は TextFie にしている
// 型なしファイルで BlockWrite を使用した場合でも現象は同じと思われる
//
// 以下を確認する
//
// (1) EXE を起動して,終了じないでテキストファイル 000.txt の内容を確認
// (2) EXE を起動して,すぐに終了してテキストファイル 000.txt の内容を確認
// (3) EXE を起動して Button1 をクリックして 000.txt の内容を確認
//
// (4) 最後の Close を削除して上と同じように動作確認する
// (5) 最後の Close を CloseFile に変更して上と同じように動作確認する
//
// 動作確認は Windows 7 U64(SP1) + Delphi XE5(UP2) Pro VCL-32
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
FFilePath := '000.txt';
System.Assignfile(FTextFile, FFilePath);
System.Rewrite(FTextFile);
System.Writeln(FTextFile, '1 行目');
System.Writeln(FTextFile, '2 行目');
System.Writeln(FTextFile, '3 行目');
// System.CloseFile(FTextFile);
System.Close(FTextFile);
end;
//=============================================================================
// フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
try
// System.CloseFile(FTextFile);
System.Close(FTextFile);
except
end;
end;
//=============================================================================
// テキストファイルに文字列を書き込む
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LTextFile : TextFile;
begin
System.Assignfile(LTextFile, FFilePath);
System.Rewrite(LTextFile);
System.Writeln(LTextFile, '4 行目');
System.Writeln(LTextFile, '5 行目');
System.Writeln(LTextFile, '6 行目');
// System.CloseFile(LTextFile);
System.Close(LTextFile);
end;
end.
> // 型なしファイルで BlockWrite を使用した場合でも現象は同じと思われる
ファイルの型を File にして BlockWrite でやってみました.
例外は発生しませんでした.
> ファイルの型が TextFile の時とは現象が違いました.
> 例外は発生しませんでした.
当然ですが,ファイルをクローズしないと,ファイルハンドルは 0 になりません.
EXE を終了しても,そのファイルは開いたままの状態となります.
> EXE を終了しても,そのファイルは開いたままの状態となります.
Assignfile する度に新しいファイルハンドが割り当てられます.
Mr.XRAYさん
サンプル作成の上での回答ありがとうございます。助かります。
今回は、形無しファイルを対象としていました。バイナリデータ出力に利用しますので。
> EXE を終了しても,そのファイルは開いたままの状態となります.
これはどのように判断されましたか?
私は、サクラエディタの使いました。
サクラエディタ メニュー > 設定 > 共通設定 > ファイル タブにて、
ファイルの排他制御 を「しない」、ファイルの更新監視にチェック
これで、出力ファイルが更新されたタイミングで、サクラエディタがそれを検出し、
表示内容の更新することができます。
これを用いて出力ファイルを監視すると、
EXE実行中のBlockWriteでは更新を検出せず、EXE終了時にファイル更新を検出します。
つまりCloseFileをせずとも、EXE終了時にファイルが閉じられ確定している、
と判断しています。間違っていますかね。
> これはどのように判断されましたか?
CreateFile 関数で確認しました.
で,再度テストしたら・・・
ゴメンナさい.EXE を終了するとファイルハンドルは解放されていますね.
ファイルハンドルが有効なのはアプリ実行中だけのようです.
TextFile 型の例のようなこともあります.
基本的にはファイルはクローズされた方が無難と思います.
> この動作はこちらの望む状態なのですが、上記のヘルプ抜粋にあるように
> AssignFileにて「すでに開いているファイル変数」に対して、
> 再度AssignFileすることはNGなのでしょうか?
>
> NGならば、どのような弊害が起きえるのか、
この掲示板をご覧になっている方が参考になるかも知れないので,
現在のコードでは問題ないけど・・・,という場合の例を,
手前味噌になってしまいますが示してみたいと思います.
既に配列の例を提示しましたが,以下はポインタ型の変数の操作例です.
http://mrxray.on.coocan.jp/Delphi/Others/Pointer.htm#list17
次は,聞きなれない方には申し訳ありませんが,サブクラス化のコードです.
[ 11_WindowProc プロパティを使用したサブクラス化 ]
http://mrxray.on.coocan.jp/Delphi/Others/InheritedClass_SubClass.htm#11
上のリンクのサンプルで,
procedure TForm1.FormDestroy(Sender: TObject);
begin
// TEdit の WindowProc を元に戻す
Edit1.WindowProc := FOriginEditProc;
end;
というコードを削除しても,上のサンプルのコードは正常に動作します.
ただし,例外が発生しないのはそのサンプルの場合です.
メッセージの内容,あるいはメッセージの処理内容によっては
OnDestroy のコードがないと例外が発生することがあります.
つまり,何々しないようにとか,何々するようにという説明があつた時には
それに従った方がいいと言うことになります.それが定石と言えます.
残念ながら「弊害が起きるなら,どんな弊害があるのか」を具体的に示すのは,
弊害が発生する示すサンプルを作成しなければなりませんから面倒です.
今回の場合は型なしファイルである File の場合は例外が発生しませんが,
TextFie の場合は例外が発生します.
ならは,型なしファイルの場合は CloseFile しなくてもいいのでは,と訊かれれば,
それはプログラマが自分で判断するしかありません.
Mr.XRAYさん、サンプルの提示ありがとうございます。
> 型なしファイルの場合は CloseFile しなくてもいいのでは,と訊かれれば,
> それはプログラマが自分で判断するしかありません.
ありがとうございます。おっしゃる通りです。
> TFileRec は、Delphi 言語の型付きファイルと型なしファイルのための内部形式です。
> TFileRec は、型付きファイルと型なしファイルのための内部形式です。
> TFileRec を利用して Delphi のファイル変数を型キャストすると、その内部のフィールドにアクセスできます。
TFileRec(fl).*** にて、AssignFile済みを判断させようとしています。
しかし、CloseFile(fl)前後でTFileRec(fl).*** の格納値が変わらず……。
すなおにフラグ管理するしかないかと考えております。
もう少し調べたうえ、回避策を提示します。
> しかし、CloseFile(fl)前後でTFileRec(fl).*** の格納値が変わらず……。
TFileRec構造体のModeがfmClosedになりませんか?
HFUKUSHIさん
ありがとうございます!
TFileRec(fl).Mode = 55216は、fmClosedなのですね。
理解・解決です。
以下のソースで検証していた際、
冒頭、fl : Fileを一度もAssignFileしていない状態での TFileRec(fl).Mode は 0 を示すもの
末尾のCloseFile(fl) 後の TFileRec(fl).Mode は 55216 を示します。
これを見て、ファイル変数が開かれているままと解釈していました。
お恥ずかしい……。
Mr.XRAYさんも多数のサンプルコードありがとうございます。勉強になりました。
procedure TForm3.FormCreate(Sender: TObject);
var
fpth : String;
begin
OutputDebugString(PChar(IntToStr( TFileRec(fl).Mode )));
fpth := ChangeFileExt(Application.ExeName, '_サンプル.txt');
AssignFile(fl,fpth);
OutputDebugString(PChar(IntToStr( TFileRec(fl).Mode )));
Rewrite(fl,1);
OutputDebugString(PChar(IntToStr( TFileRec(fl).Mode )));
CloseFile(fl);
OutputDebugString(PChar(IntToStr( TFileRec(fl).Mode )));
end;
ツイート | ![]() |