Using Streams to Read or Write Data
Go Up to Using Streams
Stream classes all share several methods for reading and writing data. These methods are distinguished by whether they:
- Return the number of bytes read or written.
- Require the number of bytes to be known.
- Raise an exception on error.
Stream methods for reading and writing
The Read method reads a specified number of bytes from the stream, starting at its current Position, into a buffer. Read then advances the current position by the number of bytes actually transferred. The prototype for Read is
virtual int __fastcall Read(void *Buffer, int Count);
function Read(var Buffer; Count: Longint): Longint;
Read is useful when the number of bytes in the file is not known. Read returns the number of bytes actually transferred, which may be less than Count if the stream did not contain Count bytes of data past the current position.
The Write method writes Count bytes from a buffer to the stream, starting at the current Position. The prototype for Write is:
virtual int __fastcall Write(const void *Buffer, int Count);
function Write(const Buffer; Count: Longint): Longint;
After writing to the file, Write advances the current position by the number of bytes written, and returns the number of bytes actually written, which may be less than Count if the end of the buffer is encountered or the stream can't accept any more bytes.
The counterpart procedures are ReadBuffer and WriteBuffer which, unlike Read and Write, do not return the number of bytes read or written. These procedures are useful in cases where the number of bytes is known and required, for example when reading in structures. ReadBuffer and WriteBuffer raise an exception (EReadError and EWriteError) if the byte count can not be matched exactly. This is in contrast to the Read and Write methods, which can return a byte count that differs from the requested value. The prototypes for ReadBuffer and WriteBuffer are:
virtual int __fastcall ReadBuffer(void *Buffer, int Count); virtual int __fastcall WriteBuffer(const void *Buffer, int Count);
procedure ReadBuffer(var Buffer; Count: Longint); procedure WriteBuffer(const Buffer; Count: Longint);
These methods call the Read and Write methods to perform the actual reading and writing.
Reading and writing components
TStream defines specialized methods, ReadComponent and WriteComponent, for reading and writing components. You can use them in your applications as a way to save components and their properties when you create or alter them at run time.
ReadComponent and WriteComponent are the methods that the IDE uses to read components from or write them to form files. When streaming components to or from a form file, stream classes work with the TFiler classes, TReader and TWriter, to read objects from the form file or write them out to disk.
For more information about using the component streaming system, see System.Classes.TStream, System.Classes.TFiler, System.Classes.TReader, System.Classes.TWriter, and System.Classes.TComponent classes.
Reading and writing strings
If you are passing a string to a read or write function, you need to be aware of the correct syntax. The Buffer parameters for the read and write routines are var and const types, respectively. These are untyped parameters, so the routine takes the address of a variable.
The default type when working with strings is UnicodeString. However, passing a long string as the Buffer parameter does not produce the correct result. Long strings contain a size, a reference count, and a pointer to the characters in the string. Consequently, dereferencing a long string does not result in the pointer element. You need to first cast the string to a Pointer or PChar, and then dereference it. For example:
procedure caststring; var fs: TFileStream; const s: string = 'Hello'; begin fs := TFileStream.Create('temp.txt', fmCreate or fmOpenWrite); fs.Write(s, Length(s));// this will give you garbage fs.Write(PChar(s)^, Length(s));// this is the correct way end;