2

C++ コードで、std::ofstreamオブジェクトを閉じるたびに、で閉じたファイルを再度開くことができないことに気付きましたstd::ifstreamstd::ifstreamopen関数は常に失敗します。

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;

}
4

1 に答える 1

3

この処理の各ポイントでストリームが確実にリセットされるようにする最善の方法は、ストリームを明示的に閉じるのではなく、使用時にストリームを宣言し、スコープから外れるようにすることです。これは、コメントで作成された RAII ポイントです。あなたの場合、これは宣言を関数の上部から、それらが使用されているスイッチの各アームの内側に移動することを意味します。caseこのようにして、スコープ終了時の破棄中に ofstream と ifstream がファイルを閉じるため、特定の を終了するときに各ファイルがきれいに閉じられていることを確認できます。

私が気付いたもう1つのことは、ファイルを開いた後に奇妙なことをテストしていることです:

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

これは悪いストリームをテストしますが、完全ではないと思います - 正常に開くかどうかをテストしている場合は、次もテストする必要があります

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

すべてのファイル処理ループと呼び出しは、またはをopen()チェックして、観察された動作を混乱させる未処理の状態としてファイル アクセス エラーを除外する必要があります。fin.fail()fout.fail()fin.eof()

この時点で、これらの明らかな誤解を修正し、うまくいかない場合はより具体的な質問に戻ることをお勧めします。

于 2010-10-30T14:17:34.577 に答える