How operate on TFileStream - delphi

Hello recently I replace TextFile with TFileStream. I never use it so I have small problem with it.
How can I add someting to my file after I assign it to variable?
How can I read someting form that file?
I need defined line form that file so I was doing something like that:
var linia_klienta:array[0..30] of string;
AssignFile(tempPlik,'klienci.txt');
Reset(tempPlik);
i:=0;
While Not Eof(tempPlik) do
begin
Readln(tempPlik,linia_klient[i]);
inc(i);
end;
CloseFile(tempPlik);
Then when line two is needed I simply
edit1.text = linia_klienta[1];

If you need to read a text file and access each line, try instead using a TStringList class with this class you can load a file, read the data (accesing each line using a index) and save the data back.
something like this
FText : TStringList;
i : integer;
begin
FText := TStringList.Create;
try
FText.LoadFromFile('C:\Foo\Foo.txt');
//read the lines
for i:=0 to FText.Count-1 do
ProcessLine(FText[i]); //do something
//Add additional lines
FText.Add('Adding a new line to the end');
FText.Add('Adding a new line to the end');
//Save the data back
FText.SaveToFile('C:\Foo\Foo.txt');
finally
FText.Free;
end;
end;
end;

I newer versions of Delphi you can use TStreamReader / TStreamWriter here is an example of using TStreamReader ... this is only for manipulating text files
var
SR : TStreamReader;
line : String;
begin
SR := TStreamReader.Create('D:\test.txt');
while not (SR.EndOfStream) do
begin
line := SR.ReadLine;
ShowMessage(line);
end;
SR.Free;
end;

TStream and its immediate descendants are mostly low-level access class. They mostly deal with generic buffers. There are some more specialized classes that descend from or use a stream to perform higher level tasks.
Since Delphi 1 TReader and TWriter could be used to read and write Delphi types directly (inlcuding strings), but they were not designed to handle "line-oriented" files (unluckily they were designed too much with component properties streaming in mind, not as a general purpose framework).
Turbo Power SysTools has a nice TStAnsiTextStream class that implements line-oriented access to text files in a way similar to that of TextFile. Since Delphi 2009 new classes (see opc0de answer) implement the same kind of access without the need of third party libraries (moreover they support different encodings thanks to Delphi 2009 extend codepage support, including Unicode).

Depending with what you want to do, its the stream class you need.
Do you want to work with text (characters with break-lines and end-of-line characters) data ?
OR, do you want to work with binary data ?
I see you are using an array of char, instead, of a string.
Do you really want to use character data as if it was binary ?
Sometimes, some applications require that case.

Related

Add ini file as resource file and read from it

I would like to add an ini file to my delphi project as a resource file.
I know where you go to add a file as a resource file:
Project > Resources and Images > Add
But once thats done what else do I need to do to be able to read from the file? I haven't used resource files before.
Is there any documentation on the process?
Thanks,
The built-in INI file classes in the RTL, providing in the System.IniFiles unit, require the INI file to be a disk file. So you could extract the resource to disk and read it from there.
If you don't like the idea of that then you could write your own INI file parser that operated on a stream rather than a file. You could use the code of TMemIniFile to guide you. Copy that code and replace LoadValues with code to read from a stream rather than a file. Or if you look hard enough then you may well find a third party INI parser that operates on streams.
If you are prepared to consider other formats then you might use JSON rather than INI. The built-in JSON parser does not require the input data to reside on disk. They can operate on in-memory strings, which sounds rather more convenient.
This above text is in fact nonsense. Thank you to Remy for pointing that out. You can use TMemIniFile and its SetStrings method to parse INI content that does not reside on disk. It goes like this:
Put your INI content into a resource as a string.
Load that resource into a string variable.
Create a TStringList, and assign the string variable to the Text property of the string list.
Create a TMemIniFile.
Call SetStrings on the TMemIniFile passing the string list.
Or:
Put your INI content into a resource as a string.
Create a TResourceStream object to read that resource.
Create a TStringList object.
Call LoadFromStream on the string list passing the resource stream.
Create a TMemIniFile.
Call SetStrings on the TMemIniFile passing the string list.
Having said all of this, it seems odd that you would choose to do this at all. Wouldn't it be simpler just to hard code the configuration information in a unit, as a series of constants?
Using the "Resources and Images" dialog, add the .ini file to the project as a RCDATA resource type. Then, you can load it at runtime like this:
uses
..., Classes, IniFiles;
var
Ini: TMemIniFile;
List: TStringList;
Strm: TResourceStream;
begin
Ini := TMemIniFile.Create;
try
List := TStringList.Create;
try
Strm := TResourceStream.Create(HInstance, 'ResourceIDHere', RT_RCDATA);
try
List.LoadFromStream(Strm);
finally
Strm.Free;
end;
Ini.SetStrings(List);
finally
List.Free;
end;
// use Ini as needed...
finally
Ini.Free;
end;
end;
To add the files in the executable, be sure to add a .rcfile (not the .ini files directly), for example inis.rc (so in the source file dpr you'll have {$R 'inis.res' 'inis.rc'}) and write in this file a list like this:
this 100 "this.ini"
that 100 "that.ini"
If you've stored the ini files in a (relative) directory, be sure to double the backslashes since this in C-syntax. (The 100here is the resource type, there's no number assigned to ini-files specifically so we'll use an unassigned number. The best next thing is 23 which is assigned to RT_HTML, see below)
If you're not using groups (and lines with just [GroupName]), I'd suggest you use plain TStringList objects and their Values property. To load them with the data, use something like this:
var
sl:TStringList;
r:TResourceStream;
begin
sl:=TStringList.Create;
try
r:=TResourceStream.Create(HInstance,'this',MAKEINTRESOURCE(100));
try
sl.LoadFromStream(r);
finally
r.Free;
end;
//sl.Values['Setting']
finally
sl.Free;
end;
end;
imho, an ini file is just a text file to distribute with an application so you can control behavior of the application in one particular environment. For example, you could store a language in "language.ini", read it from your source code, and present the GUI based on that language.
To accomplish this, your ini file contains:
[general]
language=Russian
then read it from Delphi:
...
uses Inifiles;
...
var CurrentLanguage:string;
...
Ini := TIniFile.Create('C:\somedir\languages.ini');
CurrentLanguage := Ini.ReadString('General', 'language', 'English');//if key isn't found, language is English
Ini.free();
So basically it contains TEXT info... as said above, if you add it as a resource, you might as well hardcode it. Resources should be used primarily for binary data (an image, audio file, video, etc).

The best solution to process the huge text file data in Delphi 7

I have text file like this:
"01","AAA","AAAAA"
"02","BBB","BBBBB","BBBBBBBB"
"03","CCC"
"04","DDD","DDDDD"
I want to load this text file data into temp table in sybase db. So, I need to build a program to read line by line this text file until eof. If the text file size is small, the process to read line by line is fast. But if text file size is too big (can be more than 500M), the process read line by line is too slow. I think the read line by line method not suitable for huge text file. So, need to find other solution to load text file data into db instead of read text file line by line method. Any suggestion?
Example code:
var
myFile : TextFile;
text : string;
begin
// Try to open the Test.txt file for writing to
AssignFile(myFile, 'Test.txt');
// Display the file contents
while not Eof(myFile) do
begin
ReadLn(myFile, text);
TempTable.append;
TempTable.FieldByName('Field1').asstring=Copy(text,2,2);
TempTable.FieldByName('Field2').asstring=Copy(text,7,3);
TempTable.FieldByName('Field3').asstring=Copy(text,13,5);
TempTable.FieldByName('Field4').asstring=Copy(text,21,8);
TempTable.post;
end;
// Close the file for the last time
CloseFile(myFile);
end;
Some general tips:
Ensure your TempTable is in memory, or use a fast database engine - take a look at SQlite3 or other means (like FireBird embedded, NexusDB or ElevateDB) as possible database alternatives;
If you do not use a TTable, but a true database, ensure you nest the insert within a Transaction;
For a true database, check out if you can not use ArrayDML feature, which is much faster for inserting a lot of data as you want in a remote database (like Sybase) - such Array DML is handled for instance with FireDAC AFAIK;
The FieldByName('...') method is known to be very slow: use locals TField variables instead;
When using a TextFile, assign a bigger temporary buffer;
If you are using newer Unicode versions of Delphi (2009+), using TextFile is not the best option.
So your code may be:
var
myFile : TextFile;
myFileBuffer: array[word] of byte;
text : string;
Field1, Field2, Field3, Field4: TField;
begin
// Set Field* local variables for speed within the main loop
Field1 := TempTable.FieldByName('Field1');
Field2 := TempTable.FieldByName('Field2');
Field3 := TempTable.FieldByName('Field3');
Field4 := TempTable.FieldByName('Field4');
// Try to open the Test.txt file for writing to
AssignFile(myFile, 'Test.txt');
SetTextBuf(myFile, myFileBuffer); // use 64 KB read buffer
// Display the file contents
while not Eof(myFile) do
begin
ReadLn(myFile, text);
TempTable.append;
Field1.asInteger := StrToInt(Copy(text,2,2));
Field2.asString := Copy(text,7,3);
Field3.asString := Copy(text,13,5);
Field4.asString := Copy(text,21,8);
TempTable.post;
end;
// Close the file for the last time
CloseFile(myFile);
end;
You can achieve very high speed with embedded engines, with almost no size limit, but your storage. See for instance how fast we can add content to a SQLite3 database in our ORM: about 130,000 / 150,000 rows per second in a database file, including all ORM marshalling. I also found out that SQLite3 generates much smaller database files than alternatives. If you want fast retrieval of any field, do not forget to define INDEXes in your database, if possible after the insertion of row data (for better speed). For SQLite3, there is already an ID/RowID integer primary key available, which maps your first data field, I suppose. This ID/RowID integer primary key is already indexed by SQLite3. By the way, our ORM now supports FireDAC / AnyDAC and its advanced Array DML feature.
Text files normally have a very small buffer. Look into using the SetTextBuf function to increase your performance.
var
myFile : TextFile;
text : string;
myFileBuffer: Array[1..32768] of byte;
begin
// Try to open the Test.txt file for writing to
AssignFile(myFile, 'Test.txt');
SetTextBuf(MyFile, myFileBuffer);
Reset(MyFile);
// Display the file contents
while not Eof(myFile) do
begin
ReadLn(myFile, text);
end;
// Close the file for the last time
CloseFile(myFile);
end;
In addition to what has already been said, I would also avoid using any TTable component. You would be better off using a TQuery type component (depending on the access layer you're using). Something like this :-
qryImport.SQL := 'Insert Into MyTable Values (:Field1, :Field2, :Field3, :Field4);';
Procedure ImportRecord(Const pField1, pField2, pField3, pField4 : String);
Begin
qryImport.Close;
qryImport.Params[0].AsString := pField1;
qryImport.Params[1].AsString := pField2;`
qryImport.Params[2].AsString := pField3;
qryImport.Params[3].AsString := pField4;
qryImport.ExecSQL;
End;
Hope this helps.
Another approach would be to use memory-mapped files (you can google or go torry.net to find implementations). it would not work well for files >1gb (in win32,, in win64 you can map virtually any file). It would turn all your file into PAnsiChar that you would be able to scan like a one large buffer, searching for #10 and #13 (alone or in pairs) and thus manually splitting strings.
If you use (or don't mind starting to use) the JEDI Jvcl, they have a TJvCSVDataSet which allows you to simply use your CSV file like any other dataset in Delphi, including being able to define persistent fields and use "standard" Delphi database functionality:
JvCSVDataSet1.FileName := 'MyFile.csv';
JvCSVDataSet1.Open;
while not JvCSVDataSet1.Eof do
begin
TempTable.Append; // Posts last appended row automatically;
// no need to call Post here.
// Assumes TempTable has same # of fields in the
// same order
for i := 0 to JvCSVDataSet1.FieldCount - 1 do
TempTable.Fields[i].AsString := JvCSVDataSet1.Fields[i].AsString;
JvCSVDataSet1.Next;
end;
// Post the last row appended when the loop above exited
if TempTable.State in dsEditModes then
TempTable.Post;
In Delphi 7 you can use Turbo Power SysTools TStAnsiTextStream() to read and write in a line oriented way, but using the thread safe TStream implementation and not the unsafe old pascal file interface. In later Delphi versions you will find something alike in the standard RTL (although they are a little different in their implementation), but Delphi 7 didn't offer much for text file manipulation.

How to save string into text files in Delphi?

What is the easiest way to create and save string into .txt files?
Use TStringList.
uses
Classes, Dialogs; // Classes for TStrings, Dialogs for ShowMessage
var
Lines: TStrings;
Line: string;
FileName: string;
begin
FileName := 'test.txt';
Lines := TStringList.Create;
try
Lines.Add('First line');
Lines.Add('Second line');
Lines.SaveToFile(FileName);
Lines.LoadFromFile(FileName);
for Line in Lines do
ShowMessage(Line);
finally
Lines.Free;
end;
end;
Also SaveToFile and LoadFromFile can take an additional Encoding in Delphi 2009 and newer to set the text encoding (Ansi, UTF-8, UTF-16, UTF-16 big endian).
Actually, I prefer this:
var
Txt: TextFile;
SomeFloatNumber: Double;
SomeStringVariable: string;
Buffer: Array[1..4096] of byte;
begin
SomeStringVariable := 'Text';
AssignFile(Txt, 'Some.txt');
Rewrite(Txt);
SetTextBuf(Txt, Buffer, SizeOf(Buffer));
try
WriteLn(Txt, 'Hello, World.');
WriteLn(Txt, SomeStringVariable);
SomeFloatNumber := 3.1415;
WriteLn(Txt, SomeFloatNumber:0:2); // Will save 3.14
finally CloseFile(Txt);
end;
end;
I consider this the easiest way, since you don't need the classes or any other unit for this code. And it works for all Delphi versions including -if I'm not mistaken- all .NET versions of Delphi...
I've added a call to SetTextBuf() to this example, which is a good trick to speed up textfiles in Delphi considerably. Normally, textfiles have a buffer of only 128 bytes. I tend to increase this buffer to a multiple of 4096 bytes. In several cases, I'va also implemented my own TextFile types, allowing me to use these "console" functions to write text to memo fields or even to another, external application! At this location is some example code (ZIP) I wrote in 2000 and just modified to make sure it compiles with Delphi 2007. Not sure about newer Delphi versions, though. Then again, this code is 10 years old already.These console functions have been a standard of the Pascal language since it's beginning so I don't expect them to disappear anytime soon. The TtextRec type might be modified in the future, though, so I can't predict if this code will work in the future... Some explanations:
WA_TextCustomEdit.AssignCustomEdit allows text to be written to CustomEdit-based objects like TMemo.
WA_TextDevice.TWATextDevice is a class that can be dropped on a form, which contains events where you can do something with the data written.
WA_TextLog.AssignLog is used by me to add timestamps to every line of text.
WA_TextNull.AssignNull is basically a dummy text device. It just discards anything you write to it.
WA_TextStream.AssignStream writes text to any TStream object, including memory streams, file streams, TCP/IP streams and whatever else you have.
Code in link is hereby licensed as CC-BY
Oh, the server with the ZIP file isn't very powerful, so it tends to be down a few times every day. Sorry about that.
The IOUtils unit which was introduced in Delphi 2010 provides some very convenient functions for writing/reading text files:
//add the text 'Some text' to the file 'C:\users\documents\test.txt':
TFile.AppendAllText('C:\users\documents\text.txt', 'Some text', TEncoding.ASCII);
Or if you are using an older version of Delphi (which does not have the for line in lines method of iterating a string list):
var i : integer;
begin
...
try
Lines.Add('First line');
Lines.Add('Second line');
Lines.SaveToFile(FileName);
Lines.LoadFromFile(FileName);
for i := 0 to Lines.Count -1 do
ShowMessage(Lines[i]);
finally
Lines.Free;
end;
If you're using a Delphi version >= 2009, give a look to the TStreamWriter class.
It will also take care of text file encodings and newline characters.
procedure String2File;
var s:ansiString;
begin
s:='My String';
with TFileStream.create('c:\myfile.txt',fmCreate) do
try
writeBuffer(s[1],length(s));
finally
free;
end;
end;
Care needed when using unicode strings....

Is there a function like PHP's vardump in Delphi?

I've given up on the Delphi 7 debugger and am pretty much relying on outputdebugstrings. Is there a standard function I can call to get the contents of an object as a string like the debugger would if I set a breakpoint?
Not exactly what your looking for, but you can use RTTI to get access to the values of various published properties. The magical routines are in the TypInfo unit. The ones you are probably most interested in are GetPropList which will return a list of the objects properties, and GetPropValue which will allow you to get the values of the properties.
procedure TForm1.DumpObject( YourObjectInstance : tObject );
var
PropList: PPropList;
PropCnt: integer;
iX: integer;
vValue: Variant;
sValue: String;
begin
PropCnt := GetPropList(YourObjectInstance,PropList);
for iX := 0 to PropCnt-1 do
begin
vValue := GetPropValue(YourObjectInstance,PropList[ix].Name,True);
sValue := VarToStr( vValue );
Memo1.Lines.Add(PropList[ix].Name+' = '+sValue );
end;
end;
for example, run this with DumpObject(Self) on the button click of the main form and it will dump all of the properties of the current form into the memo. This is only published properties, and requires that the main class either descends from TPersistent, OR was compiled with {$M+} turned on before the object.
Rumor has it that a "reflector" like ability will be available in a future version of Delphi (possibly 2010).
Consider something like Codesite which is a much more complete tracing solution. It allows you to output much more complex info, and then search, print, and analyse the data. But for your purposes, you can simply send an object to it with Codesite.Send('Before', self); and you get all the RTTI available properties in the log. Do an "After" one too, and then you can compare the two in the Codesite output just by selecting both. It's saved me many times.
if delphi 7 is the .NET version, then you could do (some of) that with reflection. (not easy, but not terribly hard). if it's the normal, compiled thing, then it's a hard problem and the debugger is you best bet, apart from specialized printing functions/methods.

How can a text file be converted from ANSI to UTF-8 with Delphi 7?

I written a program with Delphi 7 which searches *.srt files on a hard drive. This program lists the path and name of these files in a memo. Now I need convert these files from ANSI to UTF-8, but I haven't succeeded.
The Utf8Encode function takes a WideString string as parameter and returns a Utf-8 string.
Sample:
procedure ConvertANSIFileToUTF8File(AInputFileName, AOutputFileName: TFileName);
var
Strings: TStrings;
begin
Strings := TStringList.Create;
try
Strings.LoadFromFile(AInputFileName);
Strings.Text := UTF8Encode(Strings.Text);
Strings.SaveToFile(AOutputFileName);
finally
Strings.Free;
end;
end;
Take a look at GpTextStream which looks like it works with Delphi 7. It has the ability to read/write unicode files in older versions of Delphi (although does work with Delphi 2009) and should help with your conversion.
var
Latin1Encoding: TEncoding;
begin
Latin1Encoding := TEncoding.GetEncoding(28591);
try
MyTStringList.SaveToFile('some file.txt', Latin1Encoding);
finally
Latin1Encoding.Free;
end;
end;
Please read the whole answer before you start coding.
The proper answer to question - and it is not the easy one - basically consist of tree steps:
You have to determine the ANSI code page used on your computer. You can achieve this goal by using the GetACP() function from Windows API. (Important: you have to retrieve the codepage as soon as possible after the file name retrieval, because it can be changed by the user.)
You must convert your ANSI string to Unicode by calling MultiByteToWideChar() Windows API function with the correct CodePage parameter (retrieved in the previous step). After this step you have an UTF-16 string (practically a WideString) containing the file name list.
You have to convert the Unicode string to UTF-8 using UTF8Encode() or the WideCharToMultiByte() Windows API. This function will return an UTF-8 string you needed.
However this solution will return an UTF-8 string containing the input ANSI string, this probably is not the best way to solve your problems, since the file names may already be corrupted when the ANSI functions returned them, so proper file names are not guaranteed.
The proper solution to your problem is ways more complicated:
If you want to be sure that your file name list is exactly clean, you have to make sure it won't get converted to ANSI at all. You can do this by explicitly using the "W" version of the file handling API's. In this case - of course - you can not use TFileStream and other ANSI file handling objects, but the Windows API calls directly.
It is not that hard, but if you already have a complex framework built on e.g. TFileStream it could be a bit of a pain in the #ss. In this case the best solution is to create a TStream descendant that uses the appropriate API's.
I hope my answer helps you or anyone who has to deal with the same problem. (I had to not so long ago.)
I did only this:
procedure TForm1.FormCreate(Sender: TObject);
begin
Strings := TStringList.Create;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
Strings.Text := UTF8Encode(Memo1.Text);
Strings.SaveToFile('new.txt');
end;
Verified with Notepad++ UTF8 without BOM
Did you mean ASCII?
ASCII is backwards compatible with UTF-8.
http://en.wikipedia.org/wiki/UTF-8

Resources