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(¤t,1,1,my_outfile) != 1) //or store it in an array.
return;
current = (d[0] & 0xf) << 4;
current |= (d[1] & 0xf00) >> 8) ;
if(fwrite(¤t,1,1,my_outfile) != 1) //or store it in an array.
return;
current = d[1] & 0xff;
if(fwrite(¤t,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(¤t,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(¤t,1,1,my_outfile) != 1) //or store it in an array.
return;
current = d & 0xff;
if(fwrite(¤t,1,1,my_outfile) != 1) //or store it in an array.
return;
state = 0;
break;
}
}
精彩评论