Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Глюки с записья файла


Автор: Xenon 16.12.2006, 22:47
И опять какие-то глюки. Я fstream file вынес и сделал полем класса Cmanager и в методе save(). Теперь у меня выкидывается исключение, мол файл не существует. Если я убираю if(!file) { throw "Couldn`t open file" то цикл нормально крутится, файл создается, но размером 0 байт. Если же в save вручную ввести локальную переменную ofstream file2 и везде вместо file там испольлзовать file2, то все работает. Ошибка, вероятно, глупая. Я ее не вижу smile
И вообще save_as() вызывающий save() у меня сделано нормально, или так не делают? 
Код

#include <deque>
#include <fstream>
#include <string>
using std::cout;
using std::cin;
using std::endl;
using std::fstream;
using std::string;
using std::deque;

#pragma warning (disable:4267)

class Calbum
{
    friend class Cmanager;
private:
    int year;
    string genre,artist,title;
    Calbum(){}
    Calbum(int _year, string _genre, string _artist, string _title):year(_year),genre(_genre),artist(_artist),title(_title){}
    ~Calbum(){}
};

class Cmanager
{
public:
    bool is_changed;
////Adding album/////////////////////////////////////////////////////////////////
    void add_album(int year, string genre, string artist, string title)
    {
        album_list.push_back(new Calbum(year, genre, artist, title));
    }
/////////////////////////////////////////////////////////////////////////////////
    void add_album()
    {
        int year;
        string genre,artist,title;
        cout << "Enter artist: ";
        cin >> artist;
        cout << "Enter title: ";
        cin >> title;
        cout << "Enter genre: ";
        cin >> genre;
        cout << "Enter year: ";
        cin >> year;
        add_album(year,genre,artist,title);
        cout << "Successfully added\n";
        is_changed=true;
    }    
///Displaying a single album/////////////////////////////////////////////////////
    void display ()
    {
        cout << "Enter album`s number: ";
        int index;
        cin >> index;
        cout << album_list[index]->artist << " ";
        cout << album_list[index]->title << " ";
        cout << album_list[index]->genre << " ";
        cout << album_list[index]->year << endl;
    }
///Displaying all the albums/////////////////////////////////////////////////////
    void display_all()
    {
        cout << "---------------------------------\n";
        for (int i=0;i<(int)album_list.size();i++)
        {
            cout << album_list[i]->artist << " ";
            cout << album_list[i]->title << " ";
            cout << album_list[i]->genre << " ";
            cout << album_list[i]->year << endl;
        }
        cout << "---------------------------------\n";
    }
////Importing files from txt format/////////////////////////////////////////////
    void import_txt()
    {
        cout << "Enter path: ";
        cin.getline(path,256);
        file.open(path,std::ios::in);
        if(!file)
        {
            throw ("Coudn`t open file\n");
        }
        int year;
        string artist;
        string genre;
        string title;
        cout << "Reading file";
        while(!file.eof())
        {
            cout << "."; //To show, that something happens :)
            file >> artist >> title >> genre >> year;
            if(file.fail()) 
            {
                break;        
            }            
            add_album(year, genre, artist, title);        
        }
        file.close();
        cout << "Ok\n";
    }
////Export to txt format/////////////////////////////////////////////
    void export_txt()
    {
        if (album_list.size()==0)
        {
            throw ("Nothing to export!\n");
        }
        cout << "Where do you want to export? ";
        cin.getline(path,256);
        file.open(path,std::ios::out);
        if (!file)
        {
            throw ("Couldn`t open file!");
        }
        cout << "Exporting ... ";
        for (int i=0;i<static_cast<int>(album_list.size());i++)
        {
            file << album_list[i]->artist << " " << album_list[i]->title << " " << album_list[i]->genre << " " << album_list[i]->year << "\n";
        }
        file.close();
        cout << "Ok\n";
    }
////Get albums count//////////////////////////////////////////////////////////
    int get_size()
    {
        return (int)album_list.size();
    }
/////Removing an album////////////////////////////////////////////////////////
    void del()
    {
        int index=0;
        cout << "Enter index: ";
        cin >> index;
        if (index>=0 && index<static_cast<int>(album_list.size()) )
        {
            deque<Calbum*>::iterator iter=album_list.begin()+index;
            album_list.erase(iter);
            is_changed=true;
        }
        else
        {
            throw "Invalid index\n";
        }
    }
/////Sort by year///////////////////////////////////////////////////////
    void sort()
    {
        for (int j=1,i=0;j<static_cast<int>(album_list.size());j++)
        {
            Calbum* temp=album_list[j];
            for (i=j-1;i>=0 && album_list[i]->year > temp->year;i--)
            {
                album_list[i+1]=album_list[i];
            }
            album_list[i+1]=temp;
        }
    }
/////Save changes to file////////////////////////////////////////////////////////
    void save()
    {
        file.open(path,std::ios::binary | std::ios::out);
        if(!file)
        {
            throw ("Could not open path");
        }
        for (int i=0;i<static_cast<int>(album_list.size());i++)
        {
            cout << i;
            file.write(reinterpret_cast<char*>(&album_list[i]->year),sizeof(int));
            int tmp = album_list[i]->artist.size();
            file.write(reinterpret_cast<char*>(&tmp),sizeof(int)); //Write size of artist
            file.write(album_list[i]->artist.c_str(),sizeof(char)*(album_list[i]->artist.size()));//Write artist

            tmp = album_list[i]->genre.size();
            file.write(reinterpret_cast<char*>(&tmp),sizeof(int));//Write size of genre
            file.write(album_list[i]->genre.c_str(),sizeof(char)*(album_list[i]->genre.size()));//Write genre

            tmp = album_list[i]->title.size();
            file.write(reinterpret_cast<char*>(&tmp),sizeof(int));//Write sizeof title
            file.write(album_list[i]->title.c_str(),sizeof(char)*(album_list[i]->title.size()));//Write title
        }
        file.close();
        is_changed=false;//We have saved changes, so file is not changed now
    }
/////Save as to file////////////////////////////////////////////////////////
    void save_as()
    {
        cout << "Enter path to file: ";
        cin.getline(path,256);
        save();
    }

    void open()
    {
        cout << "Enter path to file: ";
        cin.getline(path,256);
        file.open(path,std::ios::binary | std::ios::in);
        if (!file)
        {
            throw ("Could not open file");
        }
        cout << "Reading file ";
        while (!file.eof())
        {
            cout << ".";
            string genre,title,artist;
            int year,size_g,size_t,size_a;
            file.read(reinterpret_cast<char*>(&year),sizeof(int));
            file.read(reinterpret_cast<char*>(&size_a),sizeof(int)); //Get size of artist field
            artist.resize(size_a);
            file.read(&artist[0],size_a);//Write data to artist variable
            file.read(reinterpret_cast<char*>(&size_g),sizeof(int));
            genre.resize(size_g);
            file.read(&genre[0],size_g);
            file.read(reinterpret_cast<char*>(&size_t),sizeof(int));
            title.resize(size_t);
            file.read(&title[0],size_t);
            if(file.eof()) 
            {
                break;        
            }    
            album_list.push_back(new Calbum(year, genre, artist, title));
        }
        file.close();
        cout << " Ok\n";
    }
    Cmanager():is_changed(false){}
    ~Cmanager()
    {
        for (int i=0;i<(int)album_list.size();i++)
        {
            delete album_list[i];
            cout << "Killed vector " << i << endl;
        }
        cout << "Memory freed";
    }
private:
    deque<Calbum*> album_list;
    char path[256]; //Path to file
    fstream file;
};


И как лучше реализовать проверку "Если файл открыт, ты при вызове save() просто сохранить файл, а если нет, то вызвать save_as() для введения пути к файлу"? Проверять в save условие if (!file.is_open()) { save_as(); }. Ну, естественно, из метода open() надо будет выкинуть file.close()". 

Автор: zkv 17.12.2006, 04:13
Xenon,  можно уточнить вопрос? т.е. привести участок кода, где возникает ошибка, и все что может быть с ней связано - создание объекта класса, инициализация членов и т.д. 
А то кода много, копаться лень smile 

Автор: Rockie 17.12.2006, 04:42
Xenon, если честно, не очень понятна логика.. 

По мне так - сделать массив path динамическим и в кострукторе занулять его.

Тогда в методе Save():

Если в массиве path что-то есть, смотрим, есть ли такой файл
  - если такой файл есть, дописать туда(или переписать, не знаю что тебе нужно)
  - если нет такого - спрашиваем, заносим в path название и пишем

fstream file лучше создавать в методе, это обеспечит закрытие файла при выходе из метода(ИМХО).



Автор: Xenon 17.12.2006, 14:24
Ну я вроде нормально написал где проблема в каком методе ... Ок, немного поподробнее smile
Проблема возникают тут, но где она родилась - не знаю.
Код

    void save()
    {
        file.open(path,std::ios::binary | std::ios::out);
        if(!file)
        {
            throw ("Could not open path");
        }
        for (int i=0;i<static_cast<int>(album_list.size());i++)
        {
            file.write(reinterpret_cast<char*>(&album_list[i]->year),sizeof(int));
            int tmp = album_list[i]->artist.size();
            file.write(reinterpret_cast<char*>(&tmp),sizeof(int)); //Write size of artist
            file.write(album_list[i]->artist.c_str(),sizeof(char)*(album_list[i]->artist.size()));//Write artist

            tmp = album_list[i]->genre.size();
            file.write(reinterpret_cast<char*>(&tmp),sizeof(int));//Write size of genre
            file.write(album_list[i]->genre.c_str(),sizeof(char)*(album_list[i]->genre.size()));//Write genre

            tmp = album_list[i]->title.size();
            file.write(reinterpret_cast<char*>(&tmp),sizeof(int));//Write sizeof title
            file.write(album_list[i]->title.c_str(),sizeof(char)*(album_list[i]->title.size()));//Write title
        }
        file.close();
        is_changed=false;//We have saved changes, so file is not changed now
    }
/////Save as to file////////////////////////////////////////////////////////
    void save_as()
    {
        cout << "Enter path to file: ";
        cin.getline(path,256);
        save();
    }

Как видите у меня в методе save() имеется условие:
Код

        if(!file)
        {
            throw ("Could not open path");
        }


Сейчас в независимости от того, какой путь, у меня всегда выкидывается исключение "Couldn`t open path". 
Если же вышеприведенный участок закомментировать, а в цикл добавить вывод состояния счетчика, то мы увидим, что цикл работает.
Код

        for (int i=0;i<static_cast<int>(album_list.size());i++)
        {
            cout << i; //То самое состояние - Показываем номер итерации
            file.write(reinterpret_cast<char*>(&album_list[i]->year),sizeof(int));
            int tmp = album_list[i]->artist.size();
            file.write(reinterpret_cast<char*>(&tmp),sizeof(int)); //Write size of artist
            file.write(album_list[i]->artist.c_str(),sizeof(char)*(album_list[i]->artist.size()));//Write artist

            tmp = album_list[i]->genre.size();
            file.write(reinterpret_cast<char*>(&tmp),sizeof(int));//Write size of genre
            file.write(album_list[i]->genre.c_str(),sizeof(char)*(album_list[i]->genre.size()));//Write genre

            tmp = album_list[i]->title.size();
            file.write(reinterpret_cast<char*>(&tmp),sizeof(int));//Write sizeof title
            file.write(album_list[i]->title.c_str(),sizeof(char)*(album_list[i]->title.size()));//Write title
        }

Если у меня 3 альбома в album_list, то на экране появятся цифры "0,1,2". Создается файл, но он размером в 0 байт. Если же сделать так:
Код

    void save()
    {
        ofstream file2;
        file2.open(path,std::ios::binary | std::ios::out);
        if(!file2)
        {
            throw ("Could not open path");
        }
        for (int i=0;i<static_cast<int>(album_list.size());i++)
        {
            file2.write(reinterpret_cast<char*>(&album_list[i]->year),sizeof(int));
            int tmp = album_list[i]->artist.size();
            file2.write(reinterpret_cast<char*>(&tmp),sizeof(int)); //Write size of artist
            file2.write(album_list[i]->artist.c_str(),sizeof(char)*(album_list[i]->artist.size()));//Write artist

            tmp = album_list[i]->genre.size();
            file2.write(reinterpret_cast<char*>(&tmp),sizeof(int));//Write size of genre
            file2.write(album_list[i]->genre.c_str(),sizeof(char)*(album_list[i]->genre.size()));//Write genre

            tmp = album_list[i]->title.size();
            file2.write(reinterpret_cast<char*>(&tmp),sizeof(int));//Write sizeof title
            file2.write(album_list[i]->title.c_str(),sizeof(char)*(album_list[i]->title.size()));//Write title
        }
        file2.close();
        is_changed=false;//We have saved changes, so file is not changed now
    }


То все работает ну просто зашибись - и цикл крутится и файл создается. Я поэтому и не понимаю, чего плохого в том, что file у меня торчит в виде поля класса и если это нормально, то где с ним что в коде приключается smile


Rockie, А я так сделал только потому, что мне необходимо считывать пути с пробелами ... cin.getline() же требует char*. Ты предлагаешь объявить переменную char* path, затем в save_as() и open() добавить 
Код

path=new char[256];
cin.getline(path,256);

Так ? А что ты подрузомеваешь под обнулением?
Цитата
fstream file лучше создавать в методе, это обеспечит закрытие файла при выходе из метода(ИМХО).

Ну гарантирует, я не спорю, но просто я решил то, к чему я часто обращаюсь и что часто создаю вынести и сделать общим - сэкономил несколько строчек кода smile К тому же я явно закрываю файл - по идее все нормально и что за ошибка я вообще не понимаю. У меня сейчас просто принцип : "Понять, фиг ли так не работает?".

Автор: Anikmar 17.12.2006, 14:50
Цитата

Ну гарантирует, я не спорю, но просто я решил то, к чему я часто обращаюсь и что часто создаю вынести и сделать общим - сэкономил несколько строчек кода  К тому же я явно закрываю файл - по идее все нормально и что за ошибка я вообще не понимаю. У меня сейчас просто принцип : "Понять, фиг ли так не работает?". 


Мне кажется, что если file объявлен как поле класса он может неправильно проинициализироваться. Попробуй объявить его как указатель, а создавать и уничтожать в конструкторе и деструкторе.

Автор: nickless 17.12.2006, 15:52
У меня вот так нормально работает:
Код

#include "cmanager.h"

int main()
{
   Cmanager m;

   m.add_album(1999, "gen", "kdsaldksadad artist", "titttle");
   m.save_as();

   return 0;
}

Как ты используешь свой класс? Может там где-нибуть ошибка?

Насчет path, или используй string, или хотя бы добавь в конструктор path[0] = 0;, а то если случайно вызвать save() перед save_as(), то всё это дело запишется (если повезёт), куда попало.

Автор: Xenon 17.12.2006, 15:57
Anikmar, сделал, как ты сказал, но теперь еще веселее - исключительной ситуации не происходит, но файл размером 0 байт smile

Добавлено @ 16:00 
nickless, вот так.
Код

#include <iostream>
#include "manager.h"
#include <conio.h>

int main(int argc, char* argv[])
{
    Cmanager* manager=new Cmanager;
    char choice;
    do
    {
        cout << "Choose action: " << endl;
        cout << "'a' - add album\n'r' - remove album\n'i' - import albums from txt file\n'e' - export albums to txt\n'd' - display all albums\n's' - display album\n'q' - quit" << endl;
        choice=_getch();    
        system("cls");
        try
        {
            switch (choice)
            {
            case 'a':manager->add_album();
                break;
            case 'r':manager->del();
                break;
            case 'i':manager->import_txt();
                break;
            case 'd':manager->display_all();
                break;
//            case 's':manager->display();
//                break;
            case 'u':manager->sort();
                break;
            case 'e':manager->export_txt();
                break;
            case 'p':manager->save_as();
                break;
            case 's':manager->save();
                break;
            case 'o':manager->open();
                break;            
            case 'q':
                break;
            default:
                cout << "Incorrect input";
                break;
            }
        }
        catch(char* err_msg)
        {
            cout << "\nError: " << err_msg << "\n";
        }
    }while(choice!='q');
    if (manager->is_changed==true)
    {
        do
        {
            char choice;
            cout << "Yo have made changes to file. Do you want to save them? y/n ";
            cin >> choice;
            if (choice=='y' || choice=='Y')
            {
                manager->save();
                break;
            }
            if (choice=='n' || choice=='N')
            {
                break;
            }
        }while(true);
    }
    delete manager;
    _getch();
    return 0;
}


Некоторых пунктов меню не выводится, но это я просто пока не успел их закинуть, но из кода видно, чего нажимать надо smile 
Компилятор VC++ 2005

А как ты предлагаешь в string считывать поток без пробелов с возможностю корректирования, как в cin? Разве что:
Код

string path;
path.resize(256);
cin.getline(&path[0],256);

Но это же жуть ...

Автор: nickless 17.12.2006, 16:17
Странно, вроде ничего такого... а ты добавляешь хотябы один альбом? В коде этого нет. Попробуй еще просто добавить альбом и сразу сохранить, как в моём коде.

Цитата(Xenon @ 17.12.2006,  14:57)
А как ты предлагаешь в string считывать поток без пробелов с возможностю корректирования, как в cin?

В string есть такая глобальная функция:
Код
istream& getline( istream& is, string& s, char delimiter = '\n' );

Т.е. примерно так:
Код
#include <string>
...
string path;
getline(cin, path);


Автор: Xenon 17.12.2006, 16:29
nickless, о, клево, не знал smile Пасиба.
А я пробовал просто добавить альбом и сохранить - нифига. Пробовал заимпортить и сохранить - нифига. Добавить или заимпортить из txt и экспортировать в txt - тоже файл размером 0 байт.

Автор: Xenon 17.12.2006, 17:28
nickless, попробовал сделать как ты (я про main.cpp) - работает ...  smile Где я бажу? Не может быть проблемы в том, что у тебя в стеке manager создается, а у меня в куче?..

Автор: nickless 17.12.2006, 17:34
Может VC глючит? Попробуй чем-нибудь другим скомпилить, или file.flush() в деструкторе написать.
Проверь еще, какой статус у file.fail(), file.bad() и file.good().

edit
Может ты как-то умудряешся выйти  из main() не вызвав деструктора? 

Автор: Xenon 17.12.2006, 17:54
nickless, а как невызов деструктора вляиет на невозможность считать?
Еще интересен тот факт, что созданный файл размером 0 байт нельзя убить, пока программа работает - говорит занят файл.
Еще интересно - нажимаешь p (сохранить файл), мне сразу говорит, что путь неверен. Нажимаешь еще один раз - предлагает ввести.
Если импортируешь из txt альбомы, а не просто добавляешь, то при нажатии p все нормально - выкидывается "Enter path to file", но дальше тоже самое - couldn`t open file и файл с 0 байт.
Поменя char path[256] на string - никаких эмоций.

Автор: nickless 17.12.2006, 18:12
Цитата(Xenon @ 17.12.2006,  16:54)
а как невызов деструктора вляиет на невозможность считать?

Я просто подумал, может fstream не пишет в файл если деструктор не вызывался, но вообще после close() это не должно мешать...

Цитата(Xenon @ 17.12.2006,  16:54)
Еще интересен тот факт, что созданный файл размером 0 байт нельзя убить, пока программа работает - говорит занят файл.

А если нормально выключить программу, потом сделать рефреш в експлорере и потом еще раз посмотреть на размер файла?

Цитата(Xenon @ 17.12.2006,  16:54)
Еще интересно - нажимаешь p (сохранить файл), мне сразу говорит, что путь неверен. Нажимаешь еще один раз - предлагает ввести.

Посмотри что находится в path в первый раз

Автор: Xenon 17.12.2006, 18:32
nickless, не-а, 0 байт и хоть ты здохни.
Если просто добавлять альбомы, а затем сохранять, то как и положено - сначало пусто, а потом тот путь, что указан при вводе. При использовании локально созданного file все работает ... Ну пипец ... Крыша, не уезжай smile
Код

/////Save changes to file////////////////////////////////////////////////////////
    void save()
    {
        std::ofstream file2;
        file2.open(path.c_str(),std::ios::binary | std::ios::out);
        if(!file2)
        {
            throw ("Could not open path");
        }
        for (int i=0;i<static_cast<int>(album_list.size());i++)
        {
            cout << i;
            file2.write(reinterpret_cast<char*>(&album_list[i]->year),sizeof(int));
            int tmp = album_list[i]->artist.size();
            file2.write(reinterpret_cast<char*>(&tmp),sizeof(int));
            file2.write(album_list[i]->artist.c_str(),sizeof(char)*(album_list[i]->artist.size()));

            tmp = album_list[i]->genre.size();
            file2.write(reinterpret_cast<char*>(&tmp),sizeof(int));
            file2.write(album_list[i]->genre.c_str(),sizeof(char)*(album_list[i]->genre.size()));

            tmp = album_list[i]->title.size();
            file2.write(reinterpret_cast<char*>(&tmp),sizeof(int));
            file2.write(album_list[i]->title.c_str(),sizeof(char)*(album_list[i]->title.size()));
        }
        file2.close();
        is_changed=false;
    }

Автор: zkv 17.12.2006, 20:48
не знаю, насколько верно я понял проблему, но в вашей программе я увидел две проблемы:
1. когда пользуемся  getline() туда иногда лезет мусор, попробуйте так:      
Код

cout << "Enter path to file: ";
_flushall();//самое простое, что в голову приходит
cin.getline(path,256);

2. как вам уже говорили     fstream file; у вас член класса, когда прога натыкается на eof() устанавливаются "плохие" биты состояния (или биты плохого состояния smile ), и никто это не исправляет, вот при следующей проверке вам и говорит прога, что все плохо. Попробуйте их сбрасывать после проверки:
Код

if(file.eof()) 
{
  file.clear();
  break;        
}    

но это так, первое, что под руку попало, а конкретного вопроса я так и не разглядел, простите...
PS вы про пошаговое выполнение программы не слышали?

Автор: Xenon 17.12.2006, 21:05
zkv, _flushall(); все исправил! Спасибо!
Но какая связь между fstream file объявленным как поле и как локальная переменная и буфером? 

Автор: zkv 17.12.2006, 21:15
Цитата(Xenon @  17.12.2006,  21:05 Найти цитируемый пост)
Но какая связь между fstream file объявленным как поле и как локальная переменная и буфером?  


Xenon, простите, речь шла о битах состояния потока. Дело в том, что если бы вы каждый раз создавали поток заново (локальная переменная), то не пришлось бы заботиться о битах состояния. А так биты устанавливаются в "плохие", и объект продолжает с ними жить, вплоть до следующего к нему обращения.  

Автор: Xenon 17.12.2006, 21:24
zkv, э-э-э, не понял ... Я могу создать альбомы не путем открывания файла, а просто добавлением (fstream никак тут не участвует). То есть  биты состояния никак не изменяются до того, как я вызываю save_as() (если я обращаюсь даже к переменной file, то как они изменятся)? В любом случае ошибка таже самая
Помог именно flushall(), а не сброс битов. Или это и есть есть сброс битов? Если да, то где в программе биты ошибок изменились, если я не открывал файла?

Автор: zkv 17.12.2006, 21:29
попробуй запустить свой пример (уже с учетом _flushall(), но без file.clear() ), экпортируй альбомы в текстовый файл, а потом импортируй из него альбомы в первый раз все ок будет, а во второй получишь по рукам smile
повторюсь, я не понял конкретно вопроса, по этому просто запустил твою прогу и посмотрел, что там не так работает, возможно ты еще не натолкнулся на эту ошибку

Автор: Xenon 17.12.2006, 22:07
zkv, импортируется нормально, а на экспорте уже по рукам дадут. Я говорю - все, что связано С ЗАПИСЬЮ не функционировало как надо

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)