我正在创建具有以下格式的记录的二进制文件:
quantity-of-records
record_1
record_2
...
record_N
问题在于record_1
每次都被覆盖,而不是附加。
这是我的简化代码:
#include <fstream>
#include <string>
struct Record
{
unsigned int id;
std::string text;
};
int main()
{
static const Record table[] =
{
{
1, "Apple"
},
{
2, "Salt"
},
{
3, "Margarine"
},
{
4, "Carrot"
},
{
5, "Plum"
}
};
static const size_t records_in_table =
sizeof(table) / sizeof(table[0]);
static const char table_filename[] = "record_file.bin";
size_t i;
size_t record_quantity = 1u;
for (i = 0u; i < records_in_table; ++i)
{
std::ofstream table_file(table_filename,
std::ios::binary);
table_file.seekp(0, std::ios::beg);
table_file.write(reinterpret_cast<char *>(&record_quantity),
sizeof(record_quantity));
table_file.flush();
table_file.seekp(0, std::ios::end);
table_file.write(reinterpret_cast<char const *>(&table[i].id),
sizeof(Record::id));
const size_t length(table[i].text.length());
table_file.write(reinterpret_cast<char const *>(&length),
sizeof(length));
table_file.write(table[i].text.c_str(),
length);
table_file.close();
++record_quantity;
}
return 0;
}
以下是二进制文件的内容:
$ od -Ax -x record_file.bin
000000 0005 0000 0000 0000 0005 0000 0004 0000
000010 0000 0000 6c50 6d75
000018
数字以Little Endian格式写, 32位(4字节) 64位(8字节)。
文本“ Plum”以ASCII编码为:0x50、0x6C,0x75、0x6D
这是第一次迭代后的二进制文件:
$ od -Ax -x record_file.bin
000000 0001 0000 0000 0000 0001 0000 0005 0000
000010 0000 0000 7041 6c70 0065
000019
环境/工具:
app
模式打开另一种方法是以ios::app
模式打开文件,写入新记录,然后更新记录数量:
size_t i;
size_t record_quantity = 1u;
bool first_write(true);
for (i = 0u; i < records_in_table; ++i)
{
std::ofstream table_file(table_filename,
std::ios::binary | std::ios::app);
if (first_write)
{
first_write = false;
table_file.write(reinterpret_cast<char *>(&record_quantity),
sizeof(record_quantity));
table_file.flush();
table_file.write(reinterpret_cast<char const *>(&table[i].id),
sizeof(Record::id));
const size_t length(table[i].text.length());
table_file.write(reinterpret_cast<char const *>(&length),
sizeof(length));
table_file.write(table[i].text.c_str(),
length);
}
else
{
table_file.write(reinterpret_cast<char const *>(&table[i].id),
sizeof(Record::id));
const size_t length(table[i].text.length());
table_file.write(reinterpret_cast<char const *>(&length),
sizeof(length));
table_file.write(table[i].text.c_str(),
length);
table_file.flush();
table_file.seekp(0, std::ios::beg);
table_file.write(reinterpret_cast<char *>(&record_quantity),
sizeof(record_quantity));
}
table_file.close();
++record_quantity;
}
但是,通过替代实现,记录数量或文件中的第一个整数不会更新。
这是二进制文件的内容:
$ od -Ax -x record_file.bin
000000 0001 0000 0000 0000 0001 0000 0005 0000
000010 0000 0000 7041 6c70 0165 0000 0000 0000
000020 0100 0000 0500 0000 0000 0000 4100 7070
000030 656c 0002 0000 0004 0000 0000 0000 6153
000040 746c 0002 0000 0000 0000 0003 0000 0009
000050 0000 0000 0000 614d 6772 7261 6e69 0365
000060 0000 0000 0000 0400 0000 0600 0000 0000
000070 0000 4300 7261 6f72 0474 0000 0000 0000
000080 0500 0000 0400 0000 0000 0000 5000 756c
000090 056d 0000 0000 0000 0000
000099
问题:如何将记录追加到文件末尾并更新第一个整数(在文件开头)?
答案 0 :(得分:0)
问题是因为您如何open table_file
文件流。由于仅打开它进行输出,因此无论是否使用ios:trunc
打开模式,现有文件内容都会被破坏。
要添加到现有内容中,您需要在通话中包括ios:::read
:
std::ofstream table_file(table_filename, std::ios::binary | std::ios::in);
({ofstream
将添加必需的std::ios::write
标志。)虽然在您仅写入文件时需要写入模式,但无论何时需要写入,这似乎是不直观的文件的中间部分通常需要读取一些现有内容,因为您的写操作可能无法在设备存储边界(扇区或群集)上很好地对齐。
答案 1 :(得分:0)
根本原因或问题是打开文件的方式。我的实验表明,仅在使用std::ios_base::app
打开文件时才追加数据。但是,大多数文档暗示所有写操作都将附加到文件中。寻求位置,然后写入仍会将数据写入EOF。
为了在文件的开头写入而不被截断,必须使用ofstream
和std::ios_base::in
属性打开std::ios_base::out
。
我修改了程序,以便在16个字节的边界上排列记录,并用0xFF填充未使用的字节(这使得十六进制转储更易于阅读)。所有整数数据均为32位;文本是可变长度的。
首先写入记录数据,以附加到文件。该文件使用两个不同的变量打开两次,每种模式一次。
#include <fstream>
#include <string>
struct Table_Quantity_Record
{
unsigned int quantity;
uint8_t padding[12];
};
struct Record
{
unsigned int id;
std::string text;
};
int main()
{
static const Record table[] =
{
{ 0x11111111, "Apple"},
{ 0x22222222, "Salt"},
{ 0x33333333, "Butter"},
{ 0x44444444, "Carrot"},
{ 0x55555555, "Plum"},
};
static const size_t records_in_table =
sizeof(table) / sizeof(table[0]);
static const char table_filename[] = "record_file.bin";
std::remove(&table_filename[0]);
size_t i;
Table_Quantity_Record quantity_record;
quantity_record.quantity = 1;
std::fill(&quantity_record.padding[0],
&quantity_record.padding[12],
0xffu);
static const uint8_t padding_bytes[16] = {0xFFu};
for (i = 0; i < records_in_table; ++i)
{
// Open the file in append mode, and append the new data record.
std::ofstream data_file(&table_filename[0],
std::ios_base::binary | std::ios_base::app | std::ios_base::ate);
if (data_file)
{
data_file.write((char *) &table[i].id, sizeof(Record::id));
const unsigned int length = table[i].text.length();
data_file.write((char *) &length, sizeof(length));
data_file.write(table[i].text.c_str(), length);
data_file.flush();
const unsigned int padding_qty =
16 - sizeof(Record::id) - sizeof(length) - length;
static const uint8_t pad_byte = 0xFFU;
for (size_t j = 0; j < padding_qty; ++j)
{
data_file.write((char *) &pad_byte, sizeof(pad_byte));
}
data_file.flush();
data_file.close();
}
// Open the data file with "in" attribute to write the record quantity
// at the beginning of the file.
std::ofstream table_file(&table_filename[0],
std::ios_base::binary | std::ios_base::in);
table_file.write((char *) &quantity_record, sizeof(quantity_record));
table_file.flush();
table_file.close();
++quantity_record.quantity;
}
return 0;
}
$ od -Ax -x record_file.bin
000000 0005 0000 ffff ffff ffff ffff ffff ffff
000010 2222 2222 0004 0000 6153 746c ffff ffff
000020 3333 3333 0006 0000 7542 7474 7265 ffff
000030 4444 4444 0006 0000 6143 7272 746f ffff
000040 5555 5555 0004 0000 6c50 6d75 ffff ffff
000050
注意:自问题程序以来,记录ID值已更改,以便于查找记录。