23. juni 2009 - 10:16Der er
14 kommentarer og 1 løsning
StringReplace for langsom. Hvad er hurtigere?
Hej,
Jeg har en .txt fil paa ca. 3 MB. Filen indeholder forskellige tegn, som skal erstattes af andre tegn (f.eks. skal '_' erstattes af '').
Med StringReplace er det som om programmet crasher; i hvert fald tager det laengere end 1 time (indtil videre) at komme igennem filen, hvilket i mit tilfaelde er totalt uacceptabelt.
Hvis jeg forsoeger at aabne filen i Editor, og erstatte tegnene her, crasher Editor ligeledes efter kort tid.
Hvilke muligheder findes der ellers til en saadan aktion?
Min PC: 1.6 GHz 2 GB RAM Windows XP SP3
Mit testprogram: procedure TForm1.Button1Click(Sender: TObject); var SL1: TStringList; begin SL1 := TStringList.Create; try SL1.LoadFromFile('D:\Temp\Test.txt'); SL1.Text := StringReplace(SL1.Text, '_', '', [rfReplaceAll]); SL1.SaveToFile('D:\Temp\Test2.txt'); finally SL1.Free; end; end;
I dette særtema om aspekter af AI ser vi på skiftet fra sprogmodeller til AI-agenter, og hvordan virksomheder kan navigere i spændet mellem teknologisk hastighed og behovet for menneskelig kontrol.
Min fil bestaar aabenbart kun af 2 (lange) linier, saa det er stort set samme resultat. Filen er en .xml fil, som jeg har omdoebt til .txt for at udskifte ovennaevnte tegn.
Uden at være expert på feltet vil jeg tro, at den bedste måde at løse problemet på, er at du på en eller anden måde må dele den MAXI-streng op i mindre strenge.
Jeg MENER at have læst et eller andet sted (husker ikke hvor), at Delphi laver en kopi af den streng den ændrer og med en streng på 1GB (eller mere !) skal der ikke ret mange ændringer til før du får et massivt mem-overflow og prgm-krash.
Men da XML filer (som HTML-filer) styres af TAGS - kan du ikke dele dem op i XML-tags linier ?
Kristian
Et alternativ er at læse filen TEGN for TEGN og skifte tilsvarende (som foreslået af mcb2001).
Hej, jeg bruger altid blot en pChar (pointer) til sådanne opgaver. Der behøver man ikke at kalde "length" eller bruge "for". i stedet læser man blot indtil "#0" (NULL) og så kan man lave sin egen formattering undervejs. eksempel:
const NULL = #0; //... var p:pChar;s:str; //...
s:='123_123_123'; p:=pointer(s); if p<>nil then while p^<> NULL do begin if p^='_' then p^:=' ';//erstat underscore med mellemrum inc(p) end;
p.s. ved brug af pointer, er længden på strengen "sådan set" underordnet, da man netop er uden om "length" osv. Det mest rigtige er i virkeligheden nok at beholde det hele i et stream. Et memory stream er blot et "stream" med en indbygget pointer, som holder dataene. Så er der slet ikke behov for at bruge en string.
procedure TForm1.Button1Click(Sender: TObject); var p:pChar; SL1: TStringList; s: String; TickCnt: Integer; begin SL1 := TStringList.Create; try TickCnt := GetTickCount; SL1.LoadFromFile('D:\Temp\Test.txt'); s := SL1.Text; p:=pointer(s); if p<>nil then while p^<> NULL do begin if p^='W' then p^:='T';//erstat W med T. inc(p); end; SL1.Text := s; SL1.SaveToFile('D:\Temp\Test2.txt'); ShowMessage(IntToStr(GetTickCount-TickCnt)); finally SL1.Free; end; end;
Hos mig tog det 109 ms.
Saa skal jeg bare have den til at acceptere en string, men det skulle vaere til at finde ud af.
ok :) Men husk at der kan spares endnu mere, ved at beholde det i et stream. en tStringList læser og skriver filer via et stream i forvejen, så ved at undgå listen, springer man et helt "niveau" over...
ps. jeg bruger selv en pointer som en "standard" variabel type (altså jeg bruger den lige så tit som et tal eller en streng). Når man har vænnet sig til det, er det faktisk lettere i mange situationer, såsom at lave case formattering eller tælle antal tegn i en tekst / lign. Derudover er det langt hurtigere.
Har sågar læst et sted, at pointere en "dying age". Det er da det værste vås at lukke ud på internet. Min mening er, at pointere er yderst relevant, for optimal software
mbsnet>>Ellers tak for tilbudet jeg har hvad jeg skal bruge i de nretning.
Hvad angår pointere så skal du lige passe på når du kommer ind i UicodeVerden, fordi hver tegn består af 1,2 eller 4 bytes. I øvrigt hvis du alligevel vil have en pointer til dine data så ladvære med at bruge TStringList til at loade filen ind med. Hvis du har 300 mb data i en fil tager den 8-900 millioner år at loade (overdrivelse fremme forståelsen) brug i stedet MapViewofFile.
procedure TFastFile.CloseFile; begin if (FStart <> nil) then UnmapViewOfFile(FStart);
FFileName := '';
if Assigned(MemoryStream) then FreeAndNil(MemoryStream);
ClearData; end;
constructor TFastFile.Create; begin inherited; FRefreshAfterAppend := False; ClearData; end;
destructor TFastFile.Destroy; begin CloseFile; inherited; end;
function TFastFile.EndOfFile: Boolean; begin Result := FPos >= FEnd; end;
function TFastFile.GetDataKind: TDataKind; var UnicodeMarker: WORD; begin Result := dkAnsi;
if FDataLength <= 1 then Exit;
UnicodeMarker := (Word(FStart[0]) shl 8) or WORD(FStart[1]);
if UnicodeMarker = $FFFE then Result := dkUniCode; end;
procedure TFastFile.InternalReadFile; var FileHandle: Int64; FileMappingHandle: THandle; Size: Int64; begin if not FileExists(Filename) then begin FileHandle := FileCreate(FileName); FileClose(FileHandle); end;
FileHandle := SysUtils.FileOpen(Filename, fmopenread or fmsharedenynone);
if FileHandle = 0 then Exit; //error
Size := GetFileSize(FileHandle, nil); if Size <= 0 then begin CloseHandle(FileHandle); Exit; end;
function TFastFile.ReadBuffer(ABytesToRead: Integer): pByteArray; begin ABytesToRead := Min(ABytesToRead, FEnd - FPos); GetMem(Result, ABytesToRead); Move(FPos^, Result^, ABytesToRead); end;
function TFastFile.ReadLineA: string; var P: PChar; Tmp, TmpA: string; begin p := FPos; TmpA := '';
while (not (P^ in [#10, #13])) and (P <= FEnd) do begin if p^ = #0 then begin SetString(Tmp, FPos, P - FPos); TmpA := TmpA + Tmp; Inc(P); FPos := P; end else Inc(P); end;
SetString(Result, FPos, P - FPos);
if TmpA <> '' then Result := TmpA + Result;
if P^ = #13 then Inc(P);
if P^ = #10 then Inc(P);
FPos := P; end;
function TFastFile.ReadLineW: string; var P: PWideChar; Tmp, TmpA: string; begin p := PWideChar(FPos); TmpA := '';
while (not InOpSet(P^, [#10, #13])) and (P <= FEnd) do begin if p^ = #0 then begin Tmp := WideCharLenToString(PWideChar(FPos), P - FPos); TmpA := TmpA + Tmp; Inc(P); FPos := pointer(P); end else Inc(P); end;
Result := WideCharLenToString(PWideChar(FPos), P - FPos);
if TmpA <> '' then Result := TmpA + Result;
if P^ = #13 then Inc(P);
if P^ = #10 then Inc(P);
FPos := pointer(P); end;
procedure TFastFile.SetData(Data: PChar; DataLength: Int64); begin ClearData;
procedure TFastFile.SetRefreshAfterAppend(const Value: Boolean); begin FRefreshAfterAppend := Value; end;
end.
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.