Solved

C++ Builder 4.0 - Storing bits as file

Posted on 2001-06-15
13
276 Views
Last Modified: 2012-06-21
In C++ Builder 4.0, how to store bits from array of '1' and '0' to a file? I have tried calling the bits from the array and put it into a string... But when i saved it as a file, the size is large (the bits are stored as bytes, not bits).

For example, i have 4000 bits of '1' and '0' in an array. When i saved it to a file, the size is around 4000 bytes (4KBytes). What i want is that the size should be 4000 bits, but not 4000 bytes....

Please help...

Louis.
0
Comment
Question by:LouisLing
  • 4
  • 2
  • 2
  • +3
13 Comments
 
LVL 31

Accepted Solution

by:
Zoppo earned 300 total points
ID: 6194339
Hi LouisLing,

Well, you should fill an array of lets say longs with the bits from your array ... I would do it using a class, i.e.:


#include <stdlib.h>
#include <memory.h>
#include <ios.h>
#include <iostream.h>

#ifndef DWORD
typedef unsigned long DWORD;
#endif

#define BITS_PER_DWORD ( 8 * sizeof( DWORD ) )

class CTrans
{
 DWORD*          m_pBuf;
 DWORD          m_lSize;
public:
 CTrans() { m_pBuf = NULL; }
 ~CTrans() { delete [] m_pBuf; }

 inline DWORD GetSize(){ return m_lSize; }

 void Fill( int* pData, int size );
 void Read( int* pData, int size );
};

void
CTrans::Fill( int* pData, int size )
{
 delete [] m_pBuf;

 m_lSize = size / BITS_PER_DWORD + ( ( size % BITS_PER_DWORD ) != 0 ? 1 : 0 );
 m_pBuf = new DWORD [m_lSize];

 memset( m_pBuf, 0, m_lSize * sizeof( DWORD ) );

 for ( DWORD i = 0; i < m_lSize; i++ )
 {
  for ( DWORD j = 0; j < BITS_PER_DWORD; j++ )
  {
   if ( pData[i * BITS_PER_DWORD + j ] != 0 )
    m_pBuf[i] |= 1 << j;
  }
 }
}

void
CTrans::Read( int* pData, int size )
{
 DWORD s = size / BITS_PER_DWORD;

 for ( DWORD i = 0; i < s; i++ )
 {
  for ( DWORD j = 0; j < BITS_PER_DWORD; j++ )
  {
   if ( ( m_pBuf[i] & ( 1 << j ) ) != 0 )
    pData[i * BITS_PER_DWORD + j] = 1;
   else
    pData[i * BITS_PER_DWORD + j] = 0;
  }
 }

 s = size % BITS_PER_DWORD;

 for ( DWORD j = 0; j < s; j++ )
 {
  if ( ( m_pBuf[i] & ( 1 << j ) ) != 0 )
   pData[i * BITS_PER_DWORD + j] = 1;
  else
   pData[i * BITS_PER_DWORD + j] = 0;
 }
}


// this code was just to test the class
int main(int argc, char* argv[])
{
 const int size = 4000;
 int *x1 = new int [size];
 int *x2 = new int [size];

 for ( int i = 0; i < size; i++ )
  if ( rand() % 2 )
   x1[i] = 1;
  else
   x1[i] = 0;

 CTrans t;
 t.Fill( x1, size );
 t.Read( x2, size );

 for ( i = 0; i < size; i++ )
 {
  if ( x1[i] != x2[i] )
   cout << "Error: data not equal at " << i << "!" << endl;
 }

 cout << "Saved " << size << " bits in " << t.GetSize() << " unsigned longs!" << endl;

 delete [] x1;
 delete [] x2;

 return 0;
}


now, you just would have to implement the save/load functionality for the CTrans class, which
should be simply...

hope that helps,

ZOPPO
0
 

Author Comment

by:LouisLing
ID: 6223037
Dear Zoppo,

for ( DWORD j = 0; j < s; j++ )
{
 if ( ( m_pBuf[i] & ( 1 << j ) ) != 0 )
  pData[i * BITS_PER_DWORD + j] = 1;
 else
  pData[i * BITS_PER_DWORD + j] = 0;
}
}

ermm... where is the 'i' in this code?
0
 
LVL 22

Expert Comment

by:ambience
ID: 6226696
I would suggest that you use bitsets instead of a whole char for a 1 or a zero , this way you will also reduce your memory footprint. However only if you are willing to sacrifice some efficiency.

The STL class bitset is the best way to go about that, it also has methods for conversion to integral types also there is a to_string() operator.

Another alternative is to use C bitfields. like
struct bitfields
{
    unsigned short b1 : 1;
    unsigned short b2 : 1;
    unsigned short b3 : 1;
    ....
    unsigned short b8 : 1;
};

union char_bits
{
  char ch;
  bitfields bits;
};

this helps load and save bits faster and also has a smaller memory foot-print.

Well if you just want to do save space while saving or loading there is another alternative. Instead of writing or reading a single bit you pack/extract 32 bits into/from a single long. If your bitcount is not exactly divisible by 32 you will also need to keep track of paddings.

for e.g.
say you have 40000 bits, and you plan to save them in longs.

char *pData ;  // the data that we want to write

const kSize = 40000;  // total bits
int padding = kSize % sizeof(long); // padding in last write
int kCount = kSize / sizeof(long); // total writes required

// Writing
writeInt(kCount) , writeInt(padding);
for (int i=0;i < kCount; i++)
{
   long temp = 0L;
   for(int j=0;j<sizeof(long); j++) // pack 32 bits into a long
       temp |=  *(pData + i * sizeof(long) + j) ?  (1 << j) : 0;
   writeLongToFile(temp);  
}
if(padding)
{
   long temp;
   for(int i=0;i<padding;i++)
     temp |= *(pData + kCount * sizeof(long) + i) ?  (1 << i) : 0;    
}

// Reading
readInt(&kCount) , readInt(&padding);
for (int i=0;i < kCount; i++)
{
   long temp = 0L;
   readLongFromFile(&temp);  
   for(int j=0;j<sizeof(long); j++) // extract 32 bits from a long
       *(pData + i * sizeof(long) + j) = (temp & (1<<j)) ?  1 : 0;
}
if(padding)
{
   readlongFromfile(&temp);
   for(int i=0;i<padding;i++)
     *(pData + kCount * sizeof(long) + i) =  (temp & (1<<j)) ?  1 : 0;

}

above is including all the bugs that it may have. but it may help you ...
0
Netscaler Common Configuration How To guides

If you use NetScaler you will want to see these guides. The NetScaler How To Guides show administrators how to get NetScaler up and configured by providing instructions for common scenarios and some not so common ones.

 
LVL 22

Expert Comment

by:nietod
ID: 6227315
>> The STL class bitset is the best way to go about that, it also has methods
>> for conversion to integral types
Unfortunately you cannot (safely) convert data that is longer than unsigned long.

But I definitely do aggree that bitset is probably the best way to record the data (assuming the sie is constant), but you still need to get the data into a packed binary form.  (Which is unfortunate, since that is how bitset probably stores it, but prevents you from seeing.)

But given the conditions of the questions, to convert an array of characters that use '1' and '0' to represent bits to an array of binary data, I would use


PackBits(char *DstPtr, const char *SrcPtr,int BitCnt)
{
    while (BitCnt)
    {
          char Byt = 0;
          for (int i = 0; i < 8 && BitCnt; ++i)
          {
             Byt <<= 1;
             if (*SrcPtr == '1')
                Byt |= 1;
             --BitCnt;
          }
          *DstPtr++ Byt;
    }
}

This code assumes that the destination array is large enough to hold the number of bytes required.  That is, it must be the size of the source array (BitCnt) divided by 8.  Note that the code assumes that the size of the source data is a multiple of 8.  If that is not true, it can be fixed, at soem additional cost in complexity, but it woudl be rare for there to be cases where that would not be true.  

You woudl use it like

char Result[2];
char Data[17] = '1011001010110101';
PackBits(Result,Data,16);
0
 
LVL 22

Expert Comment

by:nietod
ID: 6227324
Opps

    *DstPtr++ Byt;

should be

    *DstPtr++ = Byt;
0
 

Author Comment

by:LouisLing
ID: 6233653
I have got the answer... The working steps is like this:

    FILE *fp;
    fp = fopen("TEST.txt", "wb");

    int counting = 0;

    while(counting < KeyIdx){
        Byte mask = 0x00;
        for(int j=0; j<8; j++){
            mask |= (SKey[counting]<<j);
            ++counting;

            if(counting == KeyIdx){
                break;
            }
        }
        fwrite (&mask, 1 , 1 , fp);
    }

    fclose(fp);

Thank you for the help...
0
 
LVL 22

Expert Comment

by:ambience
ID: 6233756
Hmmm .... i hope you did read all the comments
0
 

Expert Comment

by:OL
ID: 6734617

this is a working c++ program that should answer your needs , i hope that you will appreciate the time that i spend on this , and that you will find it helpfull...





#include <iostream>
#include <process.h>
using namespace std;



void SaveBitStream(char *sFileName,BYTE *bitArray)
{
     FILE *bitstream = fopen(sFileName,"w+b");
   
     // number of bytes that will cover the bits
        //   in  buffer
     int nNumBytes =
             ((sizeof(bitArray)/sizeof(BYTE))/8)+1;
     
     // loop on the byte array
     for (int i=0;bitArray[i]!=-1;++i)
     {
     BYTE CurrByte=0;
         
     //     calculate the bit value for this byte           for (int j=i,k=0;((k<8)&&
                 (bitArray[j]! =0xFF));++j,++k)
     {
     CurrByte+=
       (j==7?(bitArray[j]*1):(bitArray[j]*(2<<(6-(j%8)))));          
     }

     // save the byte to the file
     fputc(CurrByte,bitstream);
   
     // time to stop?
     if (bitArray[j]==0xFF)
     {
     break;
     }

     i=j-1;
     }

     fclose(bitstream);
}



void     ReadBitStream(char *sFileName,BYTE *bitArray)
{
     FILE *bitstream = fopen(sFileName,"rb");
     
     // get the number of bytes to read
     fseek(bitstream,SEEK_END,0);
     int nNumBytes     =     ftell(bitstream);
     fseek(bitstream,SEEK_SET,0);
     
     // read the bits from file , they are bytes
        // so obtain thier setting: 0 or 1
     int inx     =     0;
     for (int i=0;i<=nNumBytes;++i)
     {
     BYTE Byte     =     fgetc(bitstream);

     for (int j=0;j<8;++j,++inx)
     {
        bitArray[inx]     =    
          (j==7?(Byte&1):((Byte&(2<<(6-(j%8)))?1:0)));
     }
     }
     
     // put an end mark in the bit array
     bitArray[inx]     =     0xFF;

     fclose(bitstream);
}

void main()
{
     // lets assume that we are saving this bit
        //    array ,  if the number of bits is not
     // a multiplication of 8 , then we put 0 till we
        // obtain that!
     // the right byte in each segment of 8 bytes that
        //represent one byte (8 bits) is the
     // most significant bit and the left byte is the
        // least significant bit!

     BYTE SaveBuf[]     =     {
     /*MSB*/     1,1,1,1,1,1,1,1,     /*LSB*/ //     BYTE     1
          0,1,0,1,0,0,0,1,             //     BYTE     2
          0,1,-1};                   //     BYTE     3

     // before saving
     cout<<"We are saving this :\n\n";                    
     for (int i=0;SaveBuf[i]!=0xFF;++i)
     {
          cout<<((char)(SaveBuf[i]+'0'))<<" ";
     }
     cout<<endl;

     // save to file
     SaveBitStream("bitstream",SaveBuf);

     // read from file
     BYTE ReadBuf[512]     =     {0};
     ReadBitStream("bitstream",ReadBuf);
     
     cout<<"\n\nAfter Read :\n\n";                    
     for (i=0;ReadBuf[i]!=0xFF;++i)
     {
          cout<<((char)(ReadBuf[i]+'0'))<<" ";
     }
     cout<<endl;

     system("pause");
}
0
 
LVL 22

Expert Comment

by:nietod
ID: 6736073
OL this question is a year old!  

These questions you are locking were answered months ago.  The questioner has forgotten about them and has not bothered to award points to the experts that helped him/her.  But now you awre comming in an locking them.  You don't deserve the points, the work was done months or years ago.
0
 
LVL 1

Expert Comment

by:Moondancer
ID: 6811501
Answer rejected, please guide me on the fair outcome here.  Asker clearly chose to abandon this question versus award points for your excellent assistance.  Very sad, indeed.

EE Moderator
0
 
LVL 22

Expert Comment

by:nietod
ID: 6811997
Its hard to choose between zoppo and ambience's answer, but zoppo's approach works (but is less elegant) and was first.
0
 
LVL 1

Expert Comment

by:Moondancer
ID: 6816688
Thank you very much for the advice, nietod, I very much appreciate it.  The first correct answer was accepted.  The other option may have been to change this value to 200 and offer ambience 100 for the additional enhancement.  If anyone feels I've decided incorrectly, please let me know and I'll make it right.  300 max per question, and that was the question value.
Thanks again,
EE Moderator
0
 
LVL 31

Expert Comment

by:Zoppo
ID: 6818443
Well, thanks a lot, Moondancer and nietod ...

I would let ambience decide about this ... I wouldn't have any problems
with sharing the points.

Have a nice day,

regards,

ZOPPO
0

Featured Post

Master Your Team's Linux and Cloud Stack

Come see why top tech companies like Mailchimp and Media Temple use Linux Academy to build their employee training programs.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
maze travler 6 55
how to address a class using javah; or, what are fully-qualified java class names? 13 83
FMX enumerated colours 2 101
VS2015 Redefinition errors 4 52
What is C++ STL?: STL stands for Standard Template Library and is a part of standard C++ libraries. It contains many useful data structures (containers) and algorithms, which can spare you a lot of the time. Today we will look at the STL Vector. …
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.

809 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question