不能 write/read 一个 class 对象数组到一个文件
Can't write/read an array of class objects to a file
我有一个 class 用户,有 3 个参数:id、pass、active
我想将一组用户对象写入文件,但它不起作用。
有时它可以工作,但之后我只能读取这些参数:id、active。
参数 pass 给我:什么都没有,一些字符或一个字符(几乎 'a')。
class:
class User {
int id;
char *pass;
bool active;
public:
User() {
id = -1;
pass = NULL;
active = 0;
}
User(int id, char * pass, bool active = 1) {
this->id = id;
if (pass) delete[] pass;
this->pass = new char[strlen(pass)];
memcpy(this->pass, pass, strlen(this->pass));
this->active = active;
}
void set_id(int id) {
this->id = id;
}
void set_active(bool active) {
this->active = active;
}
void set_pass(char *pass) {
this->pass = new char[strlen(pass)];
memcpy(this->pass, pass, strlen(this->pass));
}
int get_id() {
return id;
}
char *get_pass() {
return pass;
}
bool get_active() {
return active;
}
};
这是我的数组:
User *users = new User[999999]; int size_ = 0;
主要:
int main() {
fstream r_user("user.txt", ios::binary | ios::in);
if (!r_user) {
r_user.close();
char pass[100];
cerr << "You don't have any user, please create user" << endl;
cout << "User pass: "; cin >> pass;
users[0].set_id(0);
users[0].set_pass(pass);
users[0].set_active(1);
size_++;
cout << users[0].get_pass();
fstream w_user("user.txt", ios::binary | ios::out);
w_user.write(reinterpret_cast<char *>(users), size_ * sizeof(User));
w_user.close();
}
else {
r_user.seekg(0, r_user.end);
size_ = r_user.tellg();
r_user.seekg(0, r_user.beg);
size_ = size_ / sizeof(users);
r_user.read(reinterpret_cast<char *>(users), 99999 * sizeof(User));
r_user.close();
cout << users[0].get_pass();
}
return 0;
}
文件不为空。
您的代码在处理通行证存储的方式上存在两个严重错误。
您使用:
this->pass = new char[strlen(pass)];
memcpy(this->pass, pass, strlen(this->pass));
注意任何字符串都是以NULL字节结尾的,所以你要复制的数据的大小是strlen(pass)+1。例如:strlen("abcedf") == 6 但你需要 7 个字节来存储它。
后果可能是戏剧性的:当试图读取字符串时,软件将访问连接到该区域的其他内存块,并读取错误的字符串(因为没有 NULL 字节会阻止字符串读取)。随着时间的推移,随着函数的增多,这个小错误可能会将内存泄漏到文件中,可能会导致崩溃或以逐一的方式写入 NULL 字节,函数停止正常工作并且一切都变得糟糕......听起来像地狱?
this->pass = new char[strlen(pass) + 1];
memcpy(this->pass, pass, strlen(this->pass) + 1);
或者,更优雅:
this->pass = new char[strlen(pass) + 1];
this->pass = strcpy(this->pass, pass);
或者更优雅更安全:
this->pass = strdup(pass);
您可能还想为这种管理使用一个独特的私有函数,实际上这个敏感部分在您的代码中是重复的。
然后散列通行证? :-) 如果你想要一些严肃的工作,请选择 Bcrypt:https://github.com/rg3/bcrypt
我还看到您使用指针来表示传递内容。所以你保存了指针,但没有保存指向的数据。
当加载指针的值时,内存中没有任何东西,或者如果有东西肯定不是你的通行证(从未保存过)。您必须使用静态缓冲区来存储该通道,并确保您的 class 将保留 POD(普通旧数据)。
通过使用散列密码,您的最终缓冲区将具有确定的大小。例如:
char bcrypt_password[64];
而不是:
char *pass;
但请注意您的分配:
new User[999999];
会占用大量内存。
我会是你,我会去序列化你的对象,并集成 import() export() 方法。基本的 CSV 序列化将允许以后的数据库导入/导出。
fstream
仅转储原始数据,因此它仅适用于常规变量。
pass
是char*
,所以它存储的是指针地址而不是字符串值。
您的 class 需要自定义序列化。
将方法添加到您的用户 class 到 Load()
和 Save()
以便您可以处理 pass
变量。他们应该接受 fstream
缓冲区,这样他们就可以 read/write 它。
我有一个 class 用户,有 3 个参数:id、pass、active
我想将一组用户对象写入文件,但它不起作用。
有时它可以工作,但之后我只能读取这些参数:id、active。
参数 pass 给我:什么都没有,一些字符或一个字符(几乎 'a')。
class:
class User {
int id;
char *pass;
bool active;
public:
User() {
id = -1;
pass = NULL;
active = 0;
}
User(int id, char * pass, bool active = 1) {
this->id = id;
if (pass) delete[] pass;
this->pass = new char[strlen(pass)];
memcpy(this->pass, pass, strlen(this->pass));
this->active = active;
}
void set_id(int id) {
this->id = id;
}
void set_active(bool active) {
this->active = active;
}
void set_pass(char *pass) {
this->pass = new char[strlen(pass)];
memcpy(this->pass, pass, strlen(this->pass));
}
int get_id() {
return id;
}
char *get_pass() {
return pass;
}
bool get_active() {
return active;
}
};
这是我的数组:
User *users = new User[999999]; int size_ = 0;
主要:
int main() {
fstream r_user("user.txt", ios::binary | ios::in);
if (!r_user) {
r_user.close();
char pass[100];
cerr << "You don't have any user, please create user" << endl;
cout << "User pass: "; cin >> pass;
users[0].set_id(0);
users[0].set_pass(pass);
users[0].set_active(1);
size_++;
cout << users[0].get_pass();
fstream w_user("user.txt", ios::binary | ios::out);
w_user.write(reinterpret_cast<char *>(users), size_ * sizeof(User));
w_user.close();
}
else {
r_user.seekg(0, r_user.end);
size_ = r_user.tellg();
r_user.seekg(0, r_user.beg);
size_ = size_ / sizeof(users);
r_user.read(reinterpret_cast<char *>(users), 99999 * sizeof(User));
r_user.close();
cout << users[0].get_pass();
}
return 0;
}
文件不为空。
您的代码在处理通行证存储的方式上存在两个严重错误。
您使用:
this->pass = new char[strlen(pass)];
memcpy(this->pass, pass, strlen(this->pass));
注意任何字符串都是以NULL字节结尾的,所以你要复制的数据的大小是strlen(pass)+1。例如:strlen("abcedf") == 6 但你需要 7 个字节来存储它。
后果可能是戏剧性的:当试图读取字符串时,软件将访问连接到该区域的其他内存块,并读取错误的字符串(因为没有 NULL 字节会阻止字符串读取)。随着时间的推移,随着函数的增多,这个小错误可能会将内存泄漏到文件中,可能会导致崩溃或以逐一的方式写入 NULL 字节,函数停止正常工作并且一切都变得糟糕......听起来像地狱?
this->pass = new char[strlen(pass) + 1];
memcpy(this->pass, pass, strlen(this->pass) + 1);
或者,更优雅:
this->pass = new char[strlen(pass) + 1];
this->pass = strcpy(this->pass, pass);
或者更优雅更安全:
this->pass = strdup(pass);
您可能还想为这种管理使用一个独特的私有函数,实际上这个敏感部分在您的代码中是重复的。
然后散列通行证? :-) 如果你想要一些严肃的工作,请选择 Bcrypt:https://github.com/rg3/bcrypt
我还看到您使用指针来表示传递内容。所以你保存了指针,但没有保存指向的数据。
当加载指针的值时,内存中没有任何东西,或者如果有东西肯定不是你的通行证(从未保存过)。您必须使用静态缓冲区来存储该通道,并确保您的 class 将保留 POD(普通旧数据)。
通过使用散列密码,您的最终缓冲区将具有确定的大小。例如:
char bcrypt_password[64];
而不是:
char *pass;
但请注意您的分配:
new User[999999];
会占用大量内存。
我会是你,我会去序列化你的对象,并集成 import() export() 方法。基本的 CSV 序列化将允许以后的数据库导入/导出。
fstream
仅转储原始数据,因此它仅适用于常规变量。
pass
是char*
,所以它存储的是指针地址而不是字符串值。
您的 class 需要自定义序列化。
将方法添加到您的用户 class 到 Load()
和 Save()
以便您可以处理 pass
变量。他们应该接受 fstream
缓冲区,这样他们就可以 read/write 它。