我正在将一个低级文件I/O库从Java移植到C++,我需要一些关于C++中基本文件I/O的帮助。目前API看起来像:
public class BinaryFile {
// open/close the file stream
public BinaryFile(string path, string mode)
public void Close()
// append to the end of file
public void AppendBytes(byte[] bytes, uint readPos, uint length)
// write a certain byte chunk at a certain position into the file
public void WriteBytes(byte[] bytes, uint readPos, uint length, uint writePos)
// read a certain byte chunk from the file
public byte[] ReadBytes(uint position, uint length)
}
首先,我已经了解了在C/C++中访问文件/文件流的所有5种不同方法,我真的不在乎我使用哪种方法(fread
和朋友可能很好)。正如您所看到的,我需要从文件的任何部分随机读取/写入二进制块,因此fgets
不太适合,因为它会写入长度前缀。
然而,由于我对C++有些陌生,是否有一个库或头文件已经有了类似的API?(请不要像boost这样的单一框架)简而言之,我只需要读取、写入二进制块并将其附加到二进制文件中。没有汗水,没有字符串,没有JSON,没有XML,没有复杂的东西。在VC++2010中,实现这一点最简单的方法是什么?我有Visual Studio 2010。
编辑:我的目标是Windows XP+并构建一个DLL,并且我已经将<stdlib.h>
、<stdio.h>
和<windows.h>
与#define WIN32_LEAN_AND_MEAN
一起包含在内。
您可以使用<cstdio>
:中的FILE*
API
#include <cstdio>
struct foo {
unsigned int a;
unsigned int b;
};
int main(void)
{
// connect to the file
FILE *f = fopen("test.bin", "wb");
if (!f)
return 1;
// use "unbuffered mode" since you are doing random access
setbuf(f, NULL );
// declare an array of 2 objects
struct foo data[] = {
{ .a = 0xDEADBEEF, .b = 0x2B84F00D },
{ .a = 0xCAFEBABE, .b = 0xBAADB0B1 },
};
// write the data
fwrite(&data, sizeof(struct foo), 2, f);
// move to byte 0x20
fseek(f, 0x20, SEEK_SET);
// write an ASCII string
fprintf(f, "ASCII TOO");
// disconnect from the file
fclose(f);
return 0;
}
test.bin
:的Hexdump
00000000 ef be ad de 0d f0 84 2b be ba fe ca b1 b0 ad ba |.......+........|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 41 53 43 49 49 20 54 4f 4f |ASCII TOO|
00000029
- 您可以使用
<cstdio>
标头中的C样式fwrite
和fread
,也可以使用C++中的流 - 您可以使用WinAPI文件管理函数,请参阅MSDN上的示例
- 此外,您还可以使用Asio单机版及其文件操作。可能更难
无论如何,C/C++标准库都认为文件是流,而不是随机访问资源。
你的班级公共部分可能看起来像这样:
class BinaryFile
{
public:
BinaryFile(const std::string & path, const std::string & mode);
~BinaryFile();
void AppendBytes(const std::vector<uint8_t> & bytes, size_t readPos, size_t length);
void WriteBytes(const std::vector<uint8_t> & bytes, size_t readPos, size_t length, size_t writePos);
std::vector<uint8_t> ReadBytes(size_t position, size_t length);
}
"最佳"答案在很大程度上取决于您如何访问数据。其他答案已经涵盖了您的API可能是什么样子,所以我将重点讨论实现细节。
首先,Windows似乎不提供像POSIX pread()
和pwrite()
这样的原子查找和读取或查找和写入操作,它们在不修改文件偏移量的情况下从文件中的指定偏移量进行原子读取或写入。(请参阅在不同的平台上是否有pread的等价物?)因此,如果您的目标是多线程环境,那么在不添加锁定的情况下,很难使API具有可重入性和多线程安全性。
其次,考虑到您对随机访问的要求,基于流的解决方案(C++流、<cstdio>
fopen()
、fread()
等)中内置的缓冲可能会对性能产生重大负面影响。例如,如果使用缓冲8k的<cstdio>
操作,则每次在FILE *
上使用fseek()
时,可能会使关联的缓冲区无效。如果您一次只读取少数字节,则每次查找后读取的缓冲区无效将导致进程读取的字节数显著相乘。
我建议使用fread()
/fwrite()
,根据您的访问模式,可以选择使用无缓冲IO。您可以使用setbuf()
:禁用缓冲
FILE *file = ::fopen(...);
setbuf( file, NULL );
在您的情况下,请参阅setbuf()
的MSDN文档。
使用无缓冲IO的一个优点是,调用API的应用程序可能会假设每次调用时数据都安全地写入磁盘,但在正常缓冲<cstdio>
的情况下则不是。