|
15番会議室「FAQ編纂委員会」に寄せられた「よくある質問の答え」
[Q]
ファイルコピーを行う関数が見当たらないのですが、ユーザーズガイドの
CopyFile(FromFile,ToFile) のような関数はどうやって作ればいいでしょう?
どこかにサンプルがありますか?
[A]
ファイルのある部分を読む、ファイルを作成し、何かを書き込む、という関
数が色々用意されています。これらを組み合わせます。ユーザーズガイドの
CopyFile(FromFile,ToFile) は、\DELPHI\DEMOS\DOC\FILMANEX\FMXUTILS.PAS
にあります。また、Windows API に、LZCopy という、ちょうど、これに相当
する関数がありますので、これを利用されるのもいいでしょう。
1:TFileStream
2:BlockRead/BlockWrite
3:_lread/_lwrite (Windows API)
4:LZCopy (Windows API)
5:FileRead/FileWrite
推奨は、速度の点で、1、2、3(ほぼ同速度)、これに、4、5が続きま
す。といっても、あるマシン環境での 50 ファイル、計 680KB のコピー(ハ
ードから MO、コピー経過のゲージ付き、4以外はバッファー 8,192KB)で、
45〜55 秒の間にみんなはいってしまいますが。また対象ファイルの種類、つ
まり、比較的小さなファイルが多いか、大きなファイルが少しだけか、バッフ
ァーサイズの違い等によって、これらの速度のパフォーマンスが逆転する場合
もあるかも知れません。
また、FMXUTILS.PAS の CopyFile は、極めて効率的なコーディングがして
あり、上述した5つの方法、いずれもこれに組み込むとよいと思います。
[例]
上述した5つの方法を、列挙型の変数で分岐させて実行する手続きを作って
みます。
uses {必要なユニット,} LZExpand, {LZCopy に必要}
Consts; {エラー処理のところで必要}
type
TWCopy=(wcDelphiFileStream,wcDelphiBlockRW,wc_lreadw,wcLZCopy,
wcDelphiFileRW);
TacFilename=array[0..79] of char;
{適当に外部定義、かつ値入力済}
WCopy: TWCopy;
ChunkSize: integer; {例:8192}
gSumSize: longint; {コヒ゜ーしたいファイルの合計サイス゛}
procedure CopyFile(aCurDir,fCurDir2: TFilename;
{コヒ゜ー元、コヒ゜ー先テ゛ィレクトリー。末端'\'付き}
aSearchRec: TSearchRec; {コヒ゜ー元ファイルのレコート゛}
var vFcount, {この時点でのコヒ゜ー済ファイル数}
vSize, {サイス゛合計}
vProgress {サイス゛合計%}
: longint);
var
SourName,DestName: TFilename;
CopyBuffer: Pointer; {ハ゛ッファー用。LZCopy では不要}
fProgress,
AccBytes,SumBytes, {ケ゛ーシ゛表示のための一時変数}
Fsize1,FDate1,
BytesCopied {読まれたハ゛イト数。LZCopy では不要}
: Longint;
NumRead,NumWrite {BlockRead/BlockWrite で使う}
: word;
Source, Dest, {ファイルハント゛ル}
FAttr1
: integer;
bBlock
: boolean;
vF,vG: TFileStream;
vFB,vGB: File; {BlockRead/BlockWrite で使う}
acSour,acDest: TacFilename; {Windows API 用}
OpenBuf: TOFStruct; {LZCopy 用}
{CopyFile}
begin
SourName:=aSearchRec.Name;
FDate1:=aSearchRec.Time;
Fsize1:=aSearchRec.Size;
FAttr1:=aSearchRec.Attr;
DestName:=fCurDir2+SourName;
SourName:=aCurDir+SourName;
bBlock:=false;
GetMem(CopyBuffer,ChunkSize); {ハ゛ッファー。wcLZCopy では不要}
fProgress:=vProgress;
try
{元ファイルオーフ゜ン}
case WCopy of
wcDelphiFileStream:
vF:=TFileStream.Create(SourName,fmOpenRead);
wcDelphiFileRW:
Source:=FileOpen(SourName,fmShareDenyWrite); {サンフ゜ルのまま}
wcDelphiBlockRW: begin
AssignFile(vFB,SourName);
Reset(vFB,1);
end; {case}
wcLZCopy: begin
StrPCopy(acSour,SourName);
Source:=LZOpenFile(
acSour,OpenBuf,of_Share_Deny_Write or of_Read);
end; {case}
wc_lreadw: begin
StrPCopy(acSour,SourName);
Source:=_lopen(acSour,of_Share_Deny_Write or of_Read);
end; {case}
end; {case WCopy of}
case WCopy of
wcDelphiFileRW,wcLZCopy,wc_lreadw:
if Source<0 then
raise
EFOpenError.Create(
FmtLoadStr(SFOpenError,[SourName]));
end; {case WCopy of}
try
{先ファイルオーフ゜ン}
case WCopy of
wcDelphiFileStream:
vG:=TFileStream.Create(DestName,fmCreate or fmOpenWrite);
wcDelphiFileRW:
Dest:=FileCreate(DestName);
{create output file; overwrite existing}
wcDelphiBlockRW: begin
AssignFile(vGB,DestName);
rewrite(vGB,1);
end; {case}
wcLZCopy: begin
StrPCopy(acDest,DestName);
Dest:=LZOpenFile(acDest,OpenBuf,
of_Share_Exclusive or of_Write or of_Create);
end; {case}
wc_lreadw: begin
StrPCopy(acDest,DestName);
Dest:=_lcreat(acDest,0);
end; {case}
end; {case WCopy of}
case WCopy of
wcDelphiFileRW,wcLZCopy,wc_lreadw:
if Dest<0 then
raise
EFCreateError.Create(
FmtLoadStr(SFCreateError,[DestName]));
end; {case WCopy of}
try
{------コヒ゜ー開始 Read----------------------------}
AccBytes:=0;
repeat
case WCopy of
wcDelphiFileStream:
BytesCopied:=
vF.Read(CopyBuffer^,ChunkSize);
wcDelphiFileRW:
BytesCopied:=
FileRead(Source,CopyBuffer^,ChunkSize);
wcDelphiBlockRW: begin
BlockRead(vFB,CopyBuffer^,ChunkSize,NumRead);
BytesCopied:=NumRead;
end; {case}
wcLZCopy:
BytesCopied:=LZCopy(Source,Dest);
wc_lreadw:
BytesCopied:=_lread(Source,CopyBuffer,ChunkSize);
end; {case WCopy of}
if BytesCopied<0 then
raise
EFCreateError.Create(
FmtLoadStr(SFCreateError,[DestName]))
else begin
{------Write--------------------------------}
case WCopy of
wcDelphiFileStream:
vG.Write(CopyBuffer^,BytesCopied); {<---*1}
wcDelphiFileRW:
FileWrite(Dest,CopyBuffer^,BytesCopied);
wcDelphiBlockRW:
BlockWrite(vGB,CopyBuffer^,BytesCopied,NumWrite);
wcLZCopy: bBlock:=true; {抜ける}
wc_lreadw:
_lwrite(Dest,CopyBuffer,BytesCopied);
end; {case WCopy of}
end; {if BytesCopied>0}
inc(AccBytes,BytesCopied);
SumBytes:=vSize+AccBytes;
{longint オーハ゛ーフロー回避}
if (SumBytes>=(MaxLongint div 100)) then
vProgress:=(SumBytes div 100)*100 div
(gSumSize div 100)
else
vProgress:=SumBytes*100 div gSumSize;
{大きなファイルで途中経過}
if (fProgress=vProgress) or
(vProgress>fProgress+5) then begin
if (SumBytes>=(MaxLongint div 100))
then
vProgress:=
((SumBytes div 100)*100) div
(gSumSize div 100)
else
vProgress:=
SumBytes*100 div gSumSize;
{ケ゛ーシ゛表示させます。カスタム手続き}
UpdateLabels(aSearchRec.Name+' をコピー中です...',
vFcount,vProgress);
fProgress:=vProgress;
end;
until (BytesCopied<ChunkSize) or bBlock;
finally
{先ファイルクロース゛}
case WCopy of
wcDelphiFileStream: vG.Free;
wcDelphiFileRW: FileClose(Dest);
wcDelphiBlockRW: CloseFile(vGB);
wcLZCopy: LZClose(Dest);
wc_lreadw: _lclose(Dest);
end; {case WCopy of}
{先ファイルの属性・タイムスタンフ゜を元ファイルと同じにします}
FileSetAttr(DestName,FAttr1);
Dest:=FileOpen(DestName,fmOpenRead);
try
FileSetDate(Dest,FDate1);
finally
FileClose(Dest);
end;
end;
finally
{元ファイルクロース゛}
case WCopy of
wcDelphiFileStream: vF.Free;
wcDelphiFileRW: FileClose(Source);
wcDelphiBlockRW: CloseFile(vFB);
wcLZCopy: LZClose(Source);
wc_lreadw: _lclose(Source);
end; {case WCopy of}
end;
finally
FreeMem(CopyBuffer,ChunkSize);
end;
{終了処理}
inc(vFcount);
inc(vSize,Fsize1);
if (vSize>=(MaxLongint div 100)) then
vProgress:=(vSize div 100)*100 div
(gSumSize div 100)
else
vProgress:=vSize*100 div gSumSize;
end; {CopyFile}
【注意】
1.今、上記関数使ってテストしました。wcDelphiFileStream については
問題なさそうでした。いやー、自分で感心しました。10 数行ばかりの呼出手
続きで、ちゃんとこの関数が使えました。色々削ったり、FAQ サンプル用に
変えたりしましたので一発で通るって思わなかったっすよ。選択全ファイルに
対するコピー経過のゲージ付きです。
もしよろしかったら、皆さんもこれ利用して、楽して下さい。
2.テスト中に遭遇した問題点としては、コピー先の同名ファイルに対する
処理や、あと、開始、終了時などのちゃんとした表示(0%から始め、ファイル
数をきちんと出す)などですか。
3.UpdateLabels は、ゲージ表示のために、VGauge(あるいは Gauge)を
使って、
VGauge1.Progress:=vProgress;
Update;
としてるだけです。あと、「...コピー中です...」などをラベルに表示。
4.gSumSize ですけど、この会議室の、[FAQ]ファイルサイズを得るには?
の、TSearchRec を使う方法でやって下さい。ほんと、300 ファイルでもほぼ
一瞬に計算しますんで。
5.SBORLAND で質問されたことがあるんですが、上のサンプルで抜けてる
エラー処理に、コピー先での容量不足、があります(上の {<---*1})。
ここにあるドキュメントは NIFTY SERVEの Delphi Users' Forum FDELPHIに寄せられる質問の中から、よくある質問への回答を FDELPHIのメンバーがまとめたものです。 したがって、これらの回答はボーランド株式会社がサポートする公式のものではなく、掲示されている内容についての問い合わせは受けられない場合があります。
Copyright 1996-1998 Delphi Users' ForumFAQ編纂委員会
|