【问题标题】:Read and write to binary files in C?用C读写二进制文件?
【发布时间】:2013-07-10 00:45:25
【问题描述】:

谁有可以写入二进制文件的代码示例。还有可以读取二进制文件并输出到屏幕的代码。查看示例我可以写入文件,但是当我尝试从文件中读取时,它没有正确输出。

【问题讨论】:

  • 你能展示你的代码吗?
  • 互联网上会有很多例子。你是如何输出数据的?也许那是错误的。

标签: c linux file-io binary


【解决方案1】:

二进制文件的读写与任何其他文件几乎相同,唯一的区别是打开方式:

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

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

*注意字节只是在输出上交换,但数据是正确的,你可以针对这种事情进行调整

【讨论】:

  • 我知道它是旧的,但我必须提出一些问题:“// 将 10 个字节写入我们的缓冲区”意味着将缓冲区中的内容保存到文件中?
  • @Gaunt - 是的,我应该说“来自”以使这一点更清楚;我现在已经解决了。在该示例中,我假设“缓冲区”存在,并填充了一些数据,这些数据将写入 write_ptr 所指向的“test.bin”。如果你运行它,你可以“hexdump” test.bin 文件并查看其中的那些字节。
  • 请注意,在 Linux 上(以及通常在基于 Unix 的系统上),b 标志是“可选的”——在此类系统上二进制文件和文本文件之间没有区别。不过,这种差异在 Windows 上非常重要。如果您的代码具有可移植性,请在将文件视为二进制文件时添加b
  • 别忘了fclose(ptr);
  • 在二进制模式下,fread 函数在找到 CR 或 LF 字符时是否终止读取?
【解决方案2】:

有几种方法可以做到这一点。如果我想读写二进制文件,我通常使用open()read()write()close()。这与一次做一个字节完全不同。您使用整数文件描述符而不是 FILE * 变量。 fileno 将从 FILE * BTW 中获取整数描述符。您读取了一个充满数据的缓冲区,例如一次 32k 字节。缓冲区实际上是一个数组,您可以快速读取它,因为它在内存中。一次读取和写入多个字节比一次读取一个要快。我认为它在 Pascal 中称为块读取,但 read() 是 C 等价物。

我看了,但手边没有任何示例。好的,这些并不理想,因为它们也在处理 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;
    }

这是一个写法:(这里 pix 是字节数组,jwidth 和 jheight 是 JPEG 的宽度和高度,所以对于 RGB 颜色,我们写高度 * 宽度 * 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 打开,也读,写,关闭。还有这个老式的 jpeg example.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示例的人来学习。

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

    【讨论】:

      【解决方案4】:

      这是一个读取和写入二进制 jjpg 或 wmv 视频文件的示例。 文件 *fout; 文件 *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);
      

      【讨论】:

        【解决方案5】:

        我真的很难找到一种将二进制文件读入 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]);
        }
        

        【讨论】:

          【解决方案6】:

          这个问题与 CAMILO HG 的问题 How to write binary data file on C and plot it using Gnuplot 相关联。我知道真正的问题有两个部分:1)编写二进制数据文件,2)使用 Gnuplot 绘制它。

          第一部分在这里已经回答的很清楚了,所以我没有什么要补充的。

          对于第二种,简单的方法是将人们发送到 Gnuplot 手册,我确定有人找到了一个很好的答案,但我在网络上没有找到它,所以我将解释一种解决方案(必须是在真正的问题中,但我是stackoverflow的新手,我无法在那里回答):

          使用fwrite() 编写二进制数据文件后,您应该用C 编写一个非常简单的程序,一个阅读器。 reader 只包含与 writer 相同的结构,但您使用 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可以读取和绘制什么,什么不能。

          【讨论】:

            猜你喜欢
            • 2020-09-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-04-20
            • 2019-09-18
            • 1970-01-01
            • 2020-09-03
            • 2021-06-22
            相关资源
            最近更新 更多