• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 643
  • Last Modified:

Import numbers / strings from a binary file

I've attached a copy of the file I'm trying to import.  I've renamed it to a .TXT so that EE will allow me to attach it, but it's not a text file.

The attached code was used to create this file, and I'd like to come up with similar code that will allow me to import the file back into the program.

I'm just not a File I/O guy.  I need some help, preferably a solution that uses FileOpen() and FileRead() to get it done.

The first 4 bytes of this file are 06 00 00 00, and they represent an integer, 6.  The second 4 bytes are 7C 02 00 00, which represent the number 636.  The remainder of the file contains 3,816 values (6x636).

The first "value" is a 1, and it's stored as a 4-byte length (01 00 00 00) and the ascii value of the digit "1", (31)

The second value is another 5 bytes .. 01 00 00 00 31 .. this is another "1"

The third value is also 5 bytes.. 01 00 00 00 32 .. this represents a "2"


..hopefully this is clear.  I'm sure you can figure out the data layout by looking at the ::ExportPicks() method.

Can someone write for me the body of this method?

void __fastcall TMyForm::ImportPicks(AnsiString sFile, TStringGrid *pGrid)
{
    /* help */
}

Thanks in advance...
Brian Withun



So
void __fastcall TMyForm::ExportPicks(AnsiString sFile, TStringGrid *pGrid)
{
                  DeleteFile(sFile);
    int iHandle = FileCreate(sFile);
    int iLength = 0;

    FileWrite(iHandle, (char*)&(pGrid->ColCount), sizeof(pGrid->ColCount));
    FileWrite(iHandle, (char*)&(pGrid->RowCount), sizeof(pGrid->RowCount));

    for (int y=0; y<pGrid->RowCount; y++)
    {
        for (int x=0; x<pGrid->ColCount; x++)
        {

        // Write out the length of each string, followed by the string itself.

        iLength = pGrid->Cells[x][y].Length();

        FileWrite(iHandle, (char*)&iLength, sizeof(iLength));
        FileWrite(iHandle, pGrid->Cells[x][y].c_str(), pGrid->Cells[x][y].Length());

      }

    }

    FileClose(iHandle);
}

Open in new window

Copy-of-Picks.txt
0
Brian Withun
Asked:
Brian Withun
  • 3
  • 2
  • 2
2 Solutions
 
George TokasCommented:
Load the file in a buffer.
In order to prevent memory leaks declare the buffer globally at your main header file...

BYTE     *pszBuffer;

At the code:
Assuming that the file has a fixed size i.e. 256bytes, at forms constructor:
pszBuffer = new BYTE[256];
//zero all contents
for(int i = 0; i < 256; i++){pszBuffer[i] = 0;}

Load the file:
int iFileHandle;
iFileHandle = FileOpen("myfile.ext",fmOpenRead);//Just for reading and prevent mistakes
//trap error
if(iFileHandle < 0)
{/*error*/}
//read the content
FileRead(iFileHandle, pszBuffer, 256);
//The content is loaded at pszBuffer[]
FileClose(iFileHandle);
//Close the file because we are done...

NOW:
The content of the file is now inside pszBuffer that is an array and you can access it the way you like...
For myself I'm a fun of accessing every byte inside and do what I like.
In this example you can access the byte at location 0x10 using:
unsigned char a = 0;//or BYTE a if you like... we will just read or write the hex content of the location
a = pszBuffer[0x10];
ADVISE:
Do not read/write integers or 64bit integers using the buffer...
Better assemble an integer from the 4 bytes inside the buffer. Same when saving...
Maybe more code but safer in cases of leak or bugs.

George Tokas.

0
 
George TokasCommented:
The answer is generic just to saw you the way...
In case of pictures as I see to your code since the file has not a fixed size.
Calculate the size of the file and store it as an __int64 (8bytes) in the begining of the buffer.
In this case (file size not fixed) the buffer has to be declared, initialized, destroyed at runtime.
1. Read the file as showed, AND CLOSE THE FILE.
2. Read the first 8 bytes and construct the __int64 of the size of the file.
3. BYTE *pszBuffer2 = new BYTE[(the __int64 constructed)];
4. Read the file again using: FileRead(iFileHandle, pszBuffer2, (the __int64 constructed)); AND CLOSE THE FILE AGAIN!!
5. Do your job with the buffer and delete it...

The reason I'm telling you to close the file is to avoid pointer to start reading of the file mistakes.

George Tokas.
0
 
itsmeandnobodyelseCommented:
You may try the code below. Unfortuanetly I neither could find out how to propely resize a TStringGrid nor how to update the string of a specific cell.


#include <fstream>
using namespace std;

...

// assume the grid passed is already proper sized ?
int readData(const char * pszFile, TStringGrid & grid)
{
    ifstream ifs(pszFile, ios::binary, ios::in);

    int n1 = 0;
    int n2 = 0;
    if (!ifs.read((char*)&n1, 4))
        return -1;
    if (!ifs.read((char*)&n2, 4))
        return -1;
    if (n1 > grid.ColCount)
        return -2;
    if (n2 > grid.RowCount)
        return -3;

    int  n      = 0;
    char cb[32] = { '\0' };

    for (int r = 0; r < n2; ++r)
    {

        for (int c = 0; c < n1; ++c)
        {

            if (!ifs.read((char*)&n, 4))
                return -1;
            if (n >= 32 || !ifs.read(cb, n))
                return -1;
            grid.Cells[r][c] = cb;   // Is that the right statement? 

        }
    }
    ifs.close();
    return 0;

}

Open in new window

0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
Brian WithunAuthor Commented:
Thanks for your input, both of you.  I'll be posting what I ended up using.
0
 
Brian WithunAuthor Commented:
My "Export" code came right out of the "Help" from Borland C++ Builder 5.  The example for FileWrite() matched my needs almost exactly.  That was a stroke of luck.

The help page for FileRead(), however, had an unrelated example and so I couldn't use it.  I didn't exactly understand how the FileWrite() example worked, so I posted it to see if it was something anyone else was familiar with.

Specifically, the way to get a string OUT of the TStringGrid and INTO the file was unfamiliar.  I hadn't figured out how to reverse that technique for the import method.

Since posting the original question, I believe I have figured it out.  It seems unorthodox to cast an &int to a char*, but that was how the Borland example worked, and it appears to work fine in my implemention of ::Import().

I appreciate the input provided by both of the experts who responded.
void __fastcall TMyForm::Import(void)
{
    AnsiString sFile = GetFilename();

    if (OpenTLPFile(sFile))
    {
        FileRead(iHandle, (char*)&(PicksGrid->ColCount), sizeof(PicksGrid->ColCount));
        FileRead(iHandle, (char*)&(PicksGrid->RowCount), sizeof(PicksGrid->RowCount));

        int iLength;

        for (int y=0; y<PicksGrid->RowCount; y++)
        {
            for (int x=0; x<PicksGrid->ColCount; x++)
            {
                FileRead(iHandle, (char*)&iLength, sizeof(iLength));
                PicksGrid->Cells[x][y] = ReadTLPFile(iLength);
            }
        }

        CloseTLPFile();
    }
}

Open in new window

0
 
George TokasCommented:
>>FileRead(iHandle, (char*)&iLength, sizeof(iLength));
You are reading iLength bytes of data from the file and loading them at iLength that is an int and means 4 bytes...
AFTER that line the file pointer ( the location the file is about to read again ) is incremented by iLength (4 bytes)...
SO:
What you are doing here is a serialized read from the file...

What I was proposed is to load the content inside a buffer and release the file...
I don't mean that my approach is better, but in case of an exception you don't have to worry about the file handle (iHandle)...
On the other hand coding using the buffer, even though safer IMHO, needs a lot more of coding...

George Tokas.
0
 
itsmeandnobodyelseCommented:
>>>> It seems unorthodox to cast an &int to a char*,

No, that's quite common for functions coming from a C interface.  C functions cannot have overloads for poimnters of different types but have a void* or char* (or unsigned char*) which must be casted.

>>>> Specifically, the way to get a string OUT of the TStringGrid and INTO the file was unfamiliar.
That was the way text files were stored before termination by linefeed characters became standard.

>>>> PicksGrid->Cells[x][y] = ReadTLPFile(iLength);
What is ReadTLPFile?
0

Featured Post

Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

  • 3
  • 2
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now