C читать двоичный стандарт

Я пытаюсь построить симулятор конвейера инструкций, и у меня много проблем с началом работы. Что мне нужно сделать, это прочитать двоичный файл из стандартного ввода, а затем как-то сохранить его в памяти, пока я манипулирую данными. Мне нужно читать порциями ровно 32 бита один за другим.

Как читать по 32 бита за раз? Во-вторых, как я могу сохранить его для манипуляций позже?

Вот то, что я получил до сих пор, но изучая двоичные фрагменты, которые я читаю дальше, это просто не выглядит правильно, я не думаю, что читаю ровно 32 бита, как мне нужно.

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);

Как я могу читать по 32 бита за раз (все они равны 1/0, нет новых строк и т. Д.), И что мне нужно хранить в них? char[] Хорошо?

РЕДАКТИРОВАТЬ: я могу прочитать двоичный файл в, но ни один из ответов не производит биты в правильном порядке - они все искажены, я подозреваю, что порядок байтов и проблемы чтения и перемещения 8 бит ( 1 символ) за раз - это нужно работать на Windows и C ...?

7 ответов

Решение

У меня все было правильно с первого раза, за исключением того, что мне нужно было ntohl... C Endian Conversion: по крупицам

Что вам нужно freopen(), Из справочной страницы:

Если filename является нулевым указателем, функция freopen() должна попытаться изменить режим потока на режим, указанный в mode, как если бы использовалось имя файла, связанного в данный момент с потоком. В этом случае дескриптор файла, связанный с потоком, не нужно закрывать, если вызов freopen() завершается успешно. Это определяется реализацией, какие изменения режима разрешены (если есть) и при каких обстоятельствах.

По сути, лучшее, что вы можете сделать, это:

freopen(NULL, "rb", stdin);

Это будет вновь открыто stdin быть тем же входным потоком, но в двоичном режиме. В обычном режиме чтение из stdin на винде будет конвертировать \r\n (Перевод строки Windows) на один символ ASCII 10. Использование "rb" Режим отключает это преобразование, чтобы вы могли правильно читать в двоичных данных.

freopen() возвращает дескриптор файла, но это предыдущее значение (до того, как мы переведем его в двоичный режим), поэтому не используйте его ни для чего. После этого используйте fread() как уже упоминалось.

Что касается ваших проблем, однако, вы можете не читать "32 бита", но если вы используете fread() ты будешь читать через 4 char s (это лучшее, что вы можете сделать в C - char гарантированно будет по крайней мере 8 бит, но некоторые исторические и встроенные платформы имеют 16 бит char s (у некоторых даже есть 18 или хуже)). Если вы используете fgets() Вы никогда не будете читать в 4 байта. Вы будете читать по крайней мере 3 (в зависимости от того, являются ли какие-либо из них символами новой строки), и 4-й байт будет '\0' потому что строки C обнуляются и fgets() обнуляет то, что читает (например, хорошая функция). Очевидно, это не то, что вы хотите, поэтому вы должны использовать fread(),

Рассмотреть возможность использования SET_BINARY_MODE макро и 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

Подробнее о SET_BINARY_MODE макрос здесь: " Обработка двоичных файлов через стандартный ввод / вывод"

Подробнее о setmode здесь: "_setmode"

Мне пришлось собрать воедино ответ из различных комментариев от добрых людей, представленных выше, так что вот полностью рабочий пример, который работает - только для Windows, но вы, вероятно, можете перевести специфичные для Windows вещи на свою платформу.

#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;
}

Для Windows этот пример Microsoft _setmode специально показывает, как изменить стандартный ввод в двоичный режим:

// 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" );
}

fgets() здесь не так. Он нацелен на читаемый человеком текст ASCII, заканчивающийся символами конца строки, а не двоичными данными, и не даст вам того, что вам нужно.

Я недавно сделал именно то, что вы хотите, используя вызов read(). Если ваша программа не закрыла явно stdin, для первого аргумента (дескриптора файла) вы можете использовать постоянное значение 0 для stdin. Или, если вы работаете в системе POSIX (Linux, Mac OS X или какой-либо другой современный вариант Unix), вы можете использовать STDIN_FILENO.

Я не знаю, на какой ОС вы работаете, но вы обычно не можете "открыть стандартный ввод в двоичном формате". Вы можете попробовать такие вещи, как

int fd = fdreopen (fileno (stdin), outfname, O_RDONLY | OPEN_O_BINARY);

попытаться заставить его. Тогда используйте

uint32_t opcode;
read(fd, &opcode, sizeof (opcode));

Но я на самом деле не пробовал сам.:)

fread() лучше всего подходит для чтения двоичных данных.

Да, с массивом символов все в порядке, если вы планируете обрабатывать их побайтово.

Другие вопросы по тегам