robertjohns 0 Posted November 21, 2022 How can I find all instances of a string in a file , replace all instance of the string and save the copy of the file in Delphi Share this post Link to post
aehimself 396 Posted November 21, 2022 (edited) 1 hour ago, robertjohns said: How can I find all instances of a string in a file , replace all instance of the string and save the copy of the file in Delphi TFile.WriteAllText('OutFileName.txt', TFile.ReadAllText('FileName.txt').Replace('Replace from', 'Replace To')); Edited November 21, 2022 by aehimself Share this post Link to post
robertjohns 0 Posted November 22, 2022 6 hours ago, aehimself said: TFile.WriteAllText('OutFileName.txt', TFile.ReadAllText('FileName.txt').Replace('Replace from', 'Replace To')); Thanks , no it is not a text file it is binary file where I need to search all instances of t.o.p.s and need to replace only t as h in all the instances by inc method and save the file Share this post Link to post
jjw 0 Posted November 22, 2022 9 hours ago, robertjohns said: How can I find all instances of a string in a file , replace all instance of the string and save the copy of the file in Delphi Will this work? memo2.Text := StringReplace(memo1.Text, 'TextToRepace', 'ReplaceWith', [rfReplaceAll, rfIgnoreCase]); Share this post Link to post
robertjohns 0 Posted November 22, 2022 40 minutes ago, jjw said: Will this work? memo2.Text := StringReplace(memo1.Text, 'TextToRepace', 'ReplaceWith', [rfReplaceAll, rfIgnoreCase]); StringReplace method will not work for this .. because we need to loop and find the all similar occurrences of t.o.p.s in whole the files and all the found occurrences need to be replaced by t to h and then after change need to save it. Share this post Link to post
PeterBelow 238 Posted November 22, 2022 10 hours ago, robertjohns said: Thanks , no it is not a text file it is binary file where I need to search all instances of t.o.p.s and need to replace only t as h in all the instances by inc method and save the file Since the replacement does not change the total size of the data you can load the file into a buffer suitable for binary data, do the replacement in this buffer and finally write it back to the file. A suitable buffer would be a TMemoryStream. The question is what these '.' bytes in your target string are. If these are actually zero bytes displayed as '.' in a hex viewer the target would be simply the UTF16 string 'tops', if these are real dot characters it would probably rather be the ANSI string 't.o.p.s'. You can probably answer that by looking at the byte following the 's', it should be 0 if this is UTF16 text. Let's assume it is, then the algorithm would be like this (untested!): procedure ReplaceBytesInFile(const aFilename: string; const aSearchBytes, aReplaceBytes: TBytes); var LBuffer: TMemoryStream; LNumBytes: Integer; LPos, LEnd: PByte; begin LNumBytes := Length(aSearchBytes); Assert(LNumBytes = Length(aReplaceBytes), 'Arrays have to be of the same length!'); if LNumBytes = 0 then Exit; LBuffer := TMemoryStream.Create(); try LBuffer.LoadFromFile(aFilename); LPos := LBuffer.Memory; LEnd := LPos; Inc(LEnd, LBuffer.Size - LNumBytes ); while LPos < LEnd do begin if LPos^ = aSearchBytes[0] then begin if CompareMem(LPos, @aSearchBytes[0], LNumBytes) then begin CopyMemory(LPos, @aReplaceBytes[0], LNumBytes); // or // Move( aReplaceBytes[0], LPos^, LNumBytes ); Inc(LPos, LNumBytes); end else Inc(LPos); end else Inc(LPos); end; LBuffer.SaveToFile(aFilename); finally LBuffer.Free; end; end; ReplaceBytesInFile(theFilename, TEncoding.Unicode.GetBytes('tops'), TEncoding.Unicode.GetBytes('hops') ); Share this post Link to post
robertjohns 0 Posted November 22, 2022 9 hours ago, robertjohns said: t.o.p.s 1 hour ago, PeterBelow said: Since the replacement does not change the total size of the data you can load the file into a buffer suitable for binary data, do the replacement in this buffer and finally write it back to the file. A suitable buffer would be a TMemoryStream. The question is what these '.' bytes in your target string are. If these are actually zero bytes displayed as '.' in a hex viewer the target would be simply the UTF16 string 'tops', if these are real dot characters it would probably rather be the ANSI string 't.o.p.s'. You can probably answer that by looking at the byte following the 's', it should be 0 if this is UTF16 text. Let's assume it is, then the algorithm would be like this (untested!): procedure ReplaceBytesInFile(const aFilename: string; const aSearchBytes, aReplaceBytes: TBytes); var LBuffer: TMemoryStream; LNumBytes: Integer; LPos, LEnd: PByte; begin LNumBytes := Length(aSearchBytes); Assert(LNumBytes = Length(aReplaceBytes), 'Arrays have to be of the same length!'); if LNumBytes = 0 then Exit; LBuffer := TMemoryStream.Create(); try LBuffer.LoadFromFile(aFilename); LPos := LBuffer.Memory; LEnd := LPos; Inc(LEnd, LBuffer.Size - LNumBytes ); while LPos < LEnd do begin if LPos^ = aSearchBytes[0] then begin if CompareMem(LPos, @aSearchBytes[0], LNumBytes) then begin CopyMemory(LPos, @aReplaceBytes[0], LNumBytes); // or // Move( aReplaceBytes[0], LPos^, LNumBytes ); Inc(LPos, LNumBytes); end else Inc(LPos); end else Inc(LPos); end; LBuffer.SaveToFile(aFilename); finally LBuffer.Free; end; end; ReplaceBytesInFile(theFilename, TEncoding.Unicode.GetBytes('tops'), TEncoding.Unicode.GetBytes('hops') ); Thanks the code works but I am sorry I did not explained my question properly . yes '.' represents 00 in hex viewer . HEX value for t.o.p.s is 74 00 6f 00 70 00 73 and HEX value for h.o.p.s is 68 00 6f 00 70 00 73 so I have to change hex value from 74 to 68 value should be changed by hex method not tops to hops Thanks in advance Share this post Link to post
Fr0sT.Brutal 900 Posted November 23, 2022 Break the task into steps. Implement all the steps you can. Ask for hints on the steps you can't. Share this post Link to post
PeterBelow 238 Posted November 23, 2022 (edited) On 11/22/2022 at 5:00 PM, robertjohns said: Thanks the code works but I am sorry I did not explained my question properly . yes '.' represents 00 in hex viewer . HEX value for t.o.p.s is 74 00 6f 00 70 00 73 and HEX value for h.o.p.s is 68 00 6f 00 70 00 73 so I have to change hex value from 74 to 68 value should be changed by hex method not tops to hops Thanks in advance So what is the problem? The procedure takes an array of bytes, just feed it the correct one, e. g. var theFile: string; SearchFor, ReplaceWith: TBytes; begin SearchFor := [$74, $00, $6f, $00, $70, $00, $73]; ReplaceWith := [$68, $00, $6f, $00, $70, $00, $73]; ReplaceBytesInFile(theFile, SearchFor, ReplaceWith); Edited November 23, 2022 by PeterBelow Share this post Link to post
robertjohns 0 Posted November 26, 2022 On 11/23/2022 at 10:58 PM, PeterBelow said: So what is the problem? The procedure takes an array of bytes, just feed it the correct one, e. g. var theFile: string; SearchFor, ReplaceWith: TBytes; begin SearchFor := [$74, $00, $6f, $00, $70, $00, $73]; ReplaceWith := [$68, $00, $6f, $00, $70, $00, $73]; ReplaceBytesInFile(theFile, SearchFor, ReplaceWith); Will ReplaceBytesInFile function work for multiple hex changes like If I try to replace all these hex lines by other hex $76, $00, $61, $00, $6F, $00, $70, $00, $5F, $00, $00 $5F, $00, $74, $00, $7A, $00, $5F, $00, $00 $77, $00, $68, $00, $79, $00, $70, $00, $5F, $00, $00 $44, $10, $6D, $00, $6F, $00, $64, $00, $65, $00, $6D, $00, $5F, $00, $00 $69, $10, $00, $00, $6C, $00, $75, $00, $65, $00, $74, $00, $6F, $00, $6F, $00, $74, $00, $68, $00, $5F, $00, $00 $6F, $10, $6D, $00, $64, $00, $74, $00, $70, $00, $73, $00, $65, $00, $63, $00, $61, $00, $70, $00, $70, $00, $5F, $00, $00 $65, $10, $6D, $00, $64, $00, $74, $00, $70, $00, $5F, $00, $00 $00, $10, $61, $00, $00, $00, $6C, $00, $5F, $00, $00 $34, $10, $64, $00, $73, $00, $70, $00, $5F, $00, $00 $8C, $10, $6B, $00, $65, $00, $79, $00, $6D, $00, $61, $00, $73, $00, $74, $00, $65, $00, $72, $00, $5F, $00, $00 $3B, $00, $00, $00, $6F, $00, $6F, $00, $74, $00, $5F, $00, $00 Share this post Link to post
PeterBelow 238 Posted November 26, 2022 Please use your brain, its not just there to keep your head from imploding, you know . The code works for every byte sequence as long as both arrays are of the same length, so the total size of the data does not change. Share this post Link to post
robertjohns 0 Posted November 26, 2022 45 minutes ago, robertjohns said: Will ReplaceBytesInFile function work for multiple hex changes like If I try to replace all these hex lines by other hex $76, $00, $61, $00, $6F, $00, $70, $00, $5F, $00, $00 $5F, $00, $74, $00, $7A, $00, $5F, $00, $00 $77, $00, $68, $00, $79, $00, $70, $00, $5F, $00, $00 $44, $10, $6D, $00, $6F, $00, $64, $00, $65, $00, $6D, $00, $5F, $00, $00 $69, $10, $00, $00, $6C, $00, $75, $00, $65, $00, $74, $00, $6F, $00, $6F, $00, $74, $00, $68, $00, $5F, $00, $00 $6F, $10, $6D, $00, $64, $00, $74, $00, $70, $00, $73, $00, $65, $00, $63, $00, $61, $00, $70, $00, $70, $00, $5F, $00, $00 $65, $10, $6D, $00, $64, $00, $74, $00, $70, $00, $5F, $00, $00 $00, $10, $61, $00, $00, $00, $6C, $00, $5F, $00, $00 $34, $10, $64, $00, $73, $00, $70, $00, $5F, $00, $00 $8C, $10, $6B, $00, $65, $00, $79, $00, $6D, $00, $61, $00, $73, $00, $74, $00, $65, $00, $72, $00, $5F, $00, $00 $3B, $00, $00, $00, $6F, $00, $6F, $00, $74, $00, $5F, $00, $00 Thanks .. No Actually I am confused with var theFile: string; SearchFor, ReplaceWith: TBytes; begin SearchFor := [$76, $00, $61, $00, $6F, $00, $70, $00, $5F, $00, $00]; ReplaceWith := [$00, $00, $61, $00, $6F, $00, $70, $00, $5F, $00, $5F]; ReplaceBytesInFile(theFile, SearchFor, ReplaceWith); $76, $00, $61, $00, $6F, $00, $70, $00, $5F, $00, $00 $5F, $00, $74, $00, $7A, $00, $5F, $00, $00 $77, $00, $68, $00, $79, $00, $70, $00, $5F, $00, $00 $44, $10, $6D, $00, $6F, $00, $64, $00, $65, $00, $6D, $00, $5F, $00, $00 $69, $10, $00, $00, $6C, $00, $75, $00, $65, $00, $74, $00, $6F, $00, $6F, $00, $74, $00, $68, $00, $5F, $00, $00 $6F, $10, $6D, $00, $64, $00, $74, $00, $70, $00, $73, $00, $65, $00, $63, $00, $61, $00, $70, $00, $70, $00, $5F, $00, $00 $65, $10, $6D, $00, $64, $00, $74, $00, $70, $00, $5F, $00, $00 $00, $10, $61, $00, $00, $00, $6C, $00, $5F, $00, $00 $34, $10, $64, $00, $73, $00, $70, $00, $5F, $00, $00 $8C, $10, $6B, $00, $65, $00, $79, $00, $6D, $00, $61, $00, $73, $00, $74, $00, $65, $00, $72, $00, $5F, $00, $00 $3B, $00, $00, $00, $6F, $00, $6F, $00, $74, $00, $5F, $00, $00 How can declare other hex lines for variable SearchFor, ReplaceWith: TBytes; Thanks in advance Share this post Link to post
programmerdelphi2k 237 Posted November 26, 2022 (edited) maybe, needs test about memory usage, you can open file as "bytes" and in a looping, search your byte sequences! for example using "MemoryStream" search for "xx" bytes .... try find "Bitmap" signature on stream type TMyBuffer = array [0 .. 1] of byte; function MyFindBitmapSignature(AMemoryStream: TStringStream): int64; function MyIsTheSame(const ABuffer: TMyBuffer; const AFind: TMyBuffer): boolean; // better for many bytes comparing begin result := false; // if (Length(ABuffer) <> Length(AFind)) then exit; // for var i: integer := 0 to (Length(ABuffer)-1) do if (ABuffer[..i..] <> AFind[..i..]) then exit(false); // result := true; end; var LBuffer: TMyBuffer; LFind : TMyBuffer; begin result := -1; AMemoryStream.Position := 0; LFind[0] := 66; LFind[1] := 77; // for var i: int64 := 0 to ((AMemoryStream.Size div 2) - 1) do begin AMemoryStream.Read(LBuffer, 2); // if MyIsTheSame(LBuffer, LFind) then exit(i * 2); // (* if (LBuffer[0] = 66) and (LBuffer[1] = 77) then // 'nul, nul,... "BM" ...' begin exit(i * 2); end; *) end; end; Edited November 26, 2022 by programmerdelphi2k Share this post Link to post
robertjohns 0 Posted November 26, 2022 I have a binary file n which I need to make changes. Share this post Link to post
programmerdelphi2k 237 Posted November 26, 2022 (edited) no problem, use the same idea about! read your binary-file as "bytes"... -- see on Help: TStream subclasses, like: TMemoryStream, TStringStream, TFileStream, etc... -- if found the "correspondent values", just store in a other stream (or the same) and when end, just "save to file" --- if a[12] = 123 then a[12]=100; Edited November 26, 2022 by programmerdelphi2k Share this post Link to post
robertjohns 0 Posted November 26, 2022 Waiting for @PeterBelow response, he definitely may be having any easy logic like his previous function. Share this post Link to post
PeterBelow 238 Posted November 27, 2022 (edited) 23 hours ago, robertjohns said: Thanks .. No Actually I am confused with var theFile: string; SearchFor, ReplaceWith: TBytes; begin SearchFor := [$76, $00, $61, $00, $6F, $00, $70, $00, $5F, $00, $00]; ReplaceWith := [$00, $00, $61, $00, $6F, $00, $70, $00, $5F, $00, $5F]; ReplaceBytesInFile(theFile, SearchFor, ReplaceWith); $76, $00, $61, $00, $6F, $00, $70, $00, $5F, $00, $00 $5F, $00, $74, $00, $7A, $00, $5F, $00, $00 $77, $00, $68, $00, $79, $00, $70, $00, $5F, $00, $00 $44, $10, $6D, $00, $6F, $00, $64, $00, $65, $00, $6D, $00, $5F, $00, $00 $69, $10, $00, $00, $6C, $00, $75, $00, $65, $00, $74, $00, $6F, $00, $6F, $00, $74, $00, $68, $00, $5F, $00, $00 $6F, $10, $6D, $00, $64, $00, $74, $00, $70, $00, $73, $00, $65, $00, $63, $00, $61, $00, $70, $00, $70, $00, $5F, $00, $00 $65, $10, $6D, $00, $64, $00, $74, $00, $70, $00, $5F, $00, $00 $00, $10, $61, $00, $00, $00, $6C, $00, $5F, $00, $00 $34, $10, $64, $00, $73, $00, $70, $00, $5F, $00, $00 $8C, $10, $6B, $00, $65, $00, $79, $00, $6D, $00, $61, $00, $73, $00, $74, $00, $65, $00, $72, $00, $5F, $00, $00 $3B, $00, $00, $00, $6F, $00, $6F, $00, $74, $00, $5F, $00, $00 How can declare other hex lines for variable SearchFor, ReplaceWith: TBytes; Thanks in advance D11 actually accepts initialized TBytes arrays in typed constants, so yo can do this: procedure ReplaceBytesInFile(aStream: TMemoryStream; const aSearchBytes, aReplaceBytes: TBytes); var LNumBytes: Integer; LPos, LEnd: PByte; begin LNumBytes := Length(aSearchBytes); Assert(LNumBytes = Length(aReplaceBytes), 'Arrays have to be of the same length!'); if LNumBytes = 0 then Exit; LPos := aStream.Memory; LEnd := LPos; Inc(LEnd, aStream.Size - LNumBytes ); while LPos < LEnd do begin if LPos^ = aSearchBytes[0] then begin if CompareMem(LPos, @aSearchBytes[0], LNumBytes) then begin CopyMemory(LPos, @aReplaceBytes[0], LNumBytes); // or // Move( aReplaceBytes[0], LPos^, LNumBytes ); Inc(LPos, LNumBytes); end else Inc(LPos); end else Inc(LPos); end; {while} end; type TDataRec = record SearchFor, ReplaceBy: TBytes end; const Data: array [0..1] of TDataRec = ( ( SearchFor: [$74, $00, $6f, $00, $7, $00, $73]; ReplaceBy: [$68, $00, $6f, $00, $7, $00, $73]), ( SearchFor: [$76, $00, $61, $00, $6F, $00, $70, $00, $5F, $00, $00]; ReplaceBy: [$00, $00, $61, $00, $6F, $00, $70, $00, $5F, $00, $00]) // add more data as needed, adjust array upper bound accordingly ); procedure TForm2.Test; var I: Integer; theFile: string; LBuffer: TMemoryStream; begin theFile := 'pathname here'; LBuffer := TMemoryStream.Create(); try LBuffer.LoadFromFile(theFile); for I := Low(Data) to High(Data) do ReplaceBytesInFile(LBuffer, Data[I].SearchFor, Data[I].ReplaceBy); LBuffer.SaveToFile(theFile); finally LBuffer.Free; end; end; Untested! Edited November 27, 2022 by PeterBelow Share this post Link to post
robertjohns 0 Posted November 27, 2022 Thanks a lot for your help but I get error Out of memory while expanding memory stream. Share this post Link to post
PeterBelow 238 Posted November 27, 2022 48 minutes ago, robertjohns said: Thanks a lot for your help but I get error Out of memory while expanding memory stream. How large is the file you are trying to load? Did you build for a 32 or 64 bit platform? Try the latter. Share this post Link to post
robertjohns 0 Posted November 27, 2022 (edited) 27 minutes ago, PeterBelow said: How large is the file you are trying to load? Did you build for a 32 or 64 bit platform? Try the latter. It is 32 bit Platform and the file size is 3GB Edited November 27, 2022 by robertjohns Share this post Link to post
aehimself 396 Posted November 27, 2022 9 minutes ago, robertjohns said: It is 32 bit and the file size is 3GB a 32 bit process can allocate a total of 2 GBs of RAM (or 3 if it's large address aware). Loading 3 GBs of data in the memory would use that up by itself, leaving no room for anything else (like the TMemoryStream class or any other variables). So it is expected. As @PeterBelow advised, build a 64 bit executable, or use FileStreams instead of loading everything in the memory. Share this post Link to post
programmerdelphi2k 237 Posted November 27, 2022 I think that my "propose" and TFileStream can solve it! Each step on "stream-buffer", and replace with your "bytes". then it's not necessary "up" all data on memory! Share this post Link to post
robertjohns 0 Posted November 28, 2022 If aStream is TFileStream then how to use LPos := aStream.Memory; Share this post Link to post
programmerdelphi2k 237 Posted November 28, 2022 (edited) try this: function MyMinValue(const A, B: integer): integer; begin result := A; // if (A > B) then result := B; end; procedure TForm1.Button1Click(Sender: TObject); var MyFileStreamSource: TFileStream; MyFileStreamTarget: TFileStream; MyFileSourceSize : int64; MyBuffer : TBytes; // array of byte; MyBufferSize : integer; begin // See on Help about more infos: fmOpenRead, fmCreate, fmShareExclusive, etc... // DeleteFile('d:\newfile.mp4'); // MyFileStreamSource := TFileStream.Create('d:\bohemian-rhapsody-queen-how-to-play-guitar.mp4', fmOpenRead or fmShareExclusive); try MyFileSourceSize := MyFileStreamSource.Size; MyBufferSize := MyMinValue(512, MyFileSourceSize); // SetLength(MyBuffer, MyBufferSize); // MyFileStreamTarget := TFileStream.Create('d:\newfile.mp4', fmCreate or fmShareExclusive); try MyFileStreamSource.Position := 0; // while (MyFileSourceSize > 0) do begin MyFileStreamSource.Read(MyBuffer[0], MyBufferSize); // read and put on the next position... // // what to do with MyBuffer values... // MyFileStreamTarget.Write(MyBuffer[0], MyBufferSize); // write and put on the next position... // Dec(MyFileSourceSize, MyBufferSize); // MyBufferSize := MyMinValue(512, MyFileSourceSize); // NOTE: verify this value, if ok .. end; finally MyFileStreamTarget.Free; end; finally MyFileStreamSource.Free; end; // ShowMessage('end'); end; Edited November 28, 2022 by programmerdelphi2k Share this post Link to post
aehimself 396 Posted November 28, 2022 MyFileSourceSize := MyFileStreamSource.Size; Is unnecessary, MyFileSourceSize.Size - MyFileSourceSize.Position will always return the unprocessed bytes. MyFileStreamSource.Read(MyBuffer[0], MyBufferSize); // read and put on the next position... Make sure you also check if .Read returns MyBufferSize. Otherwise you will have "rouge" data what you will write back to the destination, effectively corrupting the output. Also, the pseudocode above won't handle data, which is interrupted by the end of a buffer. Share this post Link to post