问题描述
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*>(¤tCitizenEntry),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*>(¤tLine),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*>(¤tCitizenEntry),sizeof(currentCitizenEntry));
。这是错误的。正确的方法是fileToWriteTo.write(currentCitizenEntry.c_str(),currentCitizenEntry.length());
阅读fileToReadFrom.read(reinterpret_cast<char*>(¤tLine),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*>(¤tLine),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();
}
此外,如果您没有义务使用read
和write
函数,我建议移至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();
}