微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

fstream二进制写入/读取的意外行为,无法从文件读取有效值

如何解决fstream二进制写入/读取的意外行为,无法从文件读取有效值

要写入文件,我使用以下功能

void writeDBToFile_BinaryMode(std::vector<structDB::citizen> dataBase,std::string fileName) {

    std::fstream filetoWriteto;

    filetoWriteto.open(fileName,std::ios::binary | std::ios::out);

    for (auto &citizen : dataBase) {
        if (citizen.fullName == dataBase[dataBase.size() - 1].fullName
                && citizen.address.street
                        == dataBase[dataBase.size() - 1].address.street
                && citizen.address.houseNumber
                        == dataBase[dataBase.size() - 1].address.houseNumber
                && citizen.address.flatNumber
                        == dataBase[dataBase.size() - 1].address.flatNumber
                && citizen.age == dataBase[dataBase.size() - 1].age
                && citizen.gender == dataBase[dataBase.size() - 1].gender) {

            std::string currentCitizenEntry = citizen.fullName + ';'
                    + citizen.address.street + ';'
                    + std::to_string(citizen.address.houseNumber) + ';'
                    + std::to_string(citizen.address.flatNumber) + ';'
                    + citizen.gender + ';' + std::to_string(citizen.age);

            unsigned short int sizeOfLine = currentCitizenEntry.size();

            filetoWriteto.write(reinterpret_cast<char*>(&sizeOfLine),sizeof(sizeOfLine));

            filetoWriteto.write(reinterpret_cast<char*>(&currentCitizenEntry),sizeof(currentCitizenEntry));

        } else {
            std::string currentCitizenEntry = citizen.fullName + ';'
                    + citizen.address.street + ';'
                    + std::to_string(citizen.address.houseNumber) + ';'
                    + std::to_string(citizen.address.flatNumber) + ';'
                    + citizen.gender + ';' + std::to_string(citizen.age) + '\n';

            unsigned short int sizeOfLine = currentCitizenEntry.size();

            filetoWriteto.write(reinterpret_cast<char*>(&sizeOfLine),sizeOfLine);
        }

    }

    filetoWriteto.close();

}

要读取文件,我使用此功能

void readDBFromFile_BinaryMode(std::vector<structDB::citizen> &dataBase,std::string fileName) {

    std::ifstream filetoReadFrom;

    filetoReadFrom.open(fileName,std::ios::binary);

    while (!filetoReadFrom.eof()) {
        unsigned short int sizetoRead;

        filetoReadFrom.read(reinterpret_cast<char*>(&sizetoRead),sizeof(sizetoRead));

        std::string currentLine;

        filetoReadFrom.read(reinterpret_cast<char*>(&currentLine),sizetoRead);

        std::cout << sizetoRead << std::endl;
        std::cout << currentLine << std::endl << std::endl;

    }

    filetoReadFrom.close();
}

以下功能在上下文中使用:

    libdb::writeDBToFile_BinaryMode(dataBase,"writetofilebinary.txt");

    libdb::readDBFromFile_BinaryMode(dataBase,"writetofilebinary.txt");

但是,读取功能输出不是写入文件的预期有效值,而是半正确的数据与“垃圾”数据混合,例如:

0
Uɥ[ownsend;Highland Drive;5;156;Female

在执行结束时,返回错误

free(): double free detected in tcache 2
Aborted

究竟是什么导致我的代码无法按预期方式运行?

解决方法

您正在将std::string*中的char*投射到fileToWriteTo.write(reinterpret_cast<char*>(&currentCitizenEntry),sizeof(currentCitizenEntry));。这是错误的。正确的方法是fileToWriteTo.write(currentCitizenEntry.c_str(),currentCitizenEntry.length());
阅读fileToReadFrom.read(reinterpret_cast<char*>(&currentLine),sizeToRead);的方法也一样。您将读取一个临时缓冲区,然后从该缓冲区构建std::string

这可以编译并且可以工作,但是未经测试:

void writeDBToFile_BinaryMode(std::vector<structDB::citizen> dataBase,std::string fileName) {

    std::fstream fileToWriteTo;

    fileToWriteTo.open(fileName,std::ios::binary | std::ios::out);
    auto citizenCount = dataBase.size();
    auto ii = 0u;

   for (auto &citizen : dataBase) {
        std::string currentCitizenEntry = citizen.fullName + ';'
                + citizen.address.street + ';'
                + std::to_string(citizen.address.houseNumber) + ';'
                + std::to_string(citizen.address.flatNumber) + ';'
                + citizen.gender + ';' + std::to_string(citizen.age);
       
        //this way is much better to handle the last entry in the database          
        if (citizenCount - 1 == ii++)
            currentCitizenEntry += '\n';

        //std::string size() always return size_t,not an unsigned short int. Narrowing conversion is dangerous here.
        size_t sizeOfLine = currentCitizenEntry.size();

        fileToWriteTo.write(reinterpret_cast<char*>(&sizeOfLine),sizeof(sizeOfLine));
        // note that there is no reinterpret_cast anymore
        fileToWriteTo.write(currentCitizenEntry.c_str(),currentCitizenEntry.length());
    }
}
    
void readDBFromFile_BinaryMode(std::vector<structDB::citizen> &dataBase,std::string fileName) {

    std::ifstream fileToReadFrom;

    fileToReadFrom.open(fileName,std::ios::binary);

    while (!fileToReadFrom.eof()) {
        size_t sizeToRead; // note the size_t

        fileToReadFrom.read(reinterpret_cast<char*>(&sizeToRead),sizeof(sizeToRead));
        
        // in your variant you overwrited the std::string object with your data
        // which leads to memory corruption
        //fileToReadFrom.read(reinterpret_cast<char*>(&currentLine),sizeToRead);
        
        // allocate a buffer
        char* buffer =  new char[sizeToRead];
        // read to buffer
        fileToReadFrom.read(buffer,sizeToRead);
        // build the std::string from buffer
        std::string currentLine{buffer};
        
        std::cout << sizeToRead << std::endl;
        std::cout << currentLine << std::endl << std::endl;
        //free memory for buffer
        delete[] buffer;
    }

    fileToReadFrom.close();
}

此外,如果您没有义务使用readwrite函数,我建议移至operator<<()operator>>()

,

问题解决了。我使用的最终函数部分基于Suthiro的代码。

在这里。写:

void writeDBToFile_BinaryMode(std::vector < structDB::citizen > dataBase,std::string fileName) {

  std::fstream fileToWriteTo;

  fileToWriteTo.open(fileName,std::ios::binary | std::ios::out);

  for (auto & citizen: dataBase) {
    std::string currentCitizenEntry = citizen.fullName + ';' +
      citizen.address.street + ';' +
      std::to_string(citizen.address.houseNumber) + ';' +
      std::to_string(citizen.address.flatNumber) + ';' +
      citizen.gender + ';' + std::to_string(citizen.age);

    size_t sizeOfLine = currentCitizenEntry.size() + 1;
    char lineTermination = '\0';

    fileToWriteTo.write(reinterpret_cast < char * > ( & sizeOfLine),sizeof(sizeOfLine));

    for (auto & citizen: currentCitizenEntry) {
      fileToWriteTo.write(reinterpret_cast < char * > ( & citizen),sizeof(char));
    }
    fileToWriteTo.write(reinterpret_cast < char * > ( & lineTermination),sizeof(char));
  }
}

阅读:

void readDBFromFile_BinaryMode(std::vector < structDB::citizen > & dataBase,std::string fileName) {

  dataBase.clear();

  std::ifstream fileToReadFrom;

  fileToReadFrom.open(fileName,std::ios::binary);

  while (!fileToReadFrom.eof()) {
    size_t sizeToRead; // note the size_t

    fileToReadFrom.read(reinterpret_cast < char * > ( & sizeToRead),sizeof(sizeToRead));

    char * buffer = new char[sizeToRead];

    fileToReadFrom.read(reinterpret_cast < char * > (buffer),sizeToRead);

    std::string currentLine {
      buffer
    };

    delete[] buffer;

    unsigned int currChar = 0;

    while (currentLine[currChar] != '\0') {

      structDB::citizen citizen;

      while (currentLine[currChar] != ';') {
        citizen.fullName += currentLine[currChar];
        currChar++;

      }

      currChar++;
      while (currentLine[currChar] != ';') {
        citizen.address.street += currentLine[currChar];
        currChar++;
      }

      std::string numberTemp;

      currChar++;
      while (currentLine[currChar] != ';') {
        numberTemp += currentLine[currChar];
        currChar++;
      }

      citizen.address.houseNumber = std::stoi(numberTemp);
      numberTemp = "";

      currChar++;
      while (currentLine[currChar] != ';') {
        numberTemp += currentLine[currChar];
        currChar++;
      }

      citizen.address.flatNumber = std::stoi(numberTemp);
      numberTemp = "";

      currChar++;
      while (currentLine[currChar] != ';') {
        citizen.gender += currentLine[currChar];
        currChar++;
      }

      numberTemp = "";

      currChar++;
      while (currentLine[currChar] != '\0') {

        numberTemp += currentLine[currChar];
        currChar++;
      }

      citizen.age = std::stoi(numberTemp);

      dataBase.push_back(citizen);

    }

    fileToReadFrom.peek();
  }

  fileToReadFrom.close();
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。