Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C read binary stdin

Tags:

c

stdin

binary

I'm trying to build an instruction pipeline simulator and I'm having a lot of trouble getting started. What I need to do is read binary from stdin, and then store it in memory somehow while I manipulate the data. I need to read in chunks of exactly 32 bits one after the other.

How do I read in chunks of exactly 32 bits at a time? Secondly, how do I store it for manipulation later?

Here's what I've got so far, but examining the binary chunks I read further, it just doesn't look right, I don't think I'm reading exactly 32 bits like I need.

char buffer[4] = { 0 }; // initialize to 0
unsigned long c = 0;
int bytesize = 4; // read in 32 bits
while (fgets(buffer, bytesize, stdin)) {
  memcpy(&c, buffer, bytesize); // copy the data to a more usable structure for bit manipulation later
  // more stuff
  buffer[0] = 0; buffer[1] = 0; buffer[2] = 0; buffer[3] = 0; // set to zero before next loop
}
fclose(stdin);

How do I read in 32 bits at a time (they are all 1/0, no newlines etc), and what do I store it in, is char[] okay?

EDIT: I'm able to read the binary in but none of the answers produce the bits in the correct order — they are all mangled up, I suspect endianness and problems reading and moving 8 bits around ( 1 char) at a time — this needs to work on Windows and C ... ?

like image 257
rlb.usa Avatar asked Oct 21 '09 06:10

rlb.usa


4 Answers

What you need is freopen(). From the manpage:

If filename is a null pointer, the freopen() function shall attempt to change the mode of the stream to that specified by mode, as if the name of the file currently associated with the stream had been used. In this case, the file descriptor associated with the stream need not be closed if the call to freopen() succeeds. It is implementation-defined which changes of mode are permitted (if any), and under what circumstances.

Basically, the best you can really do is this:

freopen(NULL, "rb", stdin);

This will reopen stdin to be the same input stream, but in binary mode. In the normal mode, reading from stdin on Windows will convert \r\n (Windows newline) to the single character ASCII 10. Using the "rb" mode disables this conversion so that you can properly read in binary data.

freopen() returns a filehandle, but it's the previous value (before we put it in binary mode), so don't use it for anything. After that, use fread() as has been mentioned.

As to your concerns, however, you may not be reading in "32 bits" but if you use fread() you will be reading in 4 chars (which is the best you can do in C - char is guaranteed to be at least 8 bits but some historical and embedded platforms have 16 bit chars (some even have 18 or worse)). If you use fgets() you will never read in 4 bytes. You will read in at least 3 (depending on whether any of them are newlines), and the 4th byte will be '\0' because C strings are nul-terminated and fgets() nul-terminates what it reads (like a good function). Obviously, this is not what you want, so you should use fread().

like image 156
Chris Lutz Avatar answered Oct 08 '22 08:10

Chris Lutz


Consider using SET_BINARY_MODE macro and setmode:

#ifdef _WIN32
# include <io.h>
# include <fcntl.h>
# define SET_BINARY_MODE(handle) setmode(handle, O_BINARY)
#else
# define SET_BINARY_MODE(handle) ((void)0)
#endif

More details about SET_BINARY_MODE macro here: "Handling binary files via standard I/O"

More details about setmode here: "_setmode"

like image 21
Adrian Avatar answered Oct 08 '22 06:10

Adrian


I had to piece the answer together from the various comments from the kind people above, so here is a fully-working sample that works - only for Windows, but you can probably translate the windows-specific stuff to your platform.

#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
#include "windows.h"
#include <io.h>
#include <fcntl.h>

int main()
{
    char rbuf[4096];
    char *deffile = "c:\\temp\\outvideo.bin";
    size_t r;
    char *outfilename = deffile;
    FILE *newin;

    freopen(NULL, "rb", stdin);
    _setmode(_fileno(stdin), _O_BINARY);

    FILE *f = fopen(outfilename, "w+b");
    if (f == NULL)
    {
        printf("unable to open %s\n", outfilename);
        exit(1);
    }

    for (;; )
    {
        r = fread(rbuf, 1, sizeof(rbuf), stdin);
        if (r > 0)
        {
            size_t w;
            for (size_t nleft = r; nleft > 0; )
            {
                w = fwrite(rbuf, 1, nleft, f);
                if (w == 0)
                {
                    printf("error: unable to write %d bytes to %s\n", nleft, outfilename);
                    exit(1);
                }
                nleft -= w;
                fflush(f);
            }
        }
        else
        {
            Sleep(10); // wait for more input, but not in a tight loop
        }
    }

    return 0;
}
like image 3
alex gimenez Avatar answered Oct 08 '22 08:10

alex gimenez


For Windows, this Microsoft _setmode example specifically shows how to change stdin to binary mode:

// crt_setmode.c
// This program uses _setmode to change
// stdin from text mode to binary mode.

#include <stdio.h>
#include <fcntl.h>
#include <io.h>

int main( void )
{
   int result;

   // Set "stdin" to have binary mode:
   result = _setmode( _fileno( stdin ), _O_BINARY );
   if( result == -1 )
      perror( "Cannot set mode" );
   else
      printf( "'stdin' successfully changed to binary mode\n" );
}
like image 1
gregS Avatar answered Oct 08 '22 07:10

gregS