开发者

C: Use fwrite() with variables in program

Is there a way to use the fwrite method to read into a variable (a dynamic array) instead of a FILE *, or some alternative method that lets me do this? I need to read a binary file containing 16 bit chunks, but only read the last 12 bits of each chunk. 开发者_开发百科Ideally I want something like:

fwrite(&(x+4),sizeof(x)-4,1,var);

But currently, I cant write into a var directly, only a file.

I know its possible using bitmasks/bitops, but this is a much simpler way so I'd prefer to use it if possible.


No, fwrite writes to something that an be accessed like a file (FILE*).

Perhaps you want to use sprintf to format data into a string?


Read 16 bits. Assuming CHAR_BIT is 8 (the usual) that's 2 bytes:

size_t chk;
unsigned char data16[2];
chk = fread(data16, 2, 1, file);
if (chk != 1) {
    /* handle error */
}

Now, depending on endianness issues, just ignore the right 4 of those 16 bits read:

     data16 has, for instance: 01000101 11101011 (data16[0] == 0x45; data16[1] == 0xeb)
     and you want these bits:  ^^^^^.^^ ^..^^^.^
     so ... mask and shift!
/* mask and shift, bit by bit */
data12[0] = 0;
data12[0] |= !!(data16[0] & (1 << 7)) << 3;
data12[0] |= !!(data16[0] & (1 << 6)) << 2;
data12[0] |= !!(data16[0] & (1 << 5)) << 1;
data12[0] |= !!(data16[0] & (1 << 4)) << 0;
data12[1] = 0;
data12[1] |= !!(data16[0] & (1 << 3)) << 7;
data12[1] |= !!(data16[0] & (1 << 1)) << 6;
data12[1] |= !!(data16[0] & (1 << 0)) << 5;
data12[1] |= !!(data16[1] & (1 << 7)) << 4;
data12[1] |= !!(data16[1] & (1 << 4)) << 3;
data12[1] |= !!(data16[1] & (1 << 3)) << 2;
data12[1] |= !!(data16[1] & (1 << 2)) << 1;
data12[1] |= !!(data16[1] & (1 << 0)) << 0;


EDITED TO ADD BETTER ANSWER

From the comments, it looks like you want to compress a buffer -- an array of 16-bit ints by stripping off 4 bits and packing them together, right?

This code should do the trick:

#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <stdio.h>

typedef unsigned int   UINT32 ;
typedef unsigned short UINT16 ;

void compress( UINT16 *buf , int *cnt )
{
  UINT16 *src   = buf        ; // where we're copying from
  UINT16 *tgt   = buf        ; // where we're copying to
  UINT16 *limit = buf + *cnt ; // endpoint address
  UINT16  bits  = 0x0000     ;
  int     state = 0          ;

  while ( src < limit )
  {
    switch ( state )
    {
    case 0 :
      bits    = (*src++ & 0x0FFF ) <<  4 ;
      state   = 1 ;
      break ;
    case 1 :
      bits   |= (*src   & 0x0F00 ) >>  8 ;
      *tgt++  = bits ;
      bits    = (*src++ & 0x00FF ) <<  8 ;
      state   = 2 ;
      break ;
    case 2 :
      bits   |= (*src   & 0x0FF0 ) >>  4 ;
      *tgt++  = bits ;
      bits    = (*src++ & 0x000F ) << 12 ;
      state   = 3 ;
      break ;
    case 3 :
      bits   |= (*src++ & 0x0FFF ) ;
      *tgt++  = bits ;
      bits    = 0x000 ;
      state   = 0 ;
      break ;
    }
  }

  if ( state != 0 )
  {
    *tgt++ = bits ;
  }

  // hand back the new size ;
  *cnt = (tgt - buf ) ;

  while ( tgt < limit )
  {
    *tgt++ = 0x0000 ;
  }

  return ;
}

int main( int argc, char* argv[])
{
  UINT16 buf[] = { 0xF123 , 0xE456 , 0xD789 , 0xCABC , 0xBDEF , } ;
  int    bufl  = sizeof(buf) / sizeof(*buf) ;

  compress( buf , &bufl ) ;

  // buf now looks like { 0x1234 , 0x5678 , 0x9ABC , 0xDEF0 , 0x0000 }

  return 0 ;
}

ORIGINAL ANSWER

If you actually wantg to read 16-bit structures from a file and that have 12 interesting bits and 4 unused (or not very interesting) bits, and you want to avoid bit-twiddling, you can use bit fields.

Note that implementations get a lot of leeway under the standard for how this stuff works, so it's not portable in the least: you'll probably need to tweak structure alignment and possibly field order, depending on the underlying CPU. You'll note my use of #pragma pack(2) to coerce the structure into a 16-bit size — that works in Visual Studio 2010 C++. YMMV and all that.

[You sure you don't want to just mask off the bits you don't want?]

At any rate, once you deal with all that, something like the following code should work for you:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#pragma pack(2) // necessary to get correct alignment
typedef struct
{
    unsigned short unused :  4 ;
    unsigned short value  : 12 ;
} CHUNK ;
#define BUFFER_CHUNKS ((size_t)8192)

void process( FILE*input )
{
  CHUNK *buf    = (CHUNK*) calloc( BUFFER_CHUNKS , sizeof(CHUNK) ) ;
  size_t bufl   = BUFFER_CHUNKS * sizeof(CHUNK) ;
  int    chunks = 0 ;

  while ( 0 > (chunks=(int)fread( (void*)buf , sizeof(CHUNK) , BUFFER_CHUNKS , input ) ) )
  {
    for ( int i = 0 ; i < chunks ; ++i )
    {
      int value = buf[i].value ;
      printf( "%d: %d\n" , i , value ) ;
    }
  }

  return ;
}

Good Luck!


C is byte oriented, not bit oriented.

read two 16 bit ints and combine them to 24 bits (3 bytes) by shaving off the top 4 bits.

for(;;) {
  uint8_t current;
  uint16_t d[2];
  if(fread(d,sizeof d,2,my_file) != 2) //convert the int if it's in the 
                                        //oposite endian of your host.
     return;
   current = (d[0] & 0xff0) >> 4) ;
   if(fwrite(&current,1,1,my_outfile) != 1) //or store it in an array.
      return;
    current = (d[0] & 0xf) << 4;
    current |= (d[1] & 0xf00) >> 8) ;
    if(fwrite(&current,1,1,my_outfile) != 1) //or store it in an array.
       return;
     current = d[1] & 0xff;
     if(fwrite(&current,1,1,my_outfile) != 1) //or store it in an array.
        return;
 }

Alternativly read them one 16 bit int at a time:

int state = 0;
uint8_t current;
for(;;) {
  uint16_t d;
  if(fread(&d,sizeof d,1,my_file) != 1) //convert the int if it's in the 
                                        //oposite endian of your host.
     return;
   switch(state)
      case 0:
        current = (d & 0xff0) >> 4) ;
        if(fwrite(&current,1,1,my_outfile) != 1) //or store it in an array.
          return;
        current = (d & 0xf) << 4;
        state = 1;
        break;
     case 1;
        current |= (d & 0xf00) >> 8) ;
        if(fwrite(&current,1,1,my_outfile) != 1) //or store it in an array.
          return;
        current = d & 0xff;
        if(fwrite(&current,1,1,my_outfile) != 1) //or store it in an array.
          return;
        state = 0;
        break;
    }
 }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜