在C语言中读写二进制文件?

88

有人有能够写入二进制文件的代码示例吗?还有能够读取二进制文件并输出到屏幕的代码示例。我看了一些示例,可以成功地向文件中写入数据,但是当我尝试从文件中读取数据时,输出的结果不正确。


15
你可以展示一下你的代码吗? - chrisaycock
4
互联网上会有很多例子。你是如何输出数据的?也许这种方式是错误的。 - doctorlove
7个回答

146

读写二进制文件与其他文件基本相同,唯一的区别在于打开方式:

unsigned char buffer[10];
FILE *ptr;

ptr = fopen("test.bin","rb");  // r for read, b for binary

fread(buffer,sizeof(buffer),1,ptr); // read 10 bytes to our buffer

你说你可以读取它,但它的输出结果不正确...请记住,在“输出”这个数据时,你并没有读取ASCII,所以它不像将一个字符串打印到屏幕上:

for(int i = 0; i<10; i++)
    printf("%u ", buffer[i]); // prints a series of bytes

写入文件基本上是一样的,唯一的区别是你要使用fwrite()而不是fread()

FILE *write_ptr;

write_ptr = fopen("test.bin","wb");  // w for write, b for binary

fwrite(buffer,sizeof(buffer),1,write_ptr); // write 10 bytes from our buffer

既然我们在讨论Linux.. 这里有一种简单的方法来做一个合理性检查。在您的系统上安装hexdump(如果它尚未安装),并转储您的文件:

mike@mike-VirtualBox:~/C$ hexdump test.bin
0000000 457f 464c 0102 0001 0000 0000 0000 0000
0000010 0001 003e 0001 0000 0000 0000 0000 0000
...

现在将其与您的输出进行比较:

mike@mike-VirtualBox:~/C$ ./a.out 
127 69 76 70 2 1 1 0 0 0

嗯,也许将printf改为%x会使这个更清晰:

mike@mike-VirtualBox:~/C$ ./a.out 
7F 45 4C 46 2 1 1 0 0 0

嘿,看!数据现在匹配了*。太棒了,我们一定正确地读取了二进制文件!

*请注意,输出时字节只是交换了位置,但数据是正确的,您可以进行相应的调整


1
我知道这是老问题,但我还是有一些问题要问: "// 将10个字节写入我们的缓冲区" 是指将缓冲区中的内容保存到文件中吗? - Gaunt
1
@Gaunt - 是的,我应该说“从”来更清楚地表达;我现在已经修复了这个问题。在这个例子中,我假设“buffer”存在,并且填充了一些数据,这些数据将被写入由write_ptr指向的“test.bin”文件。如果你运行它,你可以“hexdump”测试二进制文件并在其中看到这些字节。 - Mike
9
请注意,在Linux(以及通常的基于Unix的系统中),b标志是“可选的” - 在这些系统中,二进制文件和文本文件之间没有区别。但在Windows系统中,这种区别非常重要。如果你的代码有可移植性的要求,当你需要将文件视为二进制文件处理时,请添加b标志。 - Jonathan Leffler
11
不要忘记 fclose(ptr); - user376845
2
在二进制模式下,fread 函数是否会在找到 CR 或 LF 字符时终止读取? - shjeff
@shjeff 不需要。这就是 sizeof(buffer) 的作用。它告诉 fread 需要读取多少数据。 - NickKnack

5
有几种方法可以实现。如果我想要读写二进制数据,我通常会使用open()read()write()close()等函数,这些函数与逐字节处理完全不同。您将使用整数文件描述符而不是FILE *变量进行操作。顺便说一下,fileno将从FILE *获取整数描述符。您可以一次读取一个缓冲区的数据,比如32k字节。该缓冲区实际上是一个数组,您可以从中快速读取,因为它在内存中。一次读取和写入多个字节比逐个字节更快。我想Pascal中叫做blockread,但是在C语言中是read()相当于blockread。
我找了一下,但没有找到合适的例子。好吧,这些例子并不理想,因为它们还涉及JPEG图像的处理。以下是一个示例的读取部分,您可能只关心从open()到close()的部分。fbuf是要读入的数组,sb.st_size是使用stat()调用获得的文件大小(以字节为单位)。
    fd = open(MASKFNAME,O_RDONLY);
    if (fd != -1) {
      read(fd,fbuf,sb.st_size);
      close(fd);
      splitmask(fbuf,(uint32_t)sb.st_size); // look at lines, etc
      have_mask = 1;
    }

这是一段代码:(byte数组pix,jwidth和jheight为JPEG的宽度和高度,对于RGB颜色,我们写入height * width * 3个颜色字节)。它表示需要写入的字节数。

void simpdump(uint8_t *pix, char *nm) { // makes a raw aka .data file
  int sdfd;
  sdfd = open(nm,O_WRONLY | O_CREAT);
  if (sdfd == -1) {
    printf("bad open\n");
    exit(-1);
  }
  printf("width: %i height: %i\n",jwidth,jheight);  // to the console
  write(sdfd,pix,(jwidth*jheight*3));
  close(sdfd);
}

查看man 2 open,也读取、写入、关闭。此外,这是一个旧式的jpeg示例.c:https://github.com/LuaDist/libjpeg/blob/master/example.c我在这里一次性读取和写入整个图像。但是它们都是对字节进行的二进制读取和写入,只是一次读取了很多字节。

“但是当我尝试从文件中读取时,输出结果不正确。”嗯。如果你读到数字65,那就是(十进制)ASCII码字符A。也许你应该看看man ascii。如果你想要数字1,就是ASCII 0x31。char变量实际上就是一个8位小整数,如果你用printf打印它,%i会得到ASCII值,%c会得到字符,%x会得到十六进制值。都来自于0到255之间的同一个数字。


3
我对我的“制作一个简单的弱密码存储程序”的解决方案感到非常满意。也许它可以帮助需要一个非常简单的二进制文件IO示例的人们。最初的回答:

我对我的“制作一个简单的弱密码存储程序”的解决方案感到非常满意。也许它可以帮助需要一个非常简单的二进制文件IO示例的人们。

$ ls
WeakPin  my_pin_code.pin  weak_pin.c
$ ./WeakPin
Pin: 45 47 49 32
$ ./WeakPin 8 2
$ Need 4 ints to write a new pin!
$./WeakPin 8 2 99 49
Pin saved.
$ ./WeakPin
Pin: 8 2 99 49
$
$ cat weak_pin.c
// a program to save and read 4-digit pin codes in binary format

#include <stdio.h>
#include <stdlib.h>

#define PIN_FILE "my_pin_code.pin"

typedef struct { unsigned short a, b, c, d; } PinCode;


int main(int argc, const char** argv)
{
    if (argc > 1)  // create pin
    {
        if (argc != 5)
        {
            printf("Need 4 ints to write a new pin!\n");
            return -1;
        }
        unsigned short _a = atoi(argv[1]);
        unsigned short _b = atoi(argv[2]);
        unsigned short _c = atoi(argv[3]);
        unsigned short _d = atoi(argv[4]);
        PinCode pc;
        pc.a = _a; pc.b = _b; pc.c = _c; pc.d = _d;
        FILE *f = fopen(PIN_FILE, "wb");  // create and/or overwrite
        if (!f)
        {
            printf("Error in creating file. Aborting.\n");
            return -2;
        }

        // write one PinCode object pc to the file *f
        fwrite(&pc, sizeof(PinCode), 1, f);  

        fclose(f);
        printf("Pin saved.\n");
        return 0;
    }

    // else read existing pin
    FILE *f = fopen(PIN_FILE, "rb");
    if (!f)
    {
        printf("Error in reading file. Abort.\n");
        return -3;
    }
    PinCode pc;
    fread(&pc, sizeof(PinCode), 1, f);
    fclose(f);

    printf("Pin: ");
    printf("%hu ", pc.a);
    printf("%hu ", pc.b);
    printf("%hu ", pc.c);
    printf("%hu\n", pc.d);
    return 0;
}
$

希望每个提供示例的人都能保持它们的简单和清晰。谢谢。 - undefined

1

我曾经苦苦寻找一种在C++中将二进制文件读入字节数组的方法,以输出与十六进制编辑器中看到的相同的十六进制值。经过多次尝试和错误,这似乎是最快的方法,而无需额外的类型转换。默认情况下,它会将整个文件加载到内存中,但仅打印前1000个字节。

string Filename = "BinaryFile.bin";
FILE* pFile;
pFile = fopen(Filename.c_str(), "rb");
fseek(pFile, 0L, SEEK_END);
size_t size = ftell(pFile);
fseek(pFile, 0L, SEEK_SET);
uint8_t* ByteArray;
ByteArray = new uint8_t[size];
if (pFile != NULL)
{
    int counter = 0;
    do {
        ByteArray[counter] = fgetc(pFile);
        counter++;
    } while (counter <= size);
    fclose(pFile);
}
for (size_t i = 0; i < 800; i++) {
    printf("%02X ", ByteArray[i]);
}

1

这是一个读写二进制jjpg或wmv视频文件的示例。 FILE *fout; FILE *fin;

Int ch;
char *s;
fin=fopen("D:\\pic.jpg","rb");
if(fin==NULL)
     {  printf("\n Unable to open the file ");
         exit(1);
      }

 fout=fopen("D:\\ newpic.jpg","wb");
 ch=fgetc(fin);
       while (ch!=EOF)
             { 
                  s=(char *)ch;
                  printf("%c",s);
                 ch=fgetc (fin):
                 fputc(s,fout);
                 s++;
              }

        printf("data read and copied");
        fclose(fin);
        fclose(fout);

0
#include <stdio.h> 
#include <stdlib.h> 

main(int argc, char **argv) //int argc; char **argv;
{ 
 int wd;
 FILE *in, *out; 

  if(argc != 3) {
    printf("Input and output file are to be specified\n");
    exit(1);
  }
  in = fopen(argv[1], "rb");
  out = fopen(argv[2], "wb");
  if(in == NULL || out == NULL) { /* open for write */ 
    printf("Cannot open an input and an output file.\n");
    getchar();
    exit(0); 
  } 
  while(wd = getw(in), !feof(in)) putw(wd, out); 

  fclose(in);
  fclose(out);

}

即使是getw和putw的官方文档也说你不应该使用它们。 - chrslg

0

这个问题与CAMILO HG的如何在C中编写二进制数据文件并使用Gnuplot绘制它问题有关。我知道真正的问题有两个部分:1)编写二进制数据文件,2)使用Gnuplot绘制它。

第一部分已经在这里得到了非常清晰的回答,所以我没有什么要补充的。

对于第二部分,简单的方法是将人们引导到Gnuplot手册,我相信有人会找到一个好的答案,但我在网上没有找到它,所以我将解释一个解决方案(这必须在真正的问题中,但我是stackoverflow的新手,无法在那里回答):

使用fwrite()写入文件后,您应该在C语言中创建一个非常简单的程序——读取器。读取器只包含与编写器相同的结构,但是您要使用fread()而不是fwrite()。因此,生成此程序非常容易:将原始代码的写入部分复制到reader.c文件中,然后将写入部分更改为读取部分(以及将“wb”更改为“rb”)。此外,您可以包括一些对数据的检查,例如,如果文件的长度是否正确。最后,您的程序需要使用printf()在标准输出中打印数据。
明确一下:您的程序运行如下。
$ ./reader data.dat

X_position Y_position  (it must be a comment for Gnuplot)*

1.23 2.45

2.54 3.12

5.98 9.52


好的,使用这个程序,在Gnuplot中你只需要将读取器的标准输出导入到Gnuplot中,就像这样:
plot '< ./reader data.dat'

这行代码运行程序读取器,将输出与Gnuplot连接并绘制数据。

*由于Gnuplot将读取程序的输出,因此您必须知道Gnuplot可以读取和绘制什么,以及不能读取和绘制什么。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接