【问题标题】:Reading a date from a file从文件中读取日期
【发布时间】:2021-11-27 23:30:14
【问题描述】:

我将输入2021/10/7(年/月/日)等输入。我不会要求用户输入,我需要代码才能正确读取输入。我不明白如何从 Linux 获取输入(日期)以及我的程序如何将该输入横向传输到我的代码中的适当位置。

我们已经简要介绍了<fstream>ifstream inData; ofstream outData; .open.close,但是对于我在发布的示例解决方案中的最后一个任务freopen 使用了我完全不熟悉的。我特别向我的教授寻求帮助,但鼓励使用外部手段,例如计算科学不和谐和辅导,但没有太大帮助。

我正在寻找一种方法来学习如何输入数据以便可以从数据流中读取数据,以及我应该如何构建这样的东西来从 Linux 而不是用户获取输入。

编写一个从标准输入流中读取日期的程序。

如果日期有效,程序应写下工作日编号,后跟英文工作日名称(例如,“Monday”、“Tuesday”、“Wednesday”、...、“Sunday”)。函数 main 返回 0。

如果从标准输入流中读取的日期无效,程序应写入“错误:无效日期”。函数 main 返回 1。

示例交互

$ echo "2021/03/01" | ./pa04
0 Monday
$ echo $?
0

$ echo "  2022/3/1" | ./pa04
1 Tuesday
$ echo $?
0

$ echo "3/1/2022" | ./pa04
Error: invalid date
$ echo $?
1

$ echo "abracadabra" | ./pa04
Error: invalid date
$ echo $?
1  
#include iostream
#include string
#include iomanip
#include cmath

using namespace std;

int main() {
    int day, month, year, w;

    // When I get here I feel there needs to be a cin.ignore() for the '/' in 2021/10/07. 
    // But I'm also concerned that if someone puts in 7 instead of 07 like in the sample interactions that it will jack up the results.
    cin >> year >> month >> day;

    w = (day + (13 * (month + 1) / 5) + (year) + (year / 4) - (year / 100) + (year / 400)) % 7;

    switch (w) {
        case 1:
            cout << "Sunday \n";
            break;
        case 2:
            cout << "Monday \n";
            break;
        case 3:
           cout << "Tuesday \n";
           break;
        case 4:
            cout << "Wednesday \n";
            break;
        case 5:
            cout << "Thursday \n";
            break;
        case 6:
            cout << "Friday \n";
            break;
        case 7:
            cout << "Saturday \n";
            break;
    }
    
    return 0;
}

【问题讨论】:

    标签: c++ file-io io


    【解决方案1】:

    您根本没有处理 / 字符。

    您可以为此使用额外的 &gt;&gt; 调用和变量:

    int day, month, year;
    char slash1, slash2;
    
    if ((cin >> year >> slash1 >> month >> slash2 >> day) &&
        (slash1 == '/') &&
        (slash2 == '/'))
    {
        // use values as needed...
    }
    else
    {
        // invalid format...
    }
    

    或者,您可以使用istream::ignore() 跳过/ 字符:

    int day, month, year;
    
    if (cin >> year &&
        cin.ignore(numeric_limits<streamsize>::max(), '/') &&
        cin >> month &&
        cin.ignore(numeric_limits<streamsize>::max(), '/') &&
        cin >> day)
    {
        // use values as needed...
    }
    else
    {
        // invalid format...
    }
    

    或者,在 C++11 及更高版本中,您可以使用 std::get_time() 让标准库读入并为您解析日期组件:

    #include <iomanip>
    
    tm date;
    if (cin >> get_time(&date, "%Y/%m/%d"))
    {
        int day = date.tm_mday;
        int month = date.tm_mon + 1;
        int year = date.tm_year + 1900;
        int w = date.tm_wday + 1;
        // use values as needed...
    }
    else
    {
        // invalid format...
    }
    

    使用std::get_time() 的另一个好处是std::tm 在一周中的某一天有一个tm_wday 成员,如上所示,因此您不必手动计算。

    【讨论】:

    • 感谢 Remy,非常感谢您的帮助
    【解决方案2】:

    您也可以为此使用 C++20 的 std::chrono。它让你:

    • 根据格式规范将输入字符串解析为时间对象(例如系统时钟中的天数)。
    • 获取该时间对象的星期几。

    我发现 std::chronoparse 方法:

    1. 不是很灵活。 AFAIK,它不允许您使用正则表达式(例如,用于匹配输入字符串开头的空格,就像在您的输入样本之一中一样,
      2022/4/2;尤其是,这很容易修复在解析之前修剪字符串)。
    2. 可能会导致一些解析意外。例如,对于您的另一个输入样本5/3/2023,它会将日期解释为第 5 年的 3 月 20 日。也就是说,它将日期与前 2 位数字匹配,20,什么应该是您输入字符串中的日期,2023

    所以我会在这里寻求混合解决方案:

    • 将输入字符串手动解析为时间对象。
    • 然后使用std::chrono 获取与该时间对象对应的工作日。

    下面的代码在using_just_chronousing_regex_and_chrono 两个不同的函数中实现了这两种解决方案,只是为了显示我上面提到的问题,如果我们只使用std::chrono。我已经使用正则表达式进行手动解析,但您可以在进行一些修剪后使用字符串流捕获每个日期的年、月和日。

    #include <chrono>  // parse, sys_days, weekday
    #include <iostream>  // cout
    #include <regex>  // regex_match, smatch
    #include <sstream>  // istringstream
    
    namespace ch = std::chrono;
    
    void using_just_chrono(const std::string& date)
    {
        ch::year_month_day ymd{};
        std::istringstream iss{ date };
        if (iss >> ch::parse(std::string{ "%Y/%m/%d" }, ymd) and ymd.ok())
        {
            std::cout << "\t\tusing just chrono: " << ch::weekday{ ch::sys_days{ ymd } } << "\n";
        }
    }
    
    void using_regex_and_chrono(const std::string& date)
    {
        // Probably more portable using [[:space:]] and [[:digit:]] here
        std::regex pattern{ R"(^\s*(\d{1,4})/(\d{1,2})/(\d{1,2})\s*$)" };
        std::smatch matches{};
        if (std::regex_match(date, matches, pattern))
        {
            ch::year_month_day ymd(
                ch::year{ std::stoi(matches[1]) },
                ch::month{ std::stoul(matches[2]) },
                ch::day{ std::stoul(matches[3]) });
            if (ymd.ok())
            {
                std::cout << "\t\tusing rgx and chr: " << ch::weekday{ ch::sys_days{ ymd } } << "\n";
            }
        }
    }
    
    int main()
    {
        std::cout << "Parsing dates:\n";
        for (const std::string& date : {
            "2021/03/01",  // OK
            "  2022/4/2",  // OK: needs trimming white spaces at the front
            "5/3/2023",  // wrong: day is 2023
            "abracadabra",  // wrong format
            "2020/12/32",  // wrong: day is 32
            "2019/13/20",  // wrong: month is 13
            "2021/2/29" })  // wrong: non existing date
        {
            std::cout << "\t\"" << date << "\":\n";
            using_just_chrono(date);
            using_regex_and_chrono(date);
        }
        std::cout << "\n";
    }
    
    // Outputs:
    //
    // Parsing dates:
    //     "2021/03/01":
    //         using just chrono: Mon
    //         using rgx and chr: Mon
    //     "  2022/4/2":
    //         using rgx and chr: Sat
    //     "5/3/2023":
    //         using just chrono: Sun
    //     "abracadabra":
    //     "2020/12/32":
    //     "2019/13/20":
    //     "2021/2/29":
    

    【讨论】:

      猜你喜欢
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-03
      • 2014-06-03
      相关资源
      最近更新 更多