3

ファイル内の望ましくない文字を別の「適切な」文字に置き換えることがよくあります。

インターフェイスは次のとおりです。

procedure cleanfileASCII2(vfilename: string; vgood: integer; voutfilename: string);

すべての望ましくないものをスペースに置き換えるには、cleanfileASCII2(original.txt, 32 ,cleaned.txt) と呼びます。

問題は、これにはかなり長い時間がかかることです。示されているよりも良い方法はありますか?

procedure cleanfileASCII2(vfilename: string; vgood: integer; voutfilename:
string);
var
  F1, F2: file of char;
  Ch: Char;
  tempfilename: string;
  i,n,dex: integer;
begin
   //original
    AssignFile(F1, vfilename);
    Reset(F1);
    //outputfile
    AssignFile(F2,voutfilename);
    Rewrite(F2);
      while not Eof(F1) do
      begin
        Read(F1, Ch);
        //
          n:=ord(ch);
          if ((n<32)or(n>127))and (not(n in [10,13])) then
             begin // bad char
               if vgood<> -1 then
                begin
                ch:=chr(vgood);
                Write(F2, Ch);
                end
             end
           else   //good char
            Write(F2, Ch);
      end;
    CloseFile(F2);
    CloseFile(F1);
end;
4

7 に答える 7

6

問題は、バッファの処理方法に関係しています。メモリ転送は、操作の中で最もコストのかかる部分です。この場合、ファイルをバイト単位で見ています。ブロック読み取りまたはバッファー読み取りに変更することで、速度が大幅に向上します。正しいバッファ サイズは、読み取り元によって異なることに注意してください。ネットワーク化されたファイルの場合、TCP/IP が課すパケット サイズのために、非常に大きなバッファは効率が悪い場合があります。これでも、gigE からの大きなパケットでは少しわかりにくくなっていますが、いつものように、最良の結果はベンチマークすることです。

便宜上、標準読み取りからファイル ストリームに変換しました。blockread を使用して同じことを簡単に行うことができます。この場合、私は 15MB のファイルを取り、あなたのルーチンを実行しました。ローカル ファイルで操作を実行するのに 131,478 ミリ秒かかりました。1024 バッファでは、258ms かかりました。

procedure cleanfileASCII3(vfilename: string; vgood: integer; voutfilename:string);
const bufsize=1023;
var
  inFS, outFS:TFileStream;
  buffer: array[0..bufsize] of byte;
  readSize:integer;
  tempfilename: string;
  i: integer;
begin
   if not FileExists(vFileName) then exit;

   inFS:=TFileStream.Create(vFileName,fmOpenRead);
   inFS.Position:=0;
   outFS:=TFileStream.Create(vOutFileName,fmCreate);
   while not (inFS.Position>=inFS.Size) do
      begin
      readSize:=inFS.Read(buffer,sizeof(buffer));
      for I := 0 to readSize-1 do
          begin
          n:=buffer[i];
          if ((n<32)or(n>127)) and (not(n in [10,13])) and (vgood<>-1) then
             buffer[i]:=vgood;
          end;
      outFS.Write(buffer,readSize);
      end;
   inFS.Free;
   outFS.Free;
end;
于 2009-05-28T16:12:03.837 に答える
2

いくつかの改善:

  1. データをバッファリングし、2k または 16k または同様のサイズのブロックを読み取ります
  2. ルックアップ テーブルを使用する

これはテストされていない刺し傷です(現在、私の前にコンパイラはありません):

procedure cleanfileASCII2(vfilename: string; vgood: integer; voutfilename: string);
var
    f1, f2: File;
    table: array[Char] of Char;
    index, inBuffer: Integer;
    buffer: array[0..2047] of Char;
    c: Char;
begin
    for c := #0 to #31 do
        table[c] := ' ';
    for c := #32 to #127 do
        table[c] := c;
    for c := #128 to #255 do
        table[c] := ' ';
    table[#10] := #10; // exception to spaces <32
    table[#13] := #13; // exception to spaces <32

    AssignFile(F1, vfilename);
    Reset(F1, 1);
    AssignFile(F2,voutfilename);
    Rewrite(F2, 1);
    while not Eof(F1) do
    begin
        BlockRead(f1, buffer, SizeOf(buffer), inBuffer);
        for index := 0 to inBuffer - 1 do
          buffer[index] := table[buffer[index]];
        BlockWrite(f2, buffer, inBuffer);
    end;
    Close(f2);
    Close(f1);
end;
于 2009-05-28T16:07:37.590 に答える
1

バッファリングはそれを行う正しい方法です。違いを確認するためにコードを変更しました。

procedure cleanfileASCII2(vfilename: string; vgood: integer; voutfilename:
string);
var
  F1, F2: file;
  NumRead, NumWritten: Integer;
  Buf: array[1..2048] of Char;
  Ch: Char;
  i, n: integer;
begin
    AssignFile(F1, vfilename);
    Reset(F1, 1); // Record size = 1
    AssignFile(F2, voutfilename);
    Rewrite(F2, 1); // Record size = 1
    repeat
      BlockRead(F1, Buf, SizeOf(Buf), NumRead);
      for i := 1 to NumRead do
      begin
        Ch := Buf[i];
        //
        n := ord(ch);
        if ((n<32)or(n>127))and (not(n in [10,13])) then
        begin // bad char
         if vgood <> -1 then
         begin
           ch := chr(vgood);
           Buf[i] := Ch;
         end
        //else   //good char
         //Write(F2, Ch);
        end;
      end;
      BlockWrite(F2, Buf, NumRead, NumWritten);
    until (NumRead = 0) or (NumWritten <> NumRead);
    CloseFile(F1);
    CloseFile(F2);
end;
于 2009-05-28T16:04:48.467 に答える
1

入力と出力をバッファリングして、文字のチャンク (大きすぎない場合はファイル全体も) を読み取り、配列を処理してから、配列全体を出力ファイルに書き込むことができます。

これらのほとんどの場合、ディスク IO がボトルネックであり、多数の小さな読み取りではなく、より少ない大きな読み取りを行うことができれば、高速になります。

于 2009-05-28T15:28:29.697 に答える
0

どこを知らずに最適化しようとしないでください。

Sampling Profiler (delphitools.info) を使用して、ボトルネックがどこにあるかを知る必要があります。使い方は簡単です。

ループの前に vgood chr 変換を事前計算します。

また、いくつかの変換は必要ありません: Ord() と Chr()。常に「Ch」変数を使用します。

if not (ch in [#10, #13, #32..#127]) then
于 2009-05-28T16:00:59.583 に答える
0

おそらく最も簡単な方法は次のとおりです。

  1. 別のファイルを作成 (一時)
  2. 基本ファイルのすべての内容を一時ファイルにコピーします。ファイル (行ごと)
  3. 置き換えたい文字や単語を読み取ったことを検出し、コピーを停止します
  4. 編集を入力します(一時ファイルへ)
  5. 続行して、基本ファイルから一時ファイルへのコピーを終了します
  6. 基本ファイルの書き換え (内容の削除)
  7. 一時ファイルから基本ファイルに行をコピーする
  8. 終わり!

役に立った場合は、この投稿に +1 票を投じてください

于 2013-01-10T19:02:53.297 に答える
0

私はこのようにして、ファイル I/O が処理前にすべて一度に行われるようにしました。コードは Unicode の更新を行うことができますが、null などの厄介なテキスト文字に対処し、TStrings 機能を提供します。ブライ

procedure TextStringToStringsAA( AStrings : TStrings; const AStr: Ansistring);
// A better routine than the stream 'SetTextStr'.
// Nulls (#0) which might be in the file e.g. from corruption in log files
// do not terminate the reading process.
var
  P, Start, VeryEnd: PansiChar;
  S: ansistring;
begin
  AStrings.BeginUpdate;
  try
    AStrings.Clear;

    P := Pansichar( AStr );
    VeryEnd := P + Length( AStr );

    if P <> nil then
      while P < VeryEnd do
      begin
        Start := P;
        while (P < VeryEnd) and not CharInSet(P^, [#10, #13]) do
         Inc(P);
        SetString(S, Start, P - Start);
        AStrings.Add(string(S));
        if P^ = #13 then Inc(P);
        if P^ = #10 then Inc(P);
      end;
  finally
    AStrings.EndUpdate;
  end;
end;


procedure TextStreamToStrings( AStream : TStream; AStrings : TStrings );
// An alternative to AStream.LoadFromStream
// Nulls (#0) which might be in the file e.g. from corruption in log files
// do not terminate the reading process.
var
  Size : Integer;
  S    : Ansistring;
begin
  AStrings.BeginUpdate;
  try
    // Make a big string with all of the text
    Size := AStream.Size - AStream.Position;
    SetString( S, nil, Size );
    AStream.Read(Pointer(S)^, Size);

    // Parse it
    TextStringToStringsAA( AStrings, S );
  finally
    AStrings.EndUpdate;
  end;
end;

procedure LoadStringsFromFile( AStrings : TStrings; const AFileName : string );
// Loads this strings from a text file
// Nulls (#0) which might be in the file e.g. from corruption in log files
// do not terminate the reading process.
var
  ST : TFileStream;
begin
  ST := TFileStream.Create( AFileName, fmOpenRead + fmShareDenyNone);
  // No attempt is made to prevent other applications from reading from or writing to the file.
  try
    ST.Position := 0;
    AStrings.BeginUpdate;
    try
      TextStreamToStrings( ST, AStrings );
    finally
      AStrings.EndUpdate;
    end;

  finally
    ST.Free;
  end;
end;
于 2009-05-28T15:47:58.097 に答える