Любая причина, почему объект std :: ofstream не закрывается должным образом? - PullRequest
2 голосов
/ 30 октября 2010

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

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

Кто-то, вероятно, собираетсяпопросите показать мой конкретный код, поэтому для того, чтобы этот пост был небольшим, я вставил его здесь .В моем коде после выполнения через a или d все std::ifstream открытые вызовы завершаются неудачно.(До публикации этого вопроса у меня было несколько человек, играющих с моим кодом, которые не смогли сделать ничего, кроме того, что 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;

}

1 Ответ

3 голосов
/ 30 октября 2010

Лучший способ гарантировать, что ваши потоки сбрасываются в каждой точке этой обработки, - это не явно закрывать их, а объявлять их в точке использования и позволять им выходить за рамки. Это пункт 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(), чтобы исключить ошибку доступа к файлу как необработанное условие, которое запутывает ваше наблюдаемое поведение.

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

...