c++ - 将结构写入二进制文件后,文件仍然有正常字符而不是不可读的字符
问题描述
我正在尝试将结构写入二进制文件。该结构由字符串和整数组成。如果没有字符串,我只需将整个对象正常写入二进制文件,但如果我现在这样做,字符串也可以轻松读取。
所以我决定分别写结构的每个属性。我使用字符串的方式与此 Stackoverflow 答案中提到的相同。
这是应该将结构保存到name.bin文件中的主要功能。
void saveFileBin(std::string nameOfFile) {
Person people[SIZE_OF_ARRAY] =
{Person("Name1", "lastName2", Address("Street1", "City1", "111"), Date(1, 1, 1111)),
Person("Name2", "lastName2", Address("Street2", "City2", "222"), Date(2, 2, 2222)),
Person("Name3", "lastName3", Address("Street3", "City3", "333"), Date(3, 3, 3333))};
std::ofstream myFile(nameOfFile + ".bin", std::ios::binary);
if (myFile.is_open())
{
for (int i = 0; i < SIZE_OF_ARRAY; i++)
{
people[i].write(&myFile);
}
myFile.close();
std::cout << "The entire thing is in memory";
}
else
{
throw std::exception("Unable to open file");
}
};
这是一个将每个属性写入文件的函数。
void Person::write(std::ofstream* out)
{
out->write(_name.c_str(), _name.size());
out->write(_lastName.c_str(), _lastName.size());
out->write(_residence.getStreet().c_str(), _residence.getStreet().size());
out->write(_residence.getZip().c_str(), _residence.getZip().size());
out->write(_residence.getCity().c_str(), _residence.getCity().size());
std::string day = std::to_string(_birthDate.getDay());
std::string month = std::to_string(_birthDate.getMonth());
std::string year = std::to_string(_birthDate.getYear());
out->write(day.c_str(), day.size());
out->write(month.c_str(), month.size());
out->write(year.c_str(), year.size());
}
生成的文件中的所有内容都是纯文本可读的。尽管如果我改为在 main 方法调用中,myFile.write((char*)people, sizeof(people));
那么它会正确显示不可读的字符,但仍然可以正常读取字符串变量。这就是为什么我将所有变量转换为字符串,然后将其完全写入 bin 文件。
为什么我的方法仍然显示所有字符串,就像它甚至不是二进制文件一样?即使我有 std::ios::binary 作为参数?
输出文件包含以下内容:
Name1lastName1Street11111City11111111Name2lastName2Street22222City22222222Name3lastName3Street33333City3333333
而如果我将整个结构写入二进制文件,它看起来像这样:
lÊ87 Name1 ÌÌÌÌÌÌÌÌÌÌ à^Ê87 lastName1 ÌÌÌÌÌÌ Ð_Ê87 Street1 ÌÌÌÌÌÌÌÌ ÐdÊ87 City1 ÌÌÌÌÌÌÌÌÌÌ bÊ87 111 ÌÌÌÌÌÌÌÌÌÌÌÌ W ÌÌÌÌ`kÊ87 Name2 ÌÌÌÌÌÌÌÌÌÌ fÊ87 lastName2 ÌÌÌÌÌÌ €iÊ87 Street2 ÌÌÌÌÌÌÌÌ PbÊ87 City2 ÌÌÌÌÌÌÌÌÌÌ ÐiÊ87 222 ÌÌÌÌÌÌÌÌÌÌÌÌ ® ÌÌÌÌ€dÊ87 Name3 ÌÌÌÌÌÌÌÌÌÌ `Ê87 lastName3 ÌÌÌÌÌÌ p`Ê87 Street3 ÌÌÌÌÌÌÌÌ gÊ87 City3 ÌÌÌÌÌÌÌÌÌÌ ðbÊ87 333 ÌÌÌÌÌÌÌÌÌÌÌÌ
ÌÌÌÌ lÊ87 Name1 ÌÌÌÌÌÌÌÌÌÌ à^Ê87 lastName1 ÌÌÌÌÌÌ Ð_Ê87 Street1 ÌÌÌÌÌÌÌÌ ÐdÊ87 City1 ÌÌÌÌÌÌÌÌÌÌ bÊ87 111 ÌÌÌÌÌÌÌÌÌÌÌÌ W ÌÌÌÌ`kÊ87 Name2 ÌÌÌÌÌÌÌÌÌÌ fÊ87 lastName2 ÌÌÌÌÌÌ €iÊ87 Street2 ÌÌÌÌÌÌÌÌ PbÊ87 City2 ÌÌÌÌÌÌÌÌÌÌ ÐiÊ87 222 ÌÌÌÌÌÌÌÌÌÌÌÌ ® ÌÌÌÌ€dÊ87 Name3 ÌÌÌÌÌÌÌÌÌÌ `Ê87 lastName3 ÌÌÌÌÌÌ p`Ê87 Street3 ÌÌÌÌÌÌÌÌ gÊ87 City3 ÌÌÌÌÌÌÌÌÌÌ ðbÊ87 333 ÌÌÌÌÌÌÌÌÌÌÌÌ
ÌÌÌÌ lÊ87 Name1 ÌÌÌÌÌÌÌÌÌÌ à^Ê87 lastName1 ÌÌÌÌÌÌ Ð_Ê87 Street1 ÌÌÌÌÌÌÌÌ ÐdÊ87 City1 ÌÌÌÌÌÌÌÌÌÌ bÊ87 111 ÌÌÌÌÌÌÌÌÌÌÌÌ W ÌÌÌÌ`kÊ87 Name2 ÌÌÌÌÌÌÌÌÌÌ fÊ87 lastName2 ÌÌÌÌÌÌ €iÊ87 Street2 ÌÌÌÌÌÌÌÌ PbÊ87 City2 ÌÌÌÌÌÌÌÌÌÌ ÐiÊ87 222 ÌÌÌÌÌÌÌÌÌÌÌÌ ® ÌÌÌÌ€dÊ87 Name3 ÌÌÌÌÌÌÌÌÌÌ `Ê87 lastName3 ÌÌÌÌÌÌ p`Ê87 Street3 ÌÌÌÌÌÌÌÌ gÊ87 City3 ÌÌÌÌÌÌÌÌÌÌ ðbÊ87 333 ÌÌÌÌÌÌÌÌÌÌÌÌ
ÌÌÌÌ
编辑:这里要求的是 Person.h 的标题
#pragma once
#ifndef PERSON_H
#define PERSON_H
#include <string.h>
#include "Address.h"
#include "Date.h"
#include <fstream>
struct Person {
public:
Person(std::string name, std::string last_name, Address _residence, Date birthDate);
Person();
friend std::ostream& operator<<(std::ostream& os, const Person& p);
friend std::istream& operator>>(std::istream& is, Person& p);
std::string getName() const { return _name; }
std::string getLastName() const { return _lastName; };
Address getResidence() const { return _residence; };
Date getDate() const { return _birthDate; };
void read(std::ifstream *in);
void write(std::ofstream *out);
private:
std::string _name;
std::string _lastName;
Address _residence;
Date _birthDate;
};
#endif // !PERSON_H
解决方案
序列化 a 的示例std::string
(长度限制为 ≤ 65536 个字符):
#include <cassert>
#include <iostream>
#include <fstream>
void writeString(std::ostream &out, const std::string &str)
{
// write length of string (two bytes, little endian)
assert(str.size() < 1 << 16);
const size_t size = str.size();
char buffer[2] = { (char)(size & 0xff), (char)(size >> 8 & 0xff) };
out.write(buffer, sizeof buffer)
// write string contents
&& out.write(str.c_str(), size);
}
void readString(std::istream &in, std::string &str)
{
// read length
char buffer[2];
if (!in.read(buffer, 2)) return; // failed
const size_t size = (unsigned char)buffer[0] | (unsigned char)buffer[1] << 8;
// allocate size
str.resize(size);
// read contents
in.read(&str[0], size);
}
int main()
{
// sample
std::string name = "Antrophy";
// write binary file
{ std::ofstream out("test.dat", std::ios::binary);
writeString(out, name);
} // closes file
// reset sample
name = "";
// read binary file
{ std::ifstream in("test.dat", std::ios::binary);
readString(in, name);
} // closes file
// report result
std::cout << "name: '" << name << "'\n";
}
输出:
name: 'Antrophy'
的十六进制转储test.dat
:
00000000 08 00 41 6e 74 72 6f 70 68 79 |..Antrophy|
0000000a
笔记:
考虑如何写入长度(限制为 16 位)。这可以类似于序列化整数值来完成。
C++FAQ 提供了一个(恕我直言)很好的介绍:
组合类型的二进制 I/O 的扩展示例Person
:
#include <cassert>
#include <iostream>
#include <fstream>
template <size_t nBytes, typename VALUE>
std::ostream& writeInt(std::ostream &out, VALUE value)
{
const size_t size = sizeof value;
char buffer[nBytes];
const size_t n = std::min(nBytes, size);
for (size_t i = 0; i < n; ++i) {
buffer[i] = (char)(value >> 8 * i & 0xff);
}
for (size_t i = size; i < nBytes; ++i) buffer[i] = '\0';
return out.write(buffer, nBytes);
}
template <size_t nBytes, typename VALUE>
std::istream& readInt(std::istream &in, VALUE &value)
{
const size_t size = sizeof value;
char buffer[nBytes];
if (in.read(buffer, nBytes)) {
value = (VALUE)0;
const size_t n = std::min(nBytes, size);
for (size_t i = 0; i < n; ++i) {
value |= (VALUE)(unsigned char)buffer[i] << 8 * i;
}
}
return in;
}
void writeString(std::ostream &out, const std::string &str)
{
// write length of string (two bytes, little endian)
assert(str.size() < 1 << 16);
const size_t size = str.size();
writeInt<2>(out, size)
// write string contents
&& out.write(str.c_str(), size);
}
void readString(std::istream &in, std::string &str)
{
// read length
std::uint16_t size = 0;
if (!readInt<2>(in, size)) return; // failed
// allocate size
str.resize(size);
// read contents
in.read(&str[0], size);
}
struct Person {
std::string lastName, firstName;
int age;
void write(std::ostream&) const;
void read(std::istream&);
};
void Person::write(std::ostream &out) const
{
writeString(out, lastName);
writeString(out, firstName);
writeInt<2>(out, age);
}
void Person::read(std::istream &in)
{
readString(in, lastName);
readString(in, firstName);
std::int16_t age; assert(sizeof age == 2); // ensure proper sign extension
if (readInt<2>(in, age)) this->age = age;
}
int main()
{
// sample
Person people[2] = {
{ "Mustermann", "Klaus", 23 },
{ "Doe", "John", -111 }
};
// write binary file
{ std::ofstream out("test.dat", std::ios::binary);
for (const Person &person : people) person.write(out);
} // closes file
// read sample
Person peopleIn[2] = {
{ "", "", -1 },
{ "", "", -1 }
};
// read binary file
{ std::ifstream in("test.dat", std::ios::binary);
for (Person &person : peopleIn) person.read(in);
} // closes file
// report result
int i = 1;
for (const Person &person : peopleIn) {
std::cout << "person " << i++ << ": '"
<< person.firstName << ' ' << person.lastName
<< ", age: " << person.age << '\n';
}
}
输出:
person 1: 'Klaus Mustermann, age: 23
person 2: 'John Doe, age: -111
的十六进制转储test.dat
:
00000000 0a 00 4d 75 73 74 65 72 6d 61 6e 6e 05 00 4b 6c |..Mustermann..Kl|
00000010 61 75 73 17 00 03 00 44 6f 65 04 00 4a 6f 68 6e |aus....Doe..John|
00000020 91 ff |..|
00000022
笔记:
与其他地方的简单相比,整数值 (readInt()
和) 的二进制 I/O 类型可能看起来过于复杂。我以一种更便携的方式完成了它,它甚至可以在具有不同字节顺序和/或不同大小的积分的不同平台上使用。writeInt()
out.write((char*)value, sizeof value);
推荐阅读
- c# - IEnumerable
foreach 返回结果中的所有页面;foreach 而不是 for 循环 - sql - 更新中的 SQlite 位运算
- c# - 根据条件编辑 ListBoxItem
- sql-server - 转置列sql server
- visual-studio - Visual Studio 15.7.3 更新后无法加载 Xamarin 解决方案
- azure - 在 Azure 中公开 webapp
- flutter - 根据内容将 TextField 高度扩展到某些行
- java - mouseEntered 中的图像和光标不匹配
- javascript - Angular 6 输入类型数字点/逗号
- html - 将锚的背景图像定位在锚文本的左侧?