Любая причина, по которой объект std::ofstream не закрывается должным образом?

Я заметил в своем коде C++, что каждый раз, когда я закрываю объект std::ofstream, я не могу снова открыть файл, который я закрыл с помощью std::ifstream. Функция open std::ifstream всегда будет давать сбой.

Есть ли что-то «дополнительное», которое я могу сделать, чтобы мой объект std::ofstream закрывался должным образом?

Кто-то, вероятно, попросит показать мой конкретный код, поэтому, чтобы этот пост был небольшим, я вставил его здесь. В моем коде после выполнения случаев a или d все вызовы std::ifstream open терпят неудачу. (Перед публикацией этого вопроса у меня было несколько человек, игравших с моим кодом, которые не смогли сделать ничего, кроме того, что std::ofstream закрыть не удалось по неизвестным причинам)

Заранее благодарим за любые полученные ответы.

Код

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

typedef struct Entry
{
   string Name;
   string Address;
   string Phone;   
};

int main()
{
   bool exit = false, found = false;
   char input, ch;
   string stringinput, stringoutput;
   ifstream fin, ibuffer;
   ofstream fout, obuffer;
   Entry buffer;

   while(!exit)
   {
      cout << "Welcome to the Address Book Application!" << endl << endl;
      cout << "\tSelect an option:" << endl << endl;
      cout << "\tA -- Add New Entry" << endl;
      cout << "\tD -- Delete an Entry" << endl;
      cout << "\tS -- Search for an Existing Entry" << endl;
      cout << "\tE -- Exit" << endl << endl;

      cin >> input;

      switch(input)
      {
         case 'a':
         case 'A':
         cin.ignore(255,'\n');//Apparently necessary because an extra new line carrys over in the cin stream
         system("cls");
         //Get Information from User
         cout << "Enter Phone Number: ";
         getline(cin, buffer.Phone);
         cout << endl << "Enter Name: ";
         getline(cin, buffer.Name);
         cout << endl << "Enter Address: ";
         getline(cin, buffer.Address);
         /*Copy existing database into a buffer. In other words, back it up*/
         fin.open("address.txt");
         if(!fin)
         {
            fin.close();
            fout.open("address.txt");
            fout << buffer.Phone << endl << buffer.Name << endl << buffer.Address << endl;
         }
         if(fin)
         {
            obuffer.open("buffer.txt");
            while(fin && fin.get(ch))
               obuffer.put(ch);
            fin.close();
            obuffer.close();
            /*Copy buffer to new database file*/
            ibuffer.open("buffer.txt");
            fout.open("address.txt");//This removes all of the previously existing info from database.txt
            while(ibuffer && ibuffer.get(ch))
               fout.put(ch);
            ibuffer.close();
            remove("buffer.txt");//Delete the buffer
            fout << endl << buffer.Phone << endl << buffer.Name << endl << buffer.Address << endl;
         }

         buffer.Phone.erase();
         buffer.Name.erase();
         buffer.Address.erase();
         fout.close();
         break;

         case 'd':
         case 'D':
         cin.ignore(255,'\n');//Apparently necessary because an extra new line carrys over in the cin stream
         system("cls");
         cout << "Enter the phone number of the entry to delete: ";
         cin >> stringinput;
         fin.open("address.txt");
         if(!fin)
         {
            cout << endl << "No entries exist!";
            fin.close();
            break;
         }
         obuffer.open("buffer.txt");
         /* Copy everything over into the buffer besides the account we wish to delete */
         while(!fin.eof())
         {

            fin.read(&ch, sizeof(char));

            if(ch != '\n' && ch != '\0')
            stringoutput += ch;

            if(ch == '\n' || ch == '\0')
            {
               if(stringinput.compare(stringoutput))
               {
                  stringoutput += ch;
                  obuffer << stringoutput;
                  stringoutput.erase();
               }

               if(!stringinput.compare(stringoutput))
               {
                  stringoutput += ch;
                  getline(fin, stringoutput);
                  getline(fin, stringoutput);
                  fin.read(&ch, sizeof(char));//Get rid of the extra '\n'
                  stringoutput.erase();
               }

            }

         }

         //Hack: Copy the last line over since the loop for some reason doesn't
         obuffer << stringoutput;
         stringoutput.erase();

         fin.close();
         obuffer.close();

         fout.open("address.txt");
         ibuffer.open("buffer.txt");

         while(ibuffer && ibuffer.get(ch))
            fout.put(ch);

         ibuffer.close();
         fout.close();
         remove("buffer.txt");

         cout << endl << "Entry " << stringinput << " deleted successfully!" << endl;
         stringinput.erase();
         stringoutput.erase();
         break;

         case 's':
         case 'S':
         cin.ignore(255,'\n');//Apparently necessary because an extra new line carrys over in the cin stream
         system("cls");

         found = false;

         fin.open("address.txt");
         if(!fin)
         {
            cout << "No entries currently exist!" << endl << endl;
            fin.close();
            break;
         }

         cout << "Enter the phone number to search for: ";
         cin >> stringinput;

         while(!fin.eof())
         {
            fin.read(&ch, sizeof(char));

            if(ch != '\n' && ch != '\0')
               stringoutput += ch;

            if(ch == '\n' || ch == '\0')
            {
               if(!stringinput.compare(stringoutput))
               {
                  found = true;
                  break;
               }

               stringoutput.erase();
            }

         }

         if(found)
         {
            cout << "Phone Number: " << stringinput << endl;
            getline(fin, stringoutput);
            cout << "Name: " << stringoutput << endl;
            getline(fin, stringoutput);
            cout << "Address: " << stringoutput << endl << endl;
         }

         if(!found)
         {
            stringoutput.erase();
            cout << endl << stringinput << " is not in the address book!" << endl << endl;
         }

         stringinput.erase();
         stringoutput.erase();
         fin.close();
         break;

         case 'e':
         case 'E':
         exit = true;
         break;

         default:
         system("cls");
         cout << input << " is not a valid option." << endl << endl;
         break;
      }

   }

   return 0;

}

person Rob S.    schedule 30.10.2010    source источник
comment
Было бы полезно узнать, какие объекты потока являются причиной вашего горя и в какой строке кода вы столкнулись с ошибкой.   -  person Marcelo Cantos    schedule 30.10.2010
comment
В моем коде, особенно после fout.close(), я не могу fin.open()   -  person Rob S.    schedule 30.10.2010
comment
Нам по-прежнему нравится оставлять код в вопросе, чтобы сообщение SO было более автономным и полезным, когда эта вставка неизбежно умирает. Пожалуйста, сократите до меньшего тестового примера и включите в вопрос.   -  person    schedule 30.10.2010
comment
Я должен отметить, что объявление всех ваших переменных вверху не является идиоматичным С++ и может быть причиной а) самой ошибки и б) трудностей, с которыми вы и ваши коллеги сталкиваетесь при диагностике ошибки. В C++ очень важно свести к минимуму область действия объектов стека. Кроме того, по возможности инициализируйте как часть построения вместо инициализации по умолчанию, за которой следует open().   -  person Marcelo Cantos    schedule 30.10.2010
comment
Прочтите об RAII. Внимательно прочитайте, убедитесь, что вы понимаете, а затем соответствующим образом измените свой код. Затем посмотрите, сохраняется ли ошибка.   -  person Björn Pollex    schedule 30.10.2010
comment
В коде есть две точки, где вы вызываете fout.close(), и за обеими следует fin.open().   -  person Marcelo Cantos    schedule 30.10.2010
comment
Еще один совет: не копируйте файлы с множеством вызовов get() и put(). Используйте функции read() и write() для более компактного ввода-вывода. Кроме того, поскольку вы копируете повсюду, реорганизуйте его в функцию.   -  person Marcelo Cantos    schedule 30.10.2010
comment
Я с Роджером, Марсело и Space_C0wb0y. Обратите внимание на стилистические подсказки, которые вы получили, и сведите задачу к 20 строкам, воспроизводящим ошибку. Тогда вернись с этим.   -  person sbi    schedule 30.10.2010
comment
@Roger Pate до сих пор мне не удалось воспроизвести это за несколько небольших попыток, которые я предпринял. Марсело Кантос, отличное предложение, я над этим поработаю. Space_C0wb0y спасибо, если предыдущие предложения не помогут, я попробую.   -  person Rob S.    schedule 30.10.2010
comment
Вы можете подумать о рефакторинге основного метода ~ 200 строк в более мелкие функции. Я часто обнаруживаю, что это облегчает выявление описанного вами поведения.   -  person Sam Miller    schedule 30.10.2010
comment
@Rob: Я бы предпочел увидеть 300 строк в вопросе, чем вставить ссылку. Конечно, я бы предпочел 50, но когда не получается... ;)   -  person    schedule 30.10.2010
comment
Я не думаю, что ваш код слишком велик для этого сайта, я вставил его выше.   -  person Steve Townsend    schedule 30.10.2010
comment
Спасибо, ребята, используя ваши предложения, мне удалось заставить его работать. Рабочий код находится здесь: pastie.org/private/3fs09qqj8toes3rzsq. Я планирую продолжить реализацию оставшихся предложений по сделать мой код максимально эффективным и чистым. Благодаря тонну!   -  person Rob S.    schedule 30.10.2010


Ответы (1)


Лучший способ убедиться, что ваши потоки сбрасываются на каждом этапе этой обработки, — это не закрывать их явно, а объявлять их в точке использования и позволять им выходить за рамки. Это точка RAII, которая была сделана в комментариях. В вашем случае это означает перемещение объявления из верхней части функции внутрь каждого рычага переключателя, где они используются. Таким образом, вы можете быть уверены, что каждый файл будет корректно закрыт при выходе из определенного case, так как ofstream и ifstream закроют файл во время уничтожения при выходе из области видимости.

Еще я заметил, что вы тестируете странную вещь после открытия файла:

 fin.open("address.txt");
 if(!fin)

Это тесты на плохой поток, но я не думаю, что он завершен — если вы тестируете успешное открытие, вы также должны проверить

 fin.open("address.txt");
 if (!fin.is_open())

Все ваши циклы обработки файлов и вызовы open() должны проверять наличие fin.fail() или fout.fail(), а также fin.eof(), чтобы исключить ошибку доступа к файлу как необработанное условие, которое сбивает с толку наблюдаемое вами поведение.

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

person Steve Townsend    schedule 30.10.2010
comment
Спасибо, ваши советы действительно помогают. - person Rob S.; 30.10.2010