【问题标题】:Text doesn't save into the file文本不保存到文件中
【发布时间】:2017-01-24 21:29:08
【问题描述】:

你好,我做了一个程序,在我的程序中我有一个类 Customer。 为了将客户保存在计算机上,我创建了一个文件并用 ::like name::password::phonenbr 分隔客户的每个数据。但是我的问题是,如果我在代码的注释中写入行,数据将保存到文件中,但是如果我在 if() 中写入同一行来检查文件是否为空,则不会做任何事情,尽管我在编译器中看到这一行没有问题。

如果你能帮助我,那将是优雅的!

void Shop::Add_Customer()
{
    fstream myfile; myfile.open("CustomerFile.txt");
    string name, password, phonenbr;
    string buffer, delimitor = "::";

    system("cls");
    cout << "Name of the customer: "; cin >> name;
    cout << "Password of the customer: "; cin >> password;
    cout << "Phone number of the customer: "; cin >> phonenbr;

    if (!myfile.is_open())
    {
        myfile.open("CustomerFile.txt", ios::out);
    }
    //myfile << name + delimitor + password + delimitor + phonenbr << endl;

    if (myfile.peek() == std::ifstream::traits_type::eof())
    {
        myfile << name + delimitor + password + delimitor + phonenbr << endl;
    }
    else
    {
        while (getline(myfile, buffer))
        {
            if (CheckIfCustomerExist(buffer, name, phonenbr) == true)
            {
                cout << "Customer already exist" << endl;
            }
            else
            {
                myfile << name + delimitor + password + delimitor + phonenbr << endl;
                cout << "Customer insert in the file " << endl;
            }
        }
    }


}

【问题讨论】:

  • 离题:您会发现编写解析器以使用单个字符分隔符读回文件要容易得多。

标签: c++ file stream


【解决方案1】:

流中的 EOF 标志在任何读取流失败时设置,因为它试图读取超过流的末尾。设置 EOF 后,流处于错误状态,在清除 EOF 标志之前无法读取或写入。

这是一个非常简单的例子:

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    fstream myfile("CustomerFile.txt", ios::out);
    if (!myfile.is_open())
    {
        cout << "file not open." << endl;
    }
    else
    {
        if (myfile.peek() == std::ifstream::traits_type::eof())
        {
            if (myfile.eof())
            {
                cout << "Need to clear the EOF flag." << endl;
            }
        }
    }
}

查看 EOF 设置 EOF 标志,将流置于错误状态并使其不可写。由于我们要扩展文件,我们需要使用the aptly named clear method. 清除该标志

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    fstream myfile("CustomerFile.txt", ios::out);
    if (!myfile.is_open())
    {
        cout << "file not open." << endl;
    }
    else
    {
        if (myfile.peek() == std::ifstream::traits_type::eof())
        {
            if (myfile.eof())
            {
                cout << "Need to clear the EOF flag." << endl;
            }
            myfile.clear();
            if (!myfile.eof())
            {
                cout << "OK. EOF clear now." << endl;
            }
        }
    }
}

题外话:

以下代码

   while (getline(myfile, buffer))
    {
        if (CheckIfCustomerExist(buffer, name, phonenbr) == true)
        {
            cout << "Customer already exist" << endl;
        }
        else
        {
            myfile << name + delimitor + password + delimitor + phonenbr << endl;
            cout << "Customer insert in the file " << endl;
        }
    }

将针对文件中的每一行重复,可能会逐一检查输入客户与文件中的每个客户。每次输入的客户不匹配时,都会将输入的客户添加到文件中。这意味着输入客户可能会多次添加到文件中。更糟糕的是,程序同时读取和写入同一个文件,最终可能会损坏文件。

最好先阅读和比较,然后如果没有找到匹配项,则前进到文件末尾并添加输入客户。

另外,文件打开逻辑也很复杂,可能还是会失败

fstream myfile; myfile.open("CustomerFile.txt");

if (!myfile.is_open())
{
    myfile.open("CustomerFile.txt", ios::out);
}

如果文件不存在,第一次调用 open 肯定会失败,从而强制第二次调用 open。不妨将ios::out添加到此调用并完成它。第二次调用top open可能会因为其他原因失败,没有测试成功,所以我推荐

fstream myfile("CustomerFile.txt", ios::out);
if (!myfile.is_open())
{
    perror("file not open: ");
}
else
{
    // your code goes here
}

Documentation for perror

【讨论】:

    【解决方案2】:

    问题的根源在于 if 语句的条件:

        (myfile.peek() == std::ifstream::traits_type::eof())
    

    显然,您的文件在以下行中以 fstream 模式打开:

        fstream myfile; myfile.open("CustomerFile.txt");
    

    现在我能得到的唯一原因是为什么你的 if 语句的条件不满足是因为文件模式不同。我不确定我是否正确(欢迎在 cmets 框中提供反馈),但这是我可以提出的原因。

    我尝试了一种我自己的方法,它总是有效的,它也适用于您的代码。我在您的代码中替换了以下几行:

        if (myfile.peek() == std::ifstream::traits_type::eof())
        {
            myfile << name + delimitor + password + delimitor + phonenbr << endl;
        }
    

    这些行:

        myfile.seekg (0, ios::end);
        int length = myfile.tellg();
    
        if (length == 0)
        {
            myfile << name + delimitor + password + delimitor + phonenbr << endl;
        }
    

    第一行myfile.seekg (0, ios::end); 获取括号中指定的2 个点之间的距离。 0 和 ios::end 是不言自明的; 0 是文件的开头, ios::end 是文件的结尾。

    第二行int length = myfile.tellg(); 将上一行中查找的值存储在一个名为length 的int 变量中。长度是“光标”从该文件的开头到结尾必须移动的字符数(尝试将光标想象为类似于 Microsoft Word 中您所在单词前面的闪烁的东西键入,除了这里,您看不到文本文件中的光标从头到尾移动)。

    这个 if 条件非常简单。如果长度为零,这意味着光标必须移动 0 点才能从文件的开头到文件的结尾,然后将您想要的任何内容写入该文件。这种技术有效(至少对我有效)。

    附带说明一下,您的代码还有其他几个方面可以改进。例如,为什么要添加这个 if 语句:

    if (!myfile.is_open())
    {
        myfile.open("CustomerFile.txt", ios::out);
    }
    

    此代码是您代码中这些行的重复:

    fstream myfile; myfile.open("CustomerFile.txt");
    

    .open() 命令已经实现了我指出的 if 语句。如果找到 open() 中指定的文件,则打开该文件;否则它将继续创建该新文件。因此,该 if 语句是多余的,应该删除,因为它消耗不必要的 CPU 功率并减慢您的程序速度(不是很多,但您很快就会意识到运行代码的每一毫秒都很重要;效率是关键)。我建议您删除该 if 语句。

    另一个问题是您接受输入的 3 个变量。鉴于它们是字符串,为什么要使用 cin >> 方法?使用 cin 只会占用句子中的第一个单词;在您的以下行中:

        cout << "Name of the customer: "; cin >> name;
    

    如果您输入 John Doe,它只会将 John 保存到 name 变量中,并且会将“Doe”移动到下一个输入变量,在您的情况下是密码。如果没有其他cin,那么它会忽略空格后面的单词。因此,对所有输入点使用以下行:

        getline(cin, name);
    

    此函数将获取所有单词和空格作为一个句子,直到您按 Enter 键为止,不像 cin 只会获取第一个单词并忽略句子的其余部分。

    最后,您的电话号码应该是 int 类型。我会根据您的要求将其留给您解决。

    希望我回答了您的问题,并希望我的提示对您有所帮助。祝你好运!


    编辑:关于您的代码,我错过的另一点是您的 while 循环对每一行都运行。这意味着它将在文件的每一行检查特定客户的姓名。这不是你想要的。您想读取文件中的每一行,但如果您找到客户,那么您想终止该功能而不继续下一行。此外,您只想在读取整个文件后打印一条错误语句,而不仅仅是一行。

    else
    {
        int check = 0;
        while (getline(myfile, buffer))
        {
            if (CheckIfCustomerExist(buffer, name, phonenbr) == true)
            {
                cout << "Customer already exist" << endl;
                check = 1;
                break;
            }
        }
        if (check == 0)
        {
            myfile << name + delimitor + password + delimitor + phonenbr << endl;
            cout << "Customer insert in the file " << endl;
        }
    }
    

    这段代码的作用是遍历每一行,检查该行中的客户。如果该行有客户记录,则它将 int 类型的检查值从 0 设置为 1,并且 break 语句终止 while 循环。读取整个文件后,它会继续执行 if 语句。在该语句中,如果check变量仍然为0,则表示该文件没有客户,在该文件中将新的记录添加到文件中。

    另外,我说过 phone_number 应该是一个 int 值。不过,我从其他 StackOverflow 用户那里收回了这一点,电话号码更适合作为字符串值,因为它的格式可能无法正确存储为 int 值(例如,0059875 将存储为 59875)。

    【讨论】:

    • 第二次调用open 涵盖了第一次调用open 未涵盖的“文件不存在”故障情况。如果文件不存在,添加的ios::out 将强制创建文件。所以这是重复的,但第二次调用是正确的。
    • 客户名称的要点。不同意电话号码是int。它可能应该保留一个字符串并测试正确的位数,并且所有输入字符都是数字。例如,int 不会处理前面的 0。
    • 其实@user4581301确实是这样,我没有考虑电话号码的这方面。
    猜你喜欢
    • 2012-09-27
    • 1970-01-01
    • 2014-10-03
    • 2021-12-13
    • 2016-10-28
    • 1970-01-01
    • 1970-01-01
    • 2021-08-18
    • 1970-01-01
    相关资源
    最近更新 更多