0

Inno Setup で使用するために、ファイル内の行全体またはその一部を置き換える Pascal 関数を作成しました。

function ReplaceInFile(const FilePath, OldLinePart, Replacement: String;
                       DoReplaceWholeLine, IsCaseSensitive: Boolean): Boolean;
var
  FileLines:          TArrayOfString;
  Index:              Integer;
  FoundAtPos:         Integer;
  LeftOfOldLinePart:  String;
  RightOfOldLinePart: String;
  IsReplaced:         Boolean;
begin
  Result := False;
  if FileExists(FilePath) then begin
    LoadStringsFromFile(FilePath, FileLines);
    for Index := 0 to GetArrayLength(FileLines) - 1 do
    begin
      repeat
        FoundAtPos := 0;
        if IsCaseSensitive then
          FoundAtPos := Pos(OldLinePart, FileLines[Index])
        else
          FoundAtPos := Pos(Uppercase(OldLinePart), Uppercase(FileLines[Index]));
        if FoundAtPos > 0 then begin
          if DoReplaceWholeLine then begin
            FileLines[Index] := Replacement;
            IsReplaced := True;
          end
          else begin
            LeftOfOldLinePart := Copy(FileLines[Index], 1, FoundAtPos - 1);
            RightOfOldLinePart := Copy(FileLines[Index], FoundAtPos 
              + Length(OldLinePart), Length(FileLines[Index]) 
              - Length(LeftOfOldLinePart + OldLinePart));
            FileLines[Index] := LeftOfOldLinePart + Replacement + RightOfOldLinePart;
            IsReplaced := True;
          end;
        end;
      until FoundAtPos = 0;
    end;
    if IsReplaced then
      if SaveStringsToFile(FilePath, FileLines, False) then
        Result := True;
  end;
end;

以前は問題なく動作していましたが、FilePath で指定されたファイルの各行で最初に出現した OldLinePart のみを Replacement に置き換えていました。それが繰り返しループを追加したときです。Pos()これ以上オカレンスが見つからない場合、ロジックは 0 を返します。その後、次の行に進む必要があります。しかし、真実はループが無限に続き、その理由がわかりません。の句にBreakステートメントを追加しようとしましたが、うまくいきません...elseif FoundAtPos > 0

追記:明らかに遅くなりました。OldLinePart を非常に長い文字列に置き換えていました...同じ単語が含まれていました。

この問題は次のように解決されました。

function ReplaceInFile(const FilePath, OldLinePart, Replacement: String; DoReplaceWholeLine, IsCaseSensitive: Boolean): Boolean;
var
  FileLines: TArrayOfString;
  Index: Integer;
  SearchLinePart: String;
  FoundAtPosition: Integer;
  SearchOffset: Integer;
  LeftOfOldLinePart: String;
  RightOfOldLinePart: String;
  IsReplaced: Boolean;
begin
  Result := False;
  if FileExists(FilePath) then
  begin
    LoadStringsFromFile(FilePath, FileLines);
    for Index := 0 to GetArrayLength(FileLines) - 1 do
    begin
      SearchOffset := 0;
      SearchLinePart := FileLines[Index];
      repeat
        FoundAtPosition := 0;
        if IsCaseSensitive then
          FoundAtPosition := SearchOffset + Pos(OldLinePart, SearchLinePart)
        else
          FoundAtPosition := SearchOffset + Pos(Uppercase(OldLinePart), Uppercase(SearchLinePart));
        if FoundAtPosition > SearchOffset then
        begin
          if DoReplaceWholeLine then
          begin
            FileLines[Index] := Replacement;
            IsReplaced := True;
            Break;
          end
          else
          begin
            LeftOfOldLinePart := '';
            RightOfOldLinePart := '';
            LeftOfOldLinePart := Copy(FileLines[Index], 1, FoundAtPosition - 1);
            RightOfOldLinePart := Copy(FileLines[Index], FoundAtPosition + Length(OldLinePart), Length(FileLines[Index]) - Length(LeftOfOldLinePart + OldLinePart));
            FileLines[Index] := LeftOfOldLinePart + Replacement + RightOfOldLinePart;
            IsReplaced := True;
            SearchOffset := Length(LeftOfOldLinePart + Replacement);
            SearchLinePart := RightOfOldLinePart;
          end;
        end;
      until FoundAtPosition <= SearchOffset;
    end;
    if IsReplaced then
      if SaveStringsToFile(FilePath, FileLines, False) then
        Result := True;
  end;
end;
4

2 に答える 2

2

ループをStringReplaceへの単一の呼び出しに置き換えました。これにより、(無限) ループが回避され、問題が解決されます。たとえばが置き換えられた場合
、 にも問題があったことに注意してください。 また、コードをいくらか単純化します。IsReplacedFileLines[4]FilesLine[last]SaveStringsToFile

uses SysUtils;

function ReplaceInFile(const FilePath, OldLinePart, Replacement: String;
                       DoReplaceWholeLine, IsCaseSensitive: Boolean): Boolean;
var
  FileLines:          TArrayOfString;
  Index:              Integer;
  TempStr:            String;
  IsReplaced:         Boolean;
  Flags:              TReplaceFlags;
  IsReplacedAnywhere: Boolean;
begin
  Result := False;
  if FileExists(FilePath) then begin
    LoadStringsFromFile(FilePath, FileLines);
    IsReplacedAnywhere:= false;
    for Index := 0 to GetArrayLength(FileLines) - 1 do begin
      if DoReplaceWholeLine then begin
        IsReplaced := 
          IsCaseSensitive and (Pos(OldLinePart, FileLines[Index]) > 0) or 
          not(IsCaseSensitive) and 
           (Pos(Uppercase(OldLinePart), Uppercase(FileLines[Index])) > 0);
        if IsReplaced then FileLines[Index] := Replacement;
      end
      else begin
        Flags:= [rfReplaceAll];
        if not(IsCaseSensitive) then Flags:= Flags + [rfIgnoreCase];
        TempStr:= StringReplace(FileLines[Index], OldLinePart, Replacement
                                ,Flags);
        IsReplaced := (TempStr <> FileLines[Index]);
        if IsReplaced then FileLines[Index]:= TempStr;
      end; {else}
      IsReplacedAnywhere:= IsReplacedAnywhere or IsReplaced;
    end; {for Index}
    Result:= IsReplacedAnywhere 
             and SaveStringsToFile(FilePath, FileLines, False);
  end; {if}
end;

これがうまくいくかどうか教えてください。

于 2011-05-08T02:48:49.673 に答える
0

これは StringChangeEx で行う必要があります。ユニコード対応なのもうれしい。彼が古い関数を削除して古いスクリプトを壊す方法は良くありません。StringReplace はコンパイルされなくなりました。これを行うための Delphi 関数ですが。StringChange は既に非推奨です。

于 2013-09-08T19:19:16.120 に答える