24

我正在尝试构建一个指令管道模拟器,但我在开始时遇到了很多麻烦。我需要做的是从标准输入读取二进制文件,然后在我操作数据时以某种方式将其存储在内存中。我需要一个接一个地读取正好 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 上工作...?

4

8 回答 8

29

你需要的是freopen(). 从手册页:

如果 filename 是空指针,freopen() 函数将尝试将流的模式更改为 mode 指定的模式,就好像当前与流关联的文件的名称已被使用一样。在这种情况下,如果对 freopen() 的调用成功,则不需要关闭与流关​​联的文件描述符。允许哪些模式更改(如果有)以及在什么情况下是实现定义的。

基本上,你能做的最好的就是:

freopen(NULL, "rb", stdin);

这将重新打开stdin为相同的输入流,但以二进制模式。在正常模式下,在 Windows 上读取stdin将转换\r\n(Windows 换行符)为单字符 ASCII 10。使用该"rb"模式会禁用此转换,以便您可以正确读取二进制数据。

freopen()返回一个文件句柄,但它是以前的值(在我们将其置于二进制模式之前),所以不要将它用于任何事情。之后,fread()如前所述使用。

但是,至于您的担忧,您可能不是在“32 位”中阅读,但如果您使用fread(),您在 4char秒内阅读(这是您在 C 中可以做的最好的 -char保证至少为8 位,但有些历史嵌入式平台有 16 位char(有些甚至有 18 位或更差))。如果您使用fgets(),您将永远不会读取 4 个字节。您将至少读入 3 个(取决于它们中的任何一个是否为换行符),第 4 个字节将是'\0'因为 C 字符串是 nul 终止fgets()的,并且它会以 nul 终止它读取的内容(就像一个好的函数)。显然,这不是你想要的,所以你应该使用fread().

于 2009-10-21T06:44:40.867 回答
19

考虑使用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此处有关宏的更多详细信息:“通过标准 I/O 处理二进制文件

有关此处的更多详细信息setmode“_setmode

于 2009-10-23T14:02:16.423 回答
2

我不得不从上面好心人的各种评论中拼凑出答案,所以这里有一个完全可用的示例——仅适用于 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;
}
于 2018-07-02T00:05:08.050 回答
1

对于 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" );
}
于 2020-09-21T19:18:42.930 回答
0

fgets() 在这里都是错误的。它针对的是由行尾字符终止的人类可读的 ASCII 文本,而不是二进制数据,并且不会为您提供所需的内容。

我最近使用read()调用做了你想要的。除非您的程序已显式关闭标准输入,否则对于第一个参数(文件描述符),您可以为标准输入使用常量值 0。或者,如果您使用的是 POSIX 系统(Linux、Mac OS X 或其他一些现代 Unix 变体),则可以使用 STDIN_FILENO。

于 2009-10-21T06:20:37.513 回答
-1

fread()最适合读取二进制数据。

是的,如果您打算按字节处理它们,char 数组是可以的。

于 2009-10-21T06:12:08.840 回答
-1

我不知道您正在运行什么操作系统,但您通常不能“以二进制打开标准输入”。你可以尝试像

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

试图强迫它。然后使用

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

但我自己并没有真正尝试过。:)

于 2009-10-21T06:19:09.190 回答
-2

我第一次做对了,除了,我需要 ntohl ... C Endian Conversion : bit by bit

于 2009-10-23T19:01:19.627 回答