我正在尝试构建一个指令管道模拟器,我在开始时遇到了很多麻烦。我需要做的是从stdin读取二进制文件,然后在操作数据时以某种方式将其存储在内存中。我需要一个接一个地读取正好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[]
好吗?
答案 0 :(得分:22)
您需要的是freopen()
。从联机帮助页:
如果filename是空指针,则freopen()函数将尝试将流的模式更改为mode指定的模式,就好像当前与流关联的文件的名称已被使用一样。在这种情况下,如果对freopen()的调用成功,则不需要关闭与流关联的文件描述符。它是实现定义的模式允许更改(如果有),以及在什么情况下。
基本上,你真正做的最好的就是:
freopen(NULL, "rb", stdin);
这将重新打开stdin
作为相同的输入流,但是处于二进制模式。在正常模式下,从Windows上的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()
nul-终止它所读取的内容(如一个好的功能)。显然,这不是你想要的,所以你应该使用fread()
。
答案 1 :(得分:17)
考虑使用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
宏的更多详情:“Handling binary files via standard I/O”
此处setmode
的更多详细信息:"_setmode“
答案 2 :(得分:1)
fgets()在这里完全错了。它的目标是人类可读的ASCII文本,以行尾字符而非二进制数据终止,无法满足您的需求。
我最近使用read()调用完成了您想要的操作。除非您的程序已明确关闭stdin,否则对于第一个参数(文件描述符),您可以对stdin使用常量值0。或者,如果您使用的是POSIX系统(Linux,Mac OS X或其他一些现代Unix版本),则可以使用STDIN_FILENO。
答案 3 :(得分:1)
我不得不根据上述同仁的各种评论将答案拼凑在一起,因此这是一个可以正常工作的示例,仅适用于Windows,但您可以将Windows特定的东西翻译到您的平台上。 / p>
#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;
}
答案 4 :(得分:0)
我不知道你在运行什么操作系统,但你通常不能“以二进制方式打开stdin”。你可以试试像
这样的东西int fd = fdreopen (fileno (stdin), outfname, O_RDONLY | OPEN_O_BINARY);
试图强迫它。然后使用
uint32_t opcode;
read(fd, &opcode, sizeof (opcode));
但我自己没有尝试过。 :)
答案 5 :(得分:0)
对于Windows,此Microsoft _setmode example特别显示了如何将stdin更改为二进制模式:
// 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" );
}
答案 6 :(得分:-1)
fread()最适合读取二进制数据。
是的,如果您计划按字节处理它们,则char数组正常。
答案 7 :(得分:-2)
我第一次没错,除了,我需要ntohl ...... C Endian Conversion : bit by bit