将结构写入二进制文件后,该文件仍然具有普通字符,而不是不可读的字符 [英] After writing a structure into a binary file, the file still has normal characters instead of unreadable ones

查看:78
本文介绍了将结构写入二进制文件后,该文件仍然具有普通字符,而不是不可读的字符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将结构写入二进制文件.该结构由字符串和整数组成.如果没有字符串,我只是将整个对象带入正常情况下将其写入二进制文件,但是如果现在执行该操作,则也可以轻松读取字符串.

I'm trying to write a structure to a binary file. The structure consists of strings and integers. If there were no strings I'd just take the entire object and write it to the binary file normally, but if I do that now, the string can be read easily as well.

因此,我决定分别编写该结构的每个属性.我正在使用这些字符串,就像在Stackoverflow答案中提到的一样.

So I decided to write each of the attribute of the structure separately. I was working with the strings the same way like it's mentioned in this Stackoverflow answer.

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

This is the main function that is supposed to save the structure into the name.bin file.

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");
    }
};

这是用于将每个属性写入文件的功能.

and this is a function for writing each attribute into the 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());
}

生成的文件具有纯文本可读的所有内容.尽管如果我改为在主方法中调用 myFile.write((char *)people,sizeof(people)); ,它会正确显示无法读取的字符,但仍可以正常读取字符串变量.这就是为什么我将所有变量都转换为字符串,然后将其完全写入bin文件的原因.

The resulting file has everything in plain text readable. Though if I instead in the main method call myFile.write((char*)people, sizeof(people)); then it shows up unreadable characters correctly but string variables are still normally read. That's why I went with converting into the string all variables and then writing it to a bin file altogether.

为什么我的方法仍然显示所有字符串,甚至不是二进制文件?即使我将std :: ios :: binary作为参数?

How come my method still shows all strings like it's not even binary file? Even though I have std::ios::binary as a parameter?

输出文件包含以下内容:

The output file contains this:

Name1lastName1Street11111City11111111Name2lastName2Street22222City22222222Name3lastName3Street33333City3333333

如果我将整个结构写到二进制文件中,则看起来像这样:

Whereas if I write the entire structure into the binary file, it looks like this:

     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的标头

As requested here is a header for 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

推荐答案

用于序列化 std :: string (长度限制为≤ 65536个字符)的示例:

A sample to serialize a std::string (with a limited length of ≤ 65536 characters):

#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位).可以像序列化整数值一样进行此操作.

Consider how the length (limited to 16 bit) is written. This can be done similar to serialize integral values.

C ++ FAQ提供了一个(IMHO)很好的介绍:

A (IMHO) good introduction is provided by the C++FAQ:

> 序列化和反序列化

组合类型为 Person 的二进制I/O的扩展示例:

An extended sample for binary I/O of a composed type 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

在大肠杆菌上进行实时演示

注意:

与简单的 out.write()相比,整数值( readInt() writeInt())的二进制I/O可能看起来过于复杂.(char *)value,sizeof value); 在其他位置找到.我以一种更加可移植的方式完成了该操作,甚至可以在具有不同字节序和/或不同积分大小的不同平台上使用.

The kind of binary I/O for integral values (readInt() and writeInt()) might look overcomplicated compared to the simple out.write((char*)value, sizeof value); found elsewhere. I did it in a more portable way which will even work when used on different platforms with distinct endianess and/or distinct size of integrals.

这篇关于将结构写入二进制文件后,该文件仍然具有普通字符,而不是不可读的字符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆