【问题标题】:How to read a birthday with symbols "/" and ":" from a .txt file?如何从 .t​​xt 文件中读取带有符号“/”和“:”的生日?
【发布时间】:2011-07-23 18:01:58
【问题描述】:

我正在编写一个 c++ prgram,并且必须从 .txt 文件中读取一些数据。格式如下所示: "name gender DD/MM/YYYY HH:MM:SS"

我知道如何读取.txt 文件并逐个输入单词,但我不知道如何处理符号“/”或“:”。
我必须比较两个人的年龄。

请帮忙!

【问题讨论】:

  • 你写这个的数据结构是什么?这听起来像是家庭作业。
  • 对我们所有人来说,重要的是您查看答案并接受能够解决您问题的答案。
  • re albert: 是的,这是一所硬件学校,这学期我参加了 C++ 编程课程,但我现在真的很后悔>.
  • re karlphillip: 抱歉回复晚了,我知道我应该接受解决我的问题的答案之一,但我仍在尝试代码......因为这是一个编程作业,我没有输入整个问题,所以我需要先自己尝试代码。另外,ans中涉及的一些概念不是教授教的,得先搞懂
  • thz 对于提供的每个答案,我会尽快做出决定。 tks!

标签: c++ file symbols text-files


【解决方案1】:

这是使用 sscanf 之类的东西真正容易得多的任务之一:

sscanf(input_string, 
       "%s %s %d/%d/%d %d:%d:%d", 
       name, gender, &day, &month, &year, &hour, &minute, &second);

您可以使用 iostream,但没有那么容易。你有几个基本的选择。一种是使用 locale 将 ':' 和 '/' 定义为空格,这样您就可以输入值并忽略分隔符(但不会警告您某些类型的格式错误,例如用 '/' 代替 ':' 或反之亦然。

另一种可能性是显式读取这些字符,并(如果您选择)通过将每个分隔符与您期望的进行比较来验证它们。如果您真的很想确保数据采用预期的格式,那么这可能就是您要走的路。

【讨论】:

  • 我很惊讶没有用于此的 Boost 库。 Boost.Format 有利地替换了printf family,但似乎没有scanf family 的等价物。
  • 我还没有学过“sscanf”或“locale”,所以我不知道如何应用你所说的,但似乎“locale”就是我想要的。你可以进一步解释一下吗?谢谢!
【解决方案2】:

查看以下链接。它会解决你的问题。

【讨论】:

    【解决方案3】:

    在 C 中,您可以使用 sscanf() 或更直接地使用 fscanf(),但这些函数在 C++ 中被认为是糟糕的风格。

    我建议这样:

    struct SData 
    {
        std::string sName;
        std::string sGender;
        int nDay;
        int nMon;
        int nYear;
        int nHour;
        int nMin;
        int nSec;
    };
    
    bool ReadRecord(std::istream &istr, SData &data)
    {
        istr >> data.sName
             >> data.sGender
             >> data.nDay;
        istr.ignore(1);       // '/'
        istr >> data.nMon;
        istr.ignore(1);       // '/'
        istr >> data.nYear
             >> data.nHour;
        istr.ignore(1);       // ':'
        istr >> data.nMin;
        istr.ignore(1);       // ':'
        istr >> data.nSec;
    
        return istr.good();
    }
    

    更新

    以下示例通过排除结构来简化使用

    #include <iostream>
    #include <fstream>
    #include <string>
    
    bool ReadRecord(std::istream &istr, 
                    std::string &sName,
                    std::string &sGender,
                    int &nDay,
                    int &nMon,
                    int &nYear,
                    int &nHour,
                    int &nMin,
                    int &nSec)
    {
        istr >> sName
             >> sGender
             >> nDay;
        istr.ignore(1); // '/'
        istr >> nMon;
        istr.ignore(1); // '/'
        istr >> nYear
             >> nHour;
        istr.ignore(1); // ':'
        istr >> nMin;
        istr.ignore(1); // ':'
        istr >> nSec;
    
        return istr.good();
    }
    
    int main()
    {
        std::string sName0, sGender0, sName1, sGender1;
        int nDay0, nMon0, nYear0, nDay1, nMon1, nYear1;
        int nHour0, nMin0, nSec0, nHour1, nMin1, nSec1;
        const char szFileName[] = "MyData.txt";
    
        std::ifstream istr(szFileName);
        if (!istr.is_open())
        {
            std::cerr << "Cannot open file\n";
            return 1;
        }
        if (!ReadRecord(istr, sName0, sGender0, nDay0, nMon0, nYear0, nHour0, nMin0, nSec0))
        {
            std::cerr << "Cannot read file\n";
            return 1;
        }
        if (!ReadRecord(istr, sName1, sGender1, nDay1, nMon1, nYear1, nHour1, nMin1, nSec1))
        {
            std::cerr << "Cannot read file\n";
            return 1;
        }
        std::string sYounger;
    
        if (nYear0 == nYear1)
        {
            if (nMon0 == nMon1)
            {
                if (nDay0 == nDay1)
                {
                    if (nHour0 == nHour1)
                    {
                        if (nMin0 == nMin1)
                        {
                            if (nSec0 > nSec1)
                            {
                                sYounger = sName0;
                            }
                            else if (nSec0 < nSec1)
                            {
                                sYounger = sName1;
                            }
                        } 
                        else if (nMin0 > nMin1)
                        {
                            sYounger = sName0;
                        }
                        else if (nMin0 < nMin1)
                        {
                            sYounger = sName1;
                        }
                    }
                    else if (nHour0 > nHour1)
                    {
                        sYounger = sName0;
                    }
                    else if (nHour0 < nHour1)
                    {
                        sYounger = sName1;
                    }
                }
                else if (nDay0 > nDay1)
                {
                    sYounger = sName0;
                }
                else if (nDay0 < nDay1)
                {
                    sYounger = sName1;
                }
            }
            else if (nMon0 > nMon1)
            {
                sYounger = sName0;
            }
            else if (nMon0 < nMon1)
            {
                sYounger = sName1;
            }
        }
        else if (nYear0 > nYear1)
        {
            sYounger = sName0;
        }
        else if (nYear0 < nYear1)
        {
            sYounger = sName1;
        }
    
        if (sYounger.empty())
        {
            std::cout << "The ages are the same\n";
        }
        else
        {
            std::cout << sYounger << "is younger\n";
        }
    
        return 0;
    }
    

    【讨论】:

    • 您不想检查被忽略的字符是否与/ 或: 匹配?
    • 我不想成为模仿者,所以我不能写出我不太了解的东西。请问“struct SData”函数的目的是什么?如果我写这两个函数,我怎么能输入数据?
    • @Matthieu - 可能。我只是想为新手保持简单。
    • @user678593 - 该结构只是保存传递给函数的 9 个参数。该函数适用于任何流。要调用它,您可以使用:“bool bOK = ReadRecord(std::cin, mydata);”。如果你想从数据文件中读取它,你可以在数据文件上打开一个流并传递它。如果需要,通过将其放入一个函数中,您可以为多条记录重复调用它。
    • re michael:我认为你的代码更清晰,风格也很好,但是像 struct、istr 这样的未学概念对我来说太难了。我可能会采纳 Xeo 的建议。但再次感谢您的帮助:)!
    【解决方案4】:

    除了sscanf 最简单的方法,如Jerry describes,也可以使用 iostreams。只需从文件中读取您想要的数据,直到您点击分隔符,然后将其丢弃到虚拟 char:

    char dummy;
    myfile >> first_name >> last_name >> day >> dummy >> month >> dummy ... // and so on.
    

    【讨论】:

    • 下面是我程序中唯一的函数: void main() { char name, gender; int 日期、月、年、时、分、秒;字符假人; ifstream inFile; inFile.open("c:\\input.txt"); inFile>>名称>>性别>>日期>>虚拟>>月>>虚拟>>年>>小时>>虚拟>>分钟>>虚拟>>秒; cout
    • 抱歉上面的评论,我输入了我的程序以及输入文件的内容,但我不知道如何将它们分开>.
    • @user:您需要自己在输出中输入空格。 cout &lt;&lt; name &lt;&lt; " " &lt;&lt; gender &lt;&lt; " " &lt;&lt; ... 等等。另外,使用int main()、void main() 不是标准的。你可以复制确切的输出吗?
    • 添加空格后,输出如下:??-858993460-85899346-85899346 -85899346-85899346-85899346
    • @user:我做了一个快速测试,完全使用上面的代码和输入文件,得到了正确和预期的输出。问题必须出在其他地方。您能否将 main.cpp 的内容复制到 Ideone(在左侧选择 C++)?
    【解决方案5】:

    受到How to split a string?的启发,我决定试一试。

    鉴于以下数据:

    文件:dates.txt

    james male 01/02/1987 04:01:02
    jerry male 11/06/1965 08:03:04
    jon male 21/10/1977 12:05:06
    

    以下应用程序解析文件的每一行并将信息存储在名为 dude 的临时对象中,然后将其添加到 people_list 中。日期/时间的解析是通过strtok 完成的,这是将字符串拆分为标记的好功能。最后,我们对向量进行迭代,打印找到的内容。

    #include <iostream>
    #include <string>
    #include <fstream>
    #include <iterator>
    #include <sstream>
    #include <vector>
    #include <algorithm>
    #include <string.h>
    
    using namespace std;
    
    typedef struct _time
    {
        int h; // hour
        int m; // minute
        int s; // seconds
    } Time;
    
    typedef struct _date
    {
        int y; // year
        int m; // month
        int d; // day
    } Date;
    
    class Person
    {
    public:
        string name;
        string gender;
        Time time;
        Date date;
    };
    
    
    int main()
    {
        ifstream in("dates.txt");
    
        vector<Person> people_list;
    
        string line;
        while (getline(in,line))
        {
            if (line.empty())
            {
                continue;
            }
    
            vector<string> tmp;
            istringstream iss(line);
    
            copy(istream_iterator<string>(iss), istream_iterator<string>(), back_inserter<vector<string> >(tmp));
    
            // Parsing name and gender (easy)
            Person dude;
            dude.name = tmp[0];
            dude.gender = tmp[1];
    
            // Date
            char delim_slash[] = "/";
            char* result = NULL;
            result = strtok(const_cast<char*>(tmp[2].c_str()), delim_slash);
            dude.date.d = atoi(result);
            result = strtok(NULL, delim_slash);
            dude.date.m = atoi(result);
            result = strtok(NULL, delim_slash);
            dude.date.y = atoi(result);
    
            // Time
            char delim_colon[] = ":";
            result = strtok(const_cast<char*>(tmp[3].c_str()), delim_colon);
            dude.time.h = atoi(result);
            result = strtok(NULL, delim_colon);
            dude.time.m = atoi(result);
            result = strtok(NULL, delim_colon);
            dude.time.s = atoi(result);
    
            people_list.push_back(dude);
        }
    
        for (int i = 0; i < people_list.size(); i++)
        {
            cout << "Name: " << people_list[i].name << endl;
            cout << "Gender: " << people_list[i].gender << endl;
            cout << "Date: " << people_list[i].date.d << "/" <<
                                people_list[i].date.m << "/" <<
                                people_list[i].date.y << endl;
    
            cout << "Time: " << people_list[i].time.h << ":" <<
                                people_list[i].time.m << ":" <<
                                people_list[i].time.s << endl << endl;
        }
    }
    

    输出:

    Name: james
    Gender: male
    Date: 1/2/1987
    Time: 4:1:2
    
    Name: jerry
    Gender: male
    Date: 11/6/1965
    Time: 8:3:4
    
    Name: jon
    Gender: male
    Date: 21/10/1977
    Time: 12:5:6
    

    【讨论】:

    • 我的眼睛受伤了。从来没有见过这样的C和C++混合,const_cast+strtok真的很丑!
    • 代码有效!但我认为它不像我以前见过的这么多库和术语那样适合我,但感谢你的工作!
    猜你喜欢
    • 2014-03-17
    • 1970-01-01
    • 2016-03-04
    • 1970-01-01
    • 1970-01-01
    • 2022-10-07
    • 2016-05-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多