Delphi replace a line - delphi

I have a .txt file and I want to replace a line with a new one. These are the steps:
Read in the .txt file
Save Source to a TStringList
Modify some data in a particular line
Save the new data back to the original file.
How do I do this?

Like this:
var
Strings: TStringList;
....
Strings := TStringList.Create;
try
Strings.LoadFromFile(FileName);
Strings[LineIndex] := NewValue;
Strings.SaveToFile(FileName);
finally
Strings.Free;
end;

With newer Delphi's you can get the contents of a file as an array of strings in a single call TFile.ReadAllLines().
program TestModifyLine; {$APPTYPE CONSOLE}
uses Types,IoUtils;
procedure ModifyLine(fn:string;Index:integer;NewText:String);
var lines:TStringDynArray;
begin
lines := TFile.ReadAllLines(fn);
lines[Index] := NewText;
TFile.WriteAllLines(fn,lines);
end;
begin
ModifyLine('test.txt',12,'hello');
end.

If you don't want to waste memory loading the entire source file at one time, you can use TStreamReader and TStreamWriter to read/write the files one line at a time, modifying the desired line after reading it and before writing it.
Var
Reader: TStreamReader;
Writer: TStreamWriter:
Line: String;
LineNum: Integer;
Begin
Reader := TStreamReader.Create(...);
Writer := TStreamWriter.Create(...);
While not Reader.EndOfStream do
Begin
Line := Reader.ReadLine;
Inc(LineNum);
If LineNum = ... Then
Begin
...
End;
Writer.WriteLine(Line);
End;
Writer.Free;
Reader.Free;
End;

Related

Delphi - open next file from a list once procedure finishes on the first

I have a total of 3214 .doc. I need to open the first file, copy its contents, paste it in a RichEdit, extract some text, insert it into a database then move on to the next file and repeat the procedure.
So far I've managed to:
Open the 1st .doc/any 1 .doc only
Copy the content and paste it in the RichEdit
Extract the text from the RichEdit
Insert the extracted text into the database
Close the opened .doc and clear the content of RichEdit
I've loaded all 3214 filenames, in order, into a Memo.
Once I finish with the 1st file from the list, how do I now make it move to the next .doc from the list and do the same thing, repeating this till I finish all the 3214 .doc files? Currently reading about loops but I can't figure it out yet.
Code so far:
procedure TForm1.Button4Click(Sender: TObject);
var
content: string;
StartPos: Integer;
endPos: Integer;
i: integer;
fname: string;
WordApp : Variant;
begin
WordApp := CreateOleObject('Word.Application');
for i := 1 to 1 do
fname := Memo1.Lines[i - 1];
WordApp.Visible := True;
WordApp.Documents.Open('C:\Users\tcsh\Desktop\all\'+fname);
WordApp.ActiveDocument.Select;
WordApp.Selection.Copy;
RichEdit1.Lines.Add(WordApp.Selection);
WordApp.documents.item(1).Close;
WordApp.Quit;
content:= RichEdit1.Text;
//<text extract code is here>
begin
//<sql code is here>
end;
RichEdit1.Clear;
Edit1.Clear;
Edit2.Clear;
Edit3.Clear;
Edit4.Clear;
Edit5.Clear;
Edit7.Clear;
Edit8.Clear;
//the TEdit's hold the extracted text so the sql can retrieve it from them and insert into the database
end;
for i := 1 to 1 do
Hmmm, that will only run once..
You may also want to try:
Create the WordApp object in each iteration ,
Add a time delay in between each iteration (using Sleep and Application.ProcessMessages) (as LU RD points out this is not necessary)
Code sample below:
for i := 0 to Memo1.Lines.Count - 1 do
begin
WordApp := CreateOleObject('Word.Application');
fname := Memo1.Lines[i];
WordApp.Visible := True;
WordApp.Documents.Open(fname);
WordApp.ActiveDocument.Select;
WordApp.Selection.Copy;
Memo2.Lines.Add(WordApp.Selection);
Memo2.Lines.Add('===');
WordApp.documents.item(1).Close;
WordApp.Quit;
//Sleep(1000); -> not needed
//Application.ProcessMessages;
end;
Try it with System.IOUtils.TDirectory.GetFiles
GetFiles('C:\temp\', '*.doc');
Here is an example
You'll find a few here on SO.
Update
...
var
line: string;
...
for line in Memo1.Lines do begin
<your code per file>
ShowMessage(line);
end

Saved data from TBlobField is corrupted for lengths >= 100KB

I'm modifying a program that is written in Delphi 6.0
I have a table in Oracle with a BLOB column named FILE_CONTENT.
I have already managed to upload an XML File that is about 100 KB. I have verified that the file content was correctly uploaded using SQL Developer.
The problem I have is when I try to download back the file content from DB to a file. This is an example code I'm using to donwload it:
procedure TfrmDownload.Save();
var
fileStream: TFileStream;
bField: TBlobField;
begin
dmDigital.qrGetData.Open;
dmDigital.RequestLive := True;
bField := TBlobField(dmDigital.qrGetData.FieldByName('FILE_CONTENT'));
fileStream := TFileStream.Create('FILE.XML', fmCreate);
bField.SaveToStream(fileStream);
FlushFileBuffers(fileStream.Handle);
fileStream.Free;
dmDigital.qrGetData.Close;
end;
The previous code already downloads the file content to FILE.XML. I'm using RequestLive:=True to be able to download a large BLOB (otherwise the file content is truncated to 32K max)
The resulting file is the same size as the original file. However, when I compare the downloaded file with the original one there are some differences (for example the last character is missing and other characters are also changed), therefore it seems to be a problem while downloading the content.
Do you know what cuould be wrong?
The problem seems to be related to Delphi code because I already tried with C# and the file content is downloaded correctly.
Don't use TBlobField.SaveToStream() directly, use TDataSet.CreateBlobStream() instead (which is what TBlobField.SaveToStream() uses internally anyway):
procedure TfrmDownload.Save;
var
fileStream: TFileStream;
bField: TField;
bStream: TStream;
begin
dmDigital.qrGetData.Open;
try
dmDigital.RequestLive := True;
bField := dmDigital.qrGetData.FieldByName('FILE_CONTENT');
bStream := bField.DataSet.CreateBlobStream(bField, bmRead);
try
fileStream := TFileStream.Create('FILE.XML', fmCreate);
try
fileStream.CopyFrom(bStream, 0);
FlushFileBuffers(fileStream.Handle);
finally
fileStream.Free;
end;
finally
bStream.Free;
end;
finally
dmDigital.qrGetData.Close;
end;
end;
TDataSet.CreateBlobStream() allows the DataSet to decide the best way to access the BLOB data. If the returned TStream is not delivering the data correctly, then either the TStream class implementation that CreateBlobStream() uses is broken, or the underlying DB driver is buggy. Try taking CopyFrom() out of the equation so you can verify the data as it is being retrieved:
procedure TfrmDownload.Save;
const
MaxBufSize = $F000;
var
Buffer: array of Byte;
N: Integer;
fileStream: TFileStream;
bField: TField;
bStream: TStream;
begin
dmDigital.qrGetData.Open;
try
dmDigital.RequestLive := True;
bField := dmDigital.qrGetData.FieldByName('FILE_CONTENT');
bStream := bField.DataSet.CreateBlobStream(bField, bmRead);
try
fileStream := TFileStream.Create('FILE.XML', fmCreate);
try
//fileStream.CopyFrom(bStream, 0);
SetLength(Buffer, MaxBufSize);
repeat
N := bStream.Read(PByte(Buffer)^, MaxBufSize);
if N < 1 then Break;
// verify data here...
fileStream.WriteBuffer(PByte(Buffer)^, N);
until False;
FlushFileBuffers(fileStream.Handle);
finally
fileStream.Free;
end;
finally
bStream.Free;
end;
finally
dmDigital.qrGetData.Close;
end;
end;

stuck with streaming file to string

okay, so I (VERY) recently started playing with lazaruz/free pascal, and I'm a little stuck with reading files with TMemoryStream and it's streaming kin.
I'm trying to write a simple base64 encoder, that can encode strings of text, or files (like images and WAVs) to then be used in html and javascript.
The following code compiles great but I get EReadError Illegal stream image when trying to load a file. I'll include the working string only procedure for reference:
procedure TForm1.TextStringChange(Sender: TObject);
begin
Memo1.Lines.Text := EncodeStringBase64(TextString.Text);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Memo1.Lines.Text := '';
Form1.BorderIcons := [biSystemMenu,biMinimize];
end;
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
begin
filename := OpenDialog1.Filename;
stream := TMemoryStream.Create;
try
StrStream := TStringStream.Create(s);
try
stream.LoadFromFile(filename);
stream.Seek(0, soFromBeginning);
ObjectBinaryToText(stream, StrStream);
StrStream.Seek(0, soFromBeginning);
Memo1.Lines.Text := EncodeStringBase64(StrStream.DataString);
finally
StrStream.Free;
end;
finally
stream.Free;
end;
end;
end;
Can anyone help me out?
You get the "illegal stream image" exception because the file you're loading probably isn't a binary DFM file. That's what ObjectBinaryToText is meant to process. It's not for arbitrary data. So get rid of that command.
You can skip the TMemoryStream, too. TStringStream already has a LoadFromFile method, so you can call it directly instead of involving another buffer.
StrStream.LoadFromFile(filename);
But a string isn't really the right data structure to store your file in prior to base64-encoding it. The input to base64 encoding is binary data; the output is text. Using a text data structure as an intermediate format means you may introduce errors into your data because of difficulties in encoding certain data as valid characters. The right interface for your encoding function is this:
function Base64Encode(Data: TStream): string;
You don't need to load the entire file into memory prior to encoding it. Just open the file with a TFileStream and pass it to your encoding function. Read a few bytes from it at a time with the stream's Read method, encode them as base64, and append them to the result string. (If you find that you need them, you can use an intermediate TStringBuilder for collecting the result, and you can add different buffering around the file reads. Don't worry about those right away, though; get your program working correctly first.)
Use it something like this:
procedure TForm1.BitBtn1Click(Sender: TObject);
var
filename: string;
stream: TStream;
begin
if OpenDialog1.Execute then begin
filename := OpenDialog1.Filename;
stream := TFileStream.Create(filename, fmOpenRead);
try
Memo1.Lines.Text := Base64Encode(stream);
finally
stream.Free;
end;
end;
end;
I never heard before about ObjectBinaryToText(), but looks like funky one. Also, what is EncodeStringBase64() function?
At first place, you shouldn't convert binary stream to text to encode it, instead you should directly B64 encode binary data. B64 algorithm is intended to work on array of bytes.
Since Delphi 6, there is EncdDecd.pas unit, which implements B64 encoding methods. I'm not sure if Lazarus/FPC have this, but if they do, your code to B64 encode file should look like this (add EncdDecd to uses list):
procedure TForm1.Button1Click(Sender: TObject);
var
instream : TFileStream;
outstream: TStringStream;
begin
if OpenDialog1.Execute then
begin
instream := TFileStream.Create(OpenDialog1.FileName, fmOpenRead or fmShareDenyNone);
try
outstream := TStringStream.Create;
try
EncodeStream(instream, outstream);
Memo1.Lines.Text := outstream.DataString;
finally
outstream.Free;
end;
finally
instream.Free;
end;
end;
end;

Delphi reading non-text file (binary)

how in Delphi could I open binary file in non-text mode?
Like C function fopen(filename,"rb")
There are a few options.
1. Use a file stream
var
Stream: TFileStream;
Value: Integer;
....
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Stream.ReadBuffer(Value, SizeOf(Value));//read a 4 byte integer
finally
Stream.Free;
end;
2. Use a reader
You would combine the above approach with a TBinaryReader to make the reading of the values simpler:
var
Stream: TFileStream;
Reader: TBinaryReader;
Value: Integer;
....
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Reader := TBinaryReader.Create(Stream);
try
Value := Reader.ReadInteger;
finally
Reader.Free;
end;
finally
Stream.Free;
end;
The reader class has lots of functions to read other data types. And you can go in the opposite direction with a binary writer.
3. Old style Pascal I/O
You can declare a variable of type File and use AssignFile, BlockRead, etc. to read from the file. I really don't recommend this approach. Modern code and libraries almost invariably prefer the stream idiom and by doing the same yourself you'll make your code easier to fit with other libraries.
You have different options, two of them are:
Use the old school approach, like the C function you pointed out:
var
F: File;
begin
AssignFile(F, 'c:\some\path\to\file');
ReSet(F);
try
//work with the file
finally
CloseFile(F);
end
end;
Use a more modern approach to create a TFileStream based on the file:
var
F: TFileStream;
begin
F := TFileStream.Create('c:\some\path\to\file', fmOpenRead);
try
//work with the file
finally
F.Free;
end;

Embed Excel file in Delphi 5

I'm trying embed an Excel file into my Delphi 5 application, so I can avoid my users just deleting the file accidentally.
Using the embedded file, I create it on disk with a Save dialog, and then open it with the Excel := CreateOleObject('Excel.Application'); method. I've seen examples on how to load a resource, using THandles, but I don't seem to get it working with Excel.WorkBooks.Open(EmbeddedExcelFile);.
Have you had to do something like this before? How would you do it?
Thanks!
You have to include the file as a resource. Say you have a blah.xls
Create a blah.rc file with the following content
blah RCDATA blah.xls
compile it with the resource compiler into blah.res
embed the RES file within the executable
{$R 'blah.res'}
in your application code, extract the file and run it with this code
procedure ExtractAndRun(resourceID:string; resourceFn:string);
var
resourceStream: TResourceStream;
fileStream: TFileStream;
tempPath: string;
fullFileName: string;
begin
tempPath:=GetTempDir;
FullFilename:=TempPath+'\'+resourceFN;
if not FileExists(FullFilename) then
begin
resourceStream := TResourceStream.Create(hInstance, resourceID, RT_RCDATA);
try
fileStream := TFileStream.Create(FullFilename, fmCreate);
try
fileStream.CopyFrom(resourceStream, 0);
finally
fileStream.Free;
end;
finally
resourceStream.Free;
end;
end;
ShellExecute(0,'open', pchar(FullFilename), nil, nil, SW_SHOWNORMAL);
end;
you'll have to add ShellApi in your uses clause
maybe you'll need this GetTempDir function
function GetTempDir: string;
var
Buffer: array[0..MAX_PATH] of char;
begin
GetTempPath(SizeOf(Buffer) - 1, Buffer);
result := StrPas(Buffer);
end;
invoke the function like this
extractAndRun('blah','blah.xls');
I am pretty sure it will not work. You have to save the file in a temp folder, alter it and and then do whatever you want.

Resources