Infinite loop in Pascal, how come?
I wrote a Pascal function to replace either a whole line, or just a part of it, in a file, for use in Inno Setup:
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;
It used to work just fine, but replaced only the first occurrence of the OldLinePart on each line of the file specified by FilePath, with the Replacement. That is when I added the repeat loop. The logic is that Pos()
returns 0 when no more occurrences are found. Then it should go on to the next line. The truth is however, that the loop keeps going infinitely and I have no idea why. I tried adding a Break
statement in the else
clause of if FoundAtPos > 0
, but no luck...
Edit: Obviously it is getting late. I was replacing the OldLinePart with a very long string... that contained the same word.
The issue has been resolved as follows.
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;
I Replaced your loop with a single call to StringReplace. This prevents the (infinite) loop and solves the problem.
Note that you also had a problem with IsReplaced
, if e.g. FileLines[4]
was replaced, but not FilesLine[last]
then your code would not call SaveStringsToFile
.
It also simplifies the code somewhat.
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;
Let me know if this works for you.
This should now be done with StringChangeEx. It is nice that it supports Unicode. It is not nice the way he breaks old scripts by removing old functions. StringReplace doesn't compile anymore; even though it is the Delphi function for doing this. StringChange is already deprecated.
精彩评论