#cpp #строки #структуры
На этот вопрос уже дан ответ здесь:
Ошибка сохранения сложной структуры в файле
(1 ответ)
Закрыт 10 месяцев назад.
Имеется структура
struct User {
string login;
string password;
};
Стоит задача сделать примитивную авторизация пользователя. Т.е. создается файлик,
в него записывается заполненный объект вышеуказанной структуры, а при последующих запусках
производится запрос логина+пароля, считываются данные из файлика и сравниваются. Код:
#include
#include
#include
#include
#include
using namespace std;
struct User {
string login;
string password;
};
void main ()
{
SetConsoleCP (1251); // установка универсальной кодировки
SetConsoleOutputCP (1251);
string path;
int realsize=0;
User u;
vector U;
bool exit = false;
do
{
system("cls");
cout<<"Укажите, на каком диске находится файл с регистрационными данными:\n";
getline(cin, path);
path += ":\\users.txt";
ifstream fin(path, ios_base::binary | ios_base::in);
if (fin.is_open())
{
cout<<"Отлично, ваш файл найден!\n";
fin.read((char*)&u, sizeof(User));
U.push_back(u);
fin.close();
cout << "Введите логин:\n";
getline(cin, u.login);
cout << "Введите пароль:\n";
getline(cin, u.password);
if (!U.at(0).login.compare(u.login) && !U.at(0).password.compare(u.password))
{
cout << "Вы авторизованы!\n";
}
else
{
cout << "Вы не авторизованы!\n";
}
exit = true;
}
else
{
cout << "Файл не найден и будет создан";
ofstream fout (path, ios_base::binary | ios_base::out);
if (fout.is_open())
{
cout << "Введите логин:\n";
getline(cin, u.login);
cout << "Введите пароль:\n";
getline(cin, u.password);
fout.write((char*)&u, sizeof(User));
fout.close();
cout << "Файл создан и данные внесены!\n";
}
else
{
cout << "Ошибка при создании файла! Работа приложения будет завершена.\n";
exit = true;
}
}
} while(!exit);
system("pause");
}
Вся беда в том, что при выполнении данного кода появляется ошибка:
Необработанное исключение по адресу 0x0FDECCC8 (msvcp110.dll) в test.exe: 0xC0000005:
нарушение прав доступа при чтении по адресу 0x0067ADE4.
Кадры стека вызовов:
Путем экспериментов установлено, что замена использования вектора на простой динамический
массив никак не влияет на ошибку (т.е. она по-прежнему появляется), а вот замена использования
string на char * приводит к устранению проявления данной ошибки. Погуглив, я пришел
в выводу, что возникает какая-то ошибка в деструкторе string (могу ошибаться). Может
кто-нибудь прояснить ситуацию и дать рекомендации по корректному использованию типа
string в данного рода задачах?
Ответы
Ответ 1
Самый простой способ (как уже упомянул в комментарии @pavel) - использовать текстовый режим работы с файлом и операторы форматированного ввода/вывода (operator<<, operator>>) для чтения/записи std::string из/в потока. Чтение: ifstream fin(path); if (fin) { fin >> u.login >> u.password; } Запись: ofstream fout(path); if (fout) { // Разделители нужны для последующего считывания fout << u.login << " " << u.password << "\n"; } При этом данный подход накладывает некоторые ограничения на строки: как минимум они не должны содержать в себе символы пробельной группы, т.к. такой символ будет расценен как разделитель. Функции istream::read, ostream::write в этом случае не используются вовсе. Причина, по которой они не работают как надо указана в ответе @gbg.Ответ 2
Запись в файл с помощью fout.write((char*)&u, sizeof(User)); действительно запишет побайтово содержимое структуры User, но т.к. она содержит не POD типы (std::string), то пользы от этого мало, т.к. сами данные ваших строк скорей всего в куче находятся. В вашем случае можно записать логин и пароль в файл (при условии, что в них не используется перевод строки), по одному на строчку, и также читать. Пример: int main() { struct User { std::string login; std::string password; }; { User user{ "login", "password" }; // Пишем логин и пароль в файл в две строчки std::ofstream f("users.txt"); f << user.login << std::endl << user.password; } { User user; // Читаем из файла std::ifstream f("users.txt"); std::getline(f, user.login); std::getline(f, user.password); std::cout << user.login << std::endl << user.password << std::endl; } }Ответ 3
Так работать не будет. string не является POD - типом, то есть сам объект типа string не содержит данные строки, они размещаются в другом месте (куда их затолкает аллокатор). Правило простое - все, что не является POD нельзя просто так взять и скопировать побайтно. Решение - использовать для хранения данных типы, являющиеся POD struct mu_ugly_pod { char user_name[100500]; char user_last_name[100500]; }; Такая штуковина должна писаться и читаться без проблем. Увы, все удобства, связанные со string в данном случае пропадут.
Комментариев нет:
Отправить комментарий