お知らせ

電子会議

ライブラリ

FDelphi サイト全文検索

Delphi FAQ一覧

サンプル蔵



FDelphi FAQ
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編纂委員会