C++ 将结构写入二进制文件后,该文件仍然具有普通字符,而不是无法读取的字符

C++ 将结构写入二进制文件后,该文件仍然具有普通字符,而不是无法读取的字符,c++,binaryfiles,binary-data,file-writing,C++,Binaryfiles,Binary Data,File Writing,我正在尝试将结构写入二进制文件。结构由字符串和整数组成。如果没有字符串,我只需要将整个对象正常地写入二进制文件,但是如果我现在这样做,字符串也可以很容易地读取 所以我决定分别编写结构的每个属性。我在用琴弦演奏 这是将结构保存到name.bin文件中的主要功能 void saveFileBin(std::string nameOfFile){ 人物[数组的大小]= {个人(“姓名1”、“姓氏2”、地址(“街道1”、“城市1”、“111”)、日期(1111年1月1日), 此人(“姓名2”、“姓氏2”

我正在尝试将结构写入二进制文件。结构由字符串和整数组成。如果没有字符串,我只需要将整个对象正常地写入二进制文件,但是如果我现在这样做,字符串也可以很容易地读取

所以我决定分别编写结构的每个属性。我在用琴弦演奏

这是将结构保存到name.bin文件中的主要功能

void saveFileBin(std::string nameOfFile){
人物[数组的大小]=
{个人(“姓名1”、“姓氏2”、地址(“街道1”、“城市1”、“111”)、日期(1111年1月1日),
此人(“姓名2”、“姓氏2”、地址(“街道2”、“城市2”、“222”)、日期(2、2、2222)),
个人(“姓名3”、“姓氏3”、地址(“街道3”、“城市3”、“333”)、日期(3333);
std::ofstreammyfile(nameOfFile+“.bin”,std::ios::binary);
如果(myFile.is_open())
{
for(int i=0;i<数组的大小;i++)
{
人员[i]。编写(&myFile);
}
myFile.close();
std::cout 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());
}
结果文件的所有内容都是纯文本可读的。 虽然如果我改为在主方法中调用
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一次
#ifndef人员
#定义人
#包括
#包括“Address.h”
#包括“Date.h”
#包括
结构人{
公众:
人员(std::string name、std::string姓氏、地址、住所、出生日期);
人();
friend std::ostream&operator(std::istream&is,Person&p);
std::string getName()常量{return\u name;}
std::string getLastName()常量{return\u lastName;};
地址getResidence()const{return\u residence;};
Date getDate()常量{return\u birthDate;};
无效读取(标准::ifstream*in);
无效写入(std::ofstream*out);
私人:
std::string _name;
std::string _lastName;
地址(香港);;
日期-出生日期;
};
#endif//!PERSON_H

序列化
std::string
(长度限制为≤ 65536个字符):

test.dat的hextump

00000000 08 00 41 6e 74 72 6f 70 68 79 |…无齿|
0000000 A

注意:

考虑如何写入长度(限制为16位)。这可以类似于序列化整数值


C++常见问题解答提供了(IMHO)良好的介绍:


组合类型的二进制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';
  }
}
test.dat的hextump

00000000 0a 00 4d 75 73 74 65 72 6d 61 6e 6e 05 00 4b 6c |吉隆坡穆斯特曼|
00000010617573170000300446F6504004A 6F686E | aus…Doe…John|
000000 20 91 ff||
00000022

注意:


整型值(
readInt()
writeInt()
)的二进制I/O类型与简单的
out.write((char*)值,sizeof值)相比可能过于复杂
可在其他地方找到。我采用了一种更便携的方式,即使在不同的平台上使用不同的尾数和/或不同的整数大小时也能工作。

字符
a
内部由其ASCII码65表示。将字符串写入二进制文件,字符串的字节将被写入。使用文本编辑查看或者,在这个二进制文件中,字节将再次被解释为字符,即值为65的字节再次成为
A
。不管你认为二进制是什么,它只是二进制,但没有加密。查看
.exe
文件。即使在那里,你也会发现一些可读文本的部分(可能)。顺便说一句
out->write(_name.c_str(),_name.size());
覆盖所有字符,但不覆盖字符串的结尾处。请写入
_name.size()+1
以捕获0终止符,或者在字符串前面加上写入长度的前缀(二进制)。二进制文件并不表示“加密”顺便说一句,你能提供Person类的声明吗?我怀疑你写的是((char*)人,sizeof(people))不向文件写入任何字符串,只向them@Antrophy然而,如果我将整个结构写入二进制文件——让我们看看结构。正如前面的评论所述,如果
Person
不可复制,那么就不应该写入二进制文件。@Antrophy——这就解决了——你不能写入
Person
到那样的二进制文件。您的类不是POD或可复制的。还要从逻辑上考虑一下——您可能在编写代码时使用了
sizeof(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';
  }
}