С Новым годом! Форум программистов, компьютерный форум, киберфорум
Наши страницы

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 23, средняя оценка - 4.61
Gepar
1178 / 534 / 20
Регистрация: 01.07.2009
Сообщений: 3,517
#1

Файл произвольного доступа - C++

03.07.2011, 14:36. Просмотров 3325. Ответов 34
Метки нет (Все метки)

Очень прошу помощи так как не могу с ними разобраться который день, видать делаю одну и ту же ошибку но не нахожу. Создаю свой класс и потом когда пытаюсь его записать и считать возникают ошибки.
Ну да наверное проще начать обо всём по порядку чтобы не запутывать.

Проблема первая: файл каждый раз после записи 10 объектов моего класса в бинарном режиме при открытии текстовым редактором выглядит по разному, хотя размер его одинаковый, это меня настораживает.

Пример: запускаю скомпилированное приложение чтобы оно записало 10 объектов моего класса ClientData созданных конструктором по умолчанию и открываю через текстовый редактор и вижу символы :u.ы-I, закрываю и открываю опять своё приложение и вижу уже символы :ucИќ, за третим разом :uО(фҐ , тоесть первые 2 символа всегда те же, а остальные меняются, так ведь быть не должно? Значит ошибка у меня в классе? Тогда просьба подсказать где именно вроде строки уже не использую и все типы данных не должны меняться ...

Сам класс ClientData мой представляет собой класс с 3мя данными (номер, имя, баланс) и set и get методами для них (проверок кое-где нету, но не в них суть).


ClientData.h (Заголовочный файл к классу)
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#ifndef CLIENTDATA_H
#define CLIENTDATA_H
 
#include <string>
using std::string;
 
 
class ClientData
{
public:
    //конструктор по умолчанию который устанавливает
    //номер =0, имя = пустой строке и баланс =0
    ClientData(int=0,string="",double=0.0);
 
    void setID(int);
    int getID() const;
 
    void setName(string);
    string getName() const;
 
    void setBalance(double);
    double getBalance() const;
 
 
private:
    int ID; //номер
    char name[15]; //имя
    double balance; // баланс
};
 
#endif
ClientData.cpp (реализация самого класса)
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <string>
using std::string;
 
#include <cstring>
#include "ClientData.h"
 
ClientData::ClientData(int i, string n, double b)
{
    setID(i);
    setName(n);
    setBalance(b);
}
 
void ClientData:: setID(int i)
{
    ID=i;
}
 
int ClientData:: getID() const
{
    return ID;
}
 
void ClientData:: setName(string n)
{
    strncpy(name,n.data(),14);
}
 
string ClientData:: getName() const
{
    return name;
}
 
void ClientData:: setBalance(double b)
{
    balance=b;
}
 
double ClientData:: getBalance() const
{
    return balance;
}
main(совершаю только запись в файл 10 объектов типа ClientData
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <iomanip>
#include <fstream>
#include "ClientData.h"
using namespace std;
 
void newRecord(fstream&);
int menu();
void print(fstream&);
void print2(fstream&);
 
int main()
{
    //создаю объект типа ClientData
    ClientData client; 
    
    //открываю файл для чтения и записи в бинарном режиме
    fstream file("trans.dat",ios::binary|ios::in|ios::out);
    if (!file)
     cerr<<"Error";
 
    //записываю 10 моих объектов в файл
    for (int i=0;i<10;i++)
     file.write(reinterpret_cast<const char*>(&client),sizeof(ClientData));
}
Очень прошу помочь разобраться.
0
Лучшие ответы (1)
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
03.07.2011, 14:36
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Файл произвольного доступа (C++):

Файл произвольного доступа - C++
Читаю Дейтелов, про файлы произвольного доступа. Написал вроде как по примеру, но что-то идет не так. Идея такая - создается файл, в нем...

Файл произвольного доступа - C++
Здравствуйте, снова к файлам, такая задача. Допустим, у меня есть текстовый файл с данными - код, название, количество, и со значениями...

Не могу прочитать файл произвольного доступа с конца - C++
Странное происходит в функции read_file, а именно не работает seekg. Почему-то читается всё время последнее значение. Почему так и что с...

Обработка файлов произвольного доступа - C++
Создать файл, содержащий массив указателей на упорядоченные в алфавитном порядке строки, представленные записями переменной длины....

Детектировать итератор произвольного доступа - C++
здравствуйте, нужно детектировать итератор произвольного доступа... это не работает: template&lt; typename... &gt; using void_t = void; ...

Создание файла произвольного доступа. - C++
#include &lt;iostream.h&gt; #include &lt;fstream.h&gt; #include &lt;stdlib.h&gt; #define number 100 struct clientData { int acctNum; ...

34
pito211
186 / 173 / 8
Регистрация: 22.03.2010
Сообщений: 612
03.07.2011, 14:51 #2
конструктор неправильно написан
0
Gepar
1178 / 534 / 20
Регистрация: 01.07.2009
Сообщений: 3,517
03.07.2011, 14:53  [ТС] #3
pito211,напиши что именно, ато там 3 строчки, вроде всё ок ... хотя может слона я и не замечаю, но вы мне покажите его хоть
0
pito211
186 / 173 / 8
Регистрация: 22.03.2010
Сообщений: 612
03.07.2011, 14:54 #4
хотя может и правильно, но как то криво. Эта строка
C++
1
(int=0,string="",double=0.0)
0
easybudda
Модератор
Эксперт CЭксперт С++
9698 / 5648 / 964
Регистрация: 25.07.2009
Сообщений: 10,864
03.07.2011, 14:54 #5
Цитата Сообщение от Gepar Посмотреть сообщение
C++
1
2
3
4
void ClientData:: setName(string n) 
{
  strncpy(name,n.data(),14); 
}
Не факт, что проблема в этом, но тем не менее, здесть завершающего нуля не хватает. strncpy(), если ей передать больше символов, чем указано, скопирует, сколько сможет, и '\0' не добавит. Добавьте
C++
1
2
3
4
5
void ClientData:: setName(string n) 
{
  strncpy(name,n.data(),14);
  name[14] = '\0'; 
}
Цитата Сообщение от Gepar Посмотреть сообщение
C++
1
2
3
4
string ClientData:: getName() const 
{ 
  return name; 
}
А это просто не корректно. Функция должна string возвращать, возвращает то ли char* то ли const char*... Оно может само как-нибудь и приводится к нужному виду, но я бы как-то так сделал:
C++
1
2
3
4
5
string ClientData:: getName() const 
{ 
  string ret(name);
  return ret; 
}
И самое главное, что не понятно - а где собственно объекты ClientData инициализируются? По сути Вы в файл случайные данные отправляете...
0
pito211
186 / 173 / 8
Регистрация: 22.03.2010
Сообщений: 612
03.07.2011, 15:10 #6
кстати фаил не закрыл
0
Gepar
1178 / 534 / 20
Регистрация: 01.07.2009
Сообщений: 3,517
03.07.2011, 15:27  [ТС] #7
Цитата Сообщение от easybudda Посмотреть сообщение
А это просто не корректно. Функция должна string возвращать, возвращает то ли char* то ли const char*... Оно может само как-нибудь и приводится к нужному виду, но я бы как-то так сделал:
Переводится Хотя я попробую сделать как Вы написали, может эти преобразования как-то затрагивают мою запись в файл ...
Цитата Сообщение от Gepar Посмотреть сообщение
И самое главное, что не понятно - а где собственно объекты ClientData инициализируются?
Так вот же в .h файле всё инициализируется:
Цитата Сообщение от Gepar Посмотреть сообщение
//конструктор по умолчанию который устанавливает
//номер =0, имя = пустой строке и баланс =0
ClientData(int=0,string="",double=0.0);
Добавлено через 1 минуту
Цитата Сообщение от pito211 Посмотреть сообщение
хотя может и правильно, но как то криво. Эта строка
Ага, строка, из которой я потом копирую данные в массив символов.


Цитата Сообщение от pito211 Посмотреть сообщение
кстати фаил не закрыл
Сам закроется после выполнения, у меня пока беда в том что записывается криво, красивости и проверки я и потом могу навести ...

Добавлено через 1 минуту
Цитата Сообщение от easybudda Посмотреть сообщение
скопирует, сколько сможет, и '\0' не добавит. Добавьте
Кстати с вашим кодом будет два хвоста у файла (если меньше 14 символов то будет один символ конца например 10ым, а потом 14ым, можно тоже добавить проверку, ну да пока пусть так

Добавлено через 2 минуты
Учёл все правки, по прежнему каждый раз при открытии файла текстовым редактором символы разные после каждого запуска программы Код в первом посте сейчас поправлю с учётом правок.

Добавлено через 3 минуты
А нет, не поправлю, в любом случае код ClientData.cpp (реализация самого класса) теперь такой:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <string>
using std::string;
 
#include <cstring>
#include "ClientData.h"
 
ClientData::ClientData(int i, string n, double b)
{
    setID(i);
    setName(n);
    setBalance(b);
}
 
void ClientData:: setID(int i)
{
    ID=i;
}
 
int ClientData:: getID() const
{
    return ID;
}
 
void ClientData:: setName(string n)
{
    strncpy(name,n.data(),14);
    name[14] = '\0';
}
 
string ClientData:: getName() const
{
    string ret(name);
    return ret;
}
 
void ClientData:: setBalance(double b)
{
    balance=b;
}
 
double ClientData:: getBalance() const
{
    return balance;
}
Всё остальное не изменилось, что же ещё не так не пойму никак, соответственно если работать с таким классом то потом данные у меня с него читаются не корректно так что надо разобраться почему они записываются каждый раз по разному ... посмотрите ещё пожалуйста, может Вы чего увидите ещё подозрительного ...
0
easybudda
Модератор
Эксперт CЭксперт С++
9698 / 5648 / 964
Регистрация: 25.07.2009
Сообщений: 10,864
03.07.2011, 15:27 #8
Цитата Сообщение от Gepar Посмотреть сообщение
Кстати с вашим кодом будет два хвоста у файла (если меньше 14 символов то будет один символ конца например 10ым, а потом 14ым, можно тоже добавить проверку, ну да пока пусть так
Нуль-терминированная строка - это та, в которой за последним символом '\0' находится. Если строка помещается в буфере большего размера, то без разницы, сколько там ещё нулей после того, что в конце строки. А вот если нуля совсем не окажется - беда...
И тем не менее - данные для записи в файл откуда берутся?
0
Gepar
1178 / 534 / 20
Регистрация: 01.07.2009
Сообщений: 3,517
03.07.2011, 15:31  [ТС] #9
Цитата Сообщение от easybudda Посмотреть сообщение
И тем не менее - данные для записи в файл откуда берутся?
Тоесть? Вот же из main:
ClientData client; - создаю объект класса ClientData, создаю по умолчанию значит номер должен быть равен 0, имя= пустой строке и баланс =0.0. Потом пишу 10 раз его в файл
for (int i=0;i<10;i++)
file.write(reinterpret_cast<const char*>(&client),sizeof(ClientData));
Можно в main и инициализировать мой объект, но это ничего не меняет (всё равно каждый раз когда запускаю программу после выполнения получаю что та же часть символов изменяется, а ведь они не должны меняться, правильно я понимаю ... или всё же могут ... или нет?).
0
Bers
Заблокирован
03.07.2011, 16:48 #10
В код особливо не втыкал...
но могу предположить, что
file.write(reinterpret_cast<const char*>(&client),sizeof(ClientData)); //записывает в файл содержимое некоторого куска памяти.

И раз уж данные в файле каждый раз разные, стало быть... в самой памяти каждый раз разное....

А что там вообще в памяти?

Цитата Сообщение от Gepar Посмотреть сообщение
int ID; //номер
char name[15]; //имя
double balance; // баланс
А там есть массив чаров, который нигде явно нулями не инициализируется, а потому имеет полное право содержать всякий разный мусор.

Таким образом, кусок памяти выденный под этот массив каждый раз может содержать разные наборы ноликов и единичек.

Не знаю, как точно работает ваш сеттер. Но если ваш сеттер устанавливает дефолтное "",
то в лучшем случае только первый элемент обнуляется (если вообще обнуляется) и последний - нулём-терминатором.

А весь остальной мусор как был, так и остаётся.


По моему скромному мнению, вы неверно сериализуете данные.
Нужно загонять в непрерывный блок памяти значение полей данных, а не толкать в файл тупо кусок памяти целиком, со всевозможным сопутствующим мусором....


/ps проинициализируйте массив чаров явно нулями, тогда мусора не будит. А в файле всегда будит одно и тоже
1
grizlik78
Эксперт С++
1972 / 1465 / 122
Регистрация: 29.05.2011
Сообщений: 3,033
03.07.2011, 18:32 #11
Цитата Сообщение от Gepar Посмотреть сообщение
C++
1
strncpy(name,n.data(),14);
Вообще-то я уже говорил вчера, почему вместо .data() здесь надо использовать .c_str()
0
ForEveR
В астрале
Эксперт С++
7983 / 4742 / 321
Регистрация: 24.06.2010
Сообщений: 10,547
Завершенные тесты: 3
03.07.2011, 19:20 #12
А чем вариант с boost::serialization не понравился? Я ведь целиковый вариант в прошлой теме дал.
0
Gepar
1178 / 534 / 20
Регистрация: 01.07.2009
Сообщений: 3,517
03.07.2011, 19:46  [ТС] #13
ForEveR, а я такой нехороший так и не прочитал, извините, забылось за выходные )
Хотя что-то не сильно гугл выводит меня на информацию о нём и на сайте cplusplus.com нет статьи о нём ...

Цитата Сообщение от grizlik78 Посмотреть сообщение
Вообще-то я уже говорил вчера, почему вместо .data() здесь надо использовать .c_str()
С ней что-то тоже файл меняется в итоге.

Изменил вот так свою реализацию класса (менялась только реализация функции setName)
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <string>
using std::string;
 
#include <cstring>
#include "ClientData.h"
 
ClientData::ClientData(int i, string n, double b)
{
    setID(i);
    setName(n);
    setBalance(b);
}
 
void ClientData:: setID(int i)
{
    ID=i;
}
 
int ClientData:: getID() const
{
    return ID;
}
 
void ClientData:: setName(string n)
{
    const char* lname=n.data();
    int length=n.size();
    length=(length<15 ? length : 14);
    strncpy(name,lname,length);
    name[length]='\0';
}
 
string ClientData:: getName() const
{
    string ret(name);
    return ret;
}
 
void ClientData:: setBalance(double b)
{
    balance=b;
}
 
double ClientData:: getBalance() const
{
    return balance;
}
и ... всё равно меняется результат записи класса каждый раз, есть ещё идеи? Вот про char была хорошая идея, я на него до этого не думал.

Добавлено через 4 минуты
У меня есть ещё main по записи и выводу данных в файл этого класса(я его не постил чтобы не запутывать) так вот после этих исправлений в классе он по прежнему после считывания выводит испорченные данные (портит имя и баланс, номер не портит) так что массив символов имя видать по прежнему всё перекручивает, хотя я вроде уже переписал его так что портить же не должен был он ничего
0
grizlik78
Эксперт С++
1972 / 1465 / 122
Регистрация: 29.05.2011
Сообщений: 3,033
03.07.2011, 20:28 #14
Цитата Сообщение от Gepar Посмотреть сообщение
С ней что-то тоже файл меняется в итоге.
Я не говорил, что файл не будет меняться. Вопрос в другом: считывается опять неправильно?

Цитата Сообщение от Gepar Посмотреть сообщение
и ... всё равно меняется результат записи класса каждый раз, есть ещё идеи? Вот про char была хорошая идея, я на него до этого не думал.
Теперь получилось точно как с .c_str(), только в 100500 раз более громоздко
Считывание не работает? Файл можно не смотреть даже.
0
Gepar
1178 / 534 / 20
Регистрация: 01.07.2009
Сообщений: 3,517
03.07.2011, 20:47  [ТС] #15
Цитата Сообщение от grizlik78 Посмотреть сообщение
Считывание не работает? Файл можно не смотреть даже.
Нет, оно работает некорректно, притом очень странно, эх ладно, запощу почти полный свой main(надеюсь не испугаю тех кто заходит в тему )
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <iostream>
#include <iomanip>
#include <fstream>
#include "ClientData.h"
using namespace std;
 
void newRecord(fstream&);
void print(fstream&);
int main()
{
    ClientData client;
    fstream file("trans.dat",ios::binary|ios::in|ios::out);
    if (!file)
     cerr<<"Error";
 
    for (int i=0;i<100;i++)
     file.write(reinterpret_cast<const char*>(&client),sizeof(ClientData));
     newRecord(file);
     newRecord(file);
     newRecord(file);
     print(file);
}
 
void newRecord(fstream& file)
{
    if (!file)
     cerr<<"\nSomethin was wrong ...\n";
    cout<<"\nEnter number of new accaunt:\n? ";
    int account;
    cin>>account; //считать номер аккаунта
 
    file.seekp((account-1)*sizeof(ClientData)); //установить указатель на нужный номер
    ClientData client;
    file.read(reinterpret_cast<char*>(&client),sizeof(ClientData)); //считать текущее значение запрошенного аккаунта
 
    //если аккаунт пуст то считать и записать данные
    if(client.getID()==0)
    {
        cout<<"\nEnter name and balance for this client:";
        double balance;
        string name;
        cin>>name>>balance;
        client.setID(account);client.setBalance(balance);client.setName(name);
        file.seekp((account-1)*sizeof(ClientData));
        file.write(reinterpret_cast<const char*>(&account),sizeof(ClientData));
 
        //вывод добавлен чтобы убедиться что сразу после создания записи она нормально считывается
        file.seekp((account-1)*sizeof(ClientData));
        file.read(reinterpret_cast<char*>(&account),sizeof(ClientData));
        cout<<setw(3)<<left<<client.getID()<<setw(10)<<left<<client.getName()<<setw(6)<<left<<client.getBalance()<<endl;
    }
 
    //иначе выдать предупреждение
    else
     cerr<<"\nError, accaunt #"<<account<<" already exist!\n";
}
 
void print(fstream& print)
{
    print.seekp(0);
    ClientData client;
 
    //ищем хотя бы одно заполненное поле в БД
    do
    {
        print.read(reinterpret_cast<char*>(&client),sizeof(ClientData));
    }while (client.getID()==0);
 
    //если такое поле есть то логично вывести подписи к столбцам
    if (client.getID()!=0)
    {
        cout<<setw(3)<<left<<"ID"<<setw(10)<<left<<"NAME"<<setw(6)<<left<<"BALANCE"<<endl;
    }
    //иначе выйти из функции так как все поля БД пусты
    else
     return;
 
    print.clear();//сбросить флаг конца файла
    print.seekp(0);
 
    print.read(reinterpret_cast<char*>(&client),sizeof(ClientData)); //считать первую запись
    while(!print.eof())
    {
        //если запись существует то вывести данные
        if (client.getID()!=0)
        {
            cout<<setw(3)<<left<<client.getID()<<setw(10)<<left<<client.getName()<<setw(6)<<left<<client.getBalance()<<endl;
        }
        print.read(reinterpret_cast<char*>(&client),sizeof(ClientData));
    }
    print.clear(); //сбросить флаг конца файла для выполения последующих операций
 
}
Посмотрите пожалуйста, тут всё очень и очень интересно:
1) Я вызываю 3 раза создание нового клиента (есть менюшка но это ещё + код, не буду запутывать), после его создания я добавил считывание и вывод данных. По работе этого я вижу что всё хорошо и данные после записи считываются нормально. Если пытаешься перезаписать данные то функция отлично видит что они уже есть так что пишет вроде нормально, вроде.
2)Я вызываю функцию для печати данных и что я вижу: поле номер считалось нормально, поле имя пустое(чаще всего), а в поле баланс поселилось что-то непонятное вида 2.53354e-307 которое появляется всегда в нём.

Помогите, справиться с этим злосчастным классом, а, очень хочется разобраться с ним
На всякий случай креплю все файлы в zip архиве, может кому так будет удобнее.
0
Вложения
Тип файла: zip c++.zip (22.5 Кб, 13 просмотров)
03.07.2011, 20:47
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
03.07.2011, 20:47
Привет! Вот еще темы с ответами:

Пишу итератор произвольного доступа - C++
Пишу итератор произвольного доступа. По минимуму он должен поддерживать несколько операций, но меня смутила операция n+it, где n это целое...

Создание файла произвольного доступа - C++
Здравствуйте.Ни как не могу разобраться с файлами произвольного доступа.Но эта тема посвящена именно созданию таких файлов. #include...

В чем ошибка(файлы произвольного доступа) - C++
#include &lt;iostream&gt; #include &lt;fstream&gt; #include &lt;Windows.h&gt; using namespace std; struct PHONE { char Company;//комания ...

Корень произвольного степени из произвольного числа, введенного с клавиатуры - C++
Не применяя встроенных функций, найти корень произвольного степени из произвольного числа, введенного с клавиатуры. Из четырех первых цифр...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
15
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.