Reading and Writing Data


IXFile is data-oriented component that provides functionality for reading and writing data of commonly used types: individual bits, integral and floating point numbers, ANSI and Unicode texts in several formats, variants and binary structures. With GetBit, GetByte, GetShort, GetLong, GetFloat, GetDouble, GetText, GetUnicodeText, GetVariant, GetBinary and PutBit, PutByte, PutShort, PutLong, PutFloat, PutDouble, PutText, PutUnicodeText, PutVariant, PutBinary methods, you can simply and easily read and write data sequence of specified length. File position can also be specified to automatically set active file position to access data at; by default current position is used. When operation completes, file position is advanced by the number of bytes actually read or written, thus allowing sequential access without specifying next position; see Positioning File for more information. All Get methods return the number of elements (not bytes) actually read, which may be less than expected if end of the file is encountered prematurely. Only complete elements can be read; if there is not enough data in file for next full element processing is finished and file remains positioned after last complete element. C++ developers also have GetAlloc versions of methods that can allocate buffer of appropriate size for read data and return it to the user. In addition, GetBlock method is available for direct access to internal data buffer. Data is saved to file automatically whenever internal buffer is full of written data, file is repositioned, resized, closed or detached. Bit buffer is also written even if it does not contain complete byte; in such case it is left padded with zero bits to fill entire byte. You can always call Flush method to force writing data to file.

Individual bits are buffered in special bit buffer which is one byte long and contains at most 8 bits to be accessed one by one. Bits are fetched from right to left, starting from least significant bit. If user requests more bits that are available in buffer, it is refilled with one complete byte read from file and file positon is moved forward by one byte. Such behaviour guarantees that other non-bit data read by user is always accessed on byte boundary regardless of number of bits present in bit buffer. Read and written bits are never mixed in buffer; if GetBit call is followed by PutBit, bit buffer is discarded before bits are written; if PutBit call is followed by GetBit, bit buffer is written to file before bits are read.

Text data is supported with both ANSI and Unicode character encoding and several formats. IXFile represents text as length-prefixed by default; actual text data in file is preceeded with a long number specifying length of the text in characters which is read or written first and then exact number of characters. NULL- and CR/LF terminated texts are also supported to allow simple access to usual text files. In addition raw text format allows processing of arbitrary strings without regard to any termination or prefix. Please note that methods for reading text return number of characters returned to the user and not the number of characters read from file; if text is formatted as length-prefixed or NULL- or CRLF-terminated, entire text is always read from file even if smaller number of characters is returned to user.

IXFile also supports structured data containing fields of various types. With GetBinary and PutBinary methods, you can read and write binary block of data which can represent arbitrary data structure. In such case entire structure should be treated as a sequence of bytes whose length is equal to total size of the structure. File data must be, however, in machine (little-endian) byte order because there is no way to determine structure of data and types of fields for big-endian conversion. Methods can also be used for accessing data of simple types that are not directly supported like Boolean, Date, Currency or Decimal; in such case size of elements must be specified to properly handle big-endian conversion.

Visual Basic developers would preferably use GetVariant and PutVariant methods for the purpose of accessing structured data. Big advantage of these methods relies on known structure of data; each data field is of known type and therefore can be correctly handled by IXFile, relieving developers of technical details of data storage. Accessed variant can be a single variable or one dimensional array (possibly with nested subarrays) of one of allowed types: Byte, Integer, Long, Single, Double, String, Boolean, Date, Currency, Decimal and Variant. With such possibilities you can read and write really complex data; the only limitation is that all arrays must be one dimensional. Text in variant is always represented by IXFile as length-prefixed Unicode string and therefore it is not possible to read structures with raw or terminated texts. Methods also allow to specify number of elements of variant that constitute data sequence; by default all elements are used but you can specify that only some initial fields should be recognized. Please note, however, that number of elements always specifies total number of elements in data sequence regardless of its structure; in other words if variant consists of nested subarrays, it specifies number of elements counted recursively through all subarrays and not elements in most outer array.

All data transferred with IXFile is buffered internally to optimize performance; size of buffer is determined when Initialize is called and cannot be changed. In some situations, however, it may be necessary to change amount of buffered data, especially for performance reasons. For such purposes active buffer size is defined which determines what part of data buffer is used in read and write operations; in other words it specifies how much file data is buffered. With SetActiveBufferSize method, developers can optimize application performance by adaptively changing size of active buffer. Unbuffered transfer is also possible by setting active buffer size to 0; in such case internal data buffer always stays empty and no data is read ahead nor buffered before writing. Active buffer size is reset to its default value whenever new file is opened or attached to the object, so it should be changed after opening or attaching file. To check how much data is currently in internal buffer GetBlockSize should be called. To verify number of bytes transferred during last operation, GetTransfer should be called; please note that method returns number of bytes transferred with last Get or Put operation only. In addition, GetReadTransfer and GetWriteTransfer can be called to obtain total number of bytes transferred during all Get and Put operations.

Examples

C++

// error handling is omitted for clarity


  int main(int, char**)
  {
   IFile ixf;
   struct
   {
    BYTE bits;
    short shrt[3];
    double dbl[2];
    DECIMAL dec;
    char txt[256];
   } data;
   long rtran, wtran, tran, size; 

   ixf.SetLicenseKey("YOURLICENSEKEY");

// default initialization 
// read/write access, auto file closing, 16KB buffer

   ixf.Initialize(); 

// open file and truncate to zero size; create if does not exist
// mode of operation forces read/write access to the file

   ixf.Open("test.bin", TRUE, TRUE); 

// resize file to 128 B

   ixf.SetSize(128);

// initialize structure members
  
   data.bits = 0xF1;
   data.shrt[0] = 0x0101;
   data.shrt[1] = 0x0202;
   data.shrt[2] = 0x0303;
   data.dbl[0] = 1.23456789;
   data.dbl[1] = -9.87654321;
   VarDecFromR8(123456789.87654321,&data.dec); 
   strcpy(data.txt,"This is a test");

// write members one by one

// write 5 rightmost bits (0x11) at position 10
  
   ixf.PutBit(data.bits, 5, 10);

// write short integers at next position (11)
  
   ixf.PutShort(data.shrt, 3);

// write doubles at next position (17)
  
   ixf.PutDouble(data.dbl, 2);

// write decimal value at next position (33)
  
   ixf.PutBinary(&data.dec, 1, sizeof(DECIMAL));

// write text as a fixed-length string at next position (48)
// this operation will write exactly 256 bytes
// right padding string with NULLs 
  
   ixf.PutText(data.txt, 256, -1, IXF_TEXT_STRING);

// get write transfer, it should be 295 bytes
  
   wtran = ixf.GetWriteTransfer();

// get file size, it should be 305 bytes
  
   size = ixf.GetSize();

// read entire structure at once

// start reading at position 10
// structure is treated as a sequence of bytes 
// and its length is equal to total size of the structure
  
   ixf.GetBinary(&data,sizeof(data),1,10);	

// get read transfer, it should be 295 bytes
  
   rtran = ixf.GetReadTransfer();

// get last transfer, it should be 295 bytes (total size of structure)
  
   tran = ixf.GetTransfer();

   .
   .
   .

// ixf object is destroyed by the system when function returns
// and file is closed automatically

   return(0);

  }

BASIC

' error handling is omitted for clarity


  Sub Main()

   Dim ixf As IXFile
   Dim data(4) As Variant
   Dim bits As Byte
   Dim shrt(2) As Integer
   Dim dbl(1) As Double
   Dim dec As Variant
   Dim txt As String
   Dim rtran As Long, wtran As Long, tran As Long, size As Long 

   Set ixf = New IXFile
   ixf.SetLicenseKey "YOURLICENSEKEY"

' default initialization 
' read/write access, auto file closing, 16KB buffer

   ixf.Initialize

' open file and truncate to zero size; create if does not exist
' mode of operation forces read/write access to the file

   ixf.Open "test.bin", True, True 

' resize file to 64 B

   ixf.SetSize 64

' initialize structure members
  
   bits = &HF1
   shrt(0) = &H0101
   shrt(1) = &H0202
   shrt(2) = &H0303
   dbl(0) = 1.23456789
   dbl(1) = -9.87654321
   dec = CDec(123456789.87654321)
   txt = "This is a test"
   data(0) = bits
   data(1) = shrt
   data(2) = dbl
   data(3) = dec
   data(4) = txt

' write members one by one

' write 5 rightmost bits (&H11) at position 10
  
   ixf.PutBit data(0), 5, 10

' write short integers at next position (11)
  
   ixf.PutShort data(1)

' write doubles at next position (17)
  
   ixf.PutDouble data(2)

' write decimal value at next position (33)
  
   ixf.PutVariant data(3)

' write text as a length-prefixed unicode string at next position (48)
' this operation will write exactly 32 bytes 
' 14 wide characters + long prefix
  
   ixf.PutUnicodeText data(4)

' get write transfer, it should be 71 bytes
  
   wtran = ixf.GetWriteTransfer

' get file size, it should be 81 bytes
  
   size = ixf.GetSize

' read entire structure at once starting at position 10
' size of data block is determined by size of variant
' please note that text is read as length-prefixed unicode string
  
   ixf.GetVariant data, -1, 10	

' get read transfer, it should be 71 bytes
  
   rtran = ixf.GetReadTransfer

' get last transfer, it should be 71 bytes (size of entire structure)
  
   tran = ixf.GetTransfer

   .
   .
   .

' ixf file is closed automatically on object destruction
' by assigning object to Nothing immediate destruction
' of the object can be forced

   Set ixf = Nothing
  End Sub

See Also

GetBit, GetByte, GetShort, GetLong, GetFloat, GetDouble, GetText, GetUnicodeText, GetVariant, GetBinary, GetBlock, UnGet, PutBit, PutByte, PutShort, PutLong, PutFloat, PutDouble, PutText, PutUnicodeText, PutVariant, PutBinary, GetBlockSize, GetActiveBufferSize, SetActiveBufferSize, GetTransfer, GetReadTransfer, GetWriteTransfer, ClearReadTransfer, ClearWriteTransfer, Flush, Positioning File