【问题标题】:Reading specific numerical entries from an input text file that otherwise contains text从包含文本的输入文本文件中读取特定的数字条目
【发布时间】:2021-05-06 12:04:33
【问题描述】:

我有一个输入文件,其中包含数字输入参数和其他不相关的文本。

例如


//file orig_data.txt
Number of customers
25
Customer_id Customer_SSN
1           1234 
2           3456
....
25          0123 

尽管客户数量可能不同,但所有输入文件都具有相同的格式。上面的例子有 25 个客户,另一个输入文件可能有 35 个客户,因此有 35 行数据(id 和 SSN)

对我来说,一种方法是手动删除文本并创建一个仅包含数字数据的修改输入数据。


//file modified_data.txt
25
1           1234 
2           3456
....
25          0123 

使用这种类型的输入文件(modified_data.txt),我可以使用以下代码来读取数据。

void readdata() {
        FILE* fp = fopen("modified_data.txt","r");
        fscanf(fp, "%d", &no_cust);
        int* ids  = new int[no_cust+1];
        int* ssns = new int [no_cust+1];
        for(int i = 1; i <= no_cust; i++)
                   fscanf(fp, "%d %d", &ids[i], &ssns[i]);
        //other stuff
}

有没有办法适当地使用orig_data.txt 本身并让读取此文件的函数跳过包含不需要的文本的第一行和第三行?

【问题讨论】:

标签: c++ file-io


【解决方案1】:

你应该只使用fscanf的返回值,因为它包含成功转换的项目数。

因此,您可以对建议的代码应用最少的更改:

void readdata() {
        FILE* fp = fopen("modified_data.txt","r");
        for (;;) {
            if (1 == fscanf(fp, "%d", &no_cust)) break; // will skip over non numeric data
        }
        int* ids  = new int[no_cust+1];
        int* ssns = new int [no_cust+1];
        for(int i = 1; i <= no_cust; i++) {
            for(;;) {
                   if (2 == fscanf(fp, "%d %d", &ids[i], &ssns[i])) break;
            }
        //other stuff
}

但这确实是最小的,因为它不会尝试提供错误恢复,甚至不会尝试提供面对错误输入数据的相关消息...

【讨论】:

    【解决方案2】:

    你是用 C++ 标记的,为什么要降到 C 的级别?

    标准库

    IOStreams有货,但接口繁琐,实现效率不高:

    struct Customer {
        int       id;
        uintmax_t SSN;
    };
    
    auto parse_customers(std::istream& data) {
        std::vector<Customer> customers;
        std::string line;
    
        while (getline(data, line))
            if (line == "Number of customers")
                break;
    
        auto skipline = [](std::istream& is, int n = 1) -> auto& {
            // o horrors, iostreams interface is not enjoyable
            while (is && n--)
                is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            return is;
        };
    
        std::size_t n;
        if (skipline((data >> n), 2); n)
            while (data.good() && n--) {
                Customer c;
                if (skipline(data >> c.id >> c.SSN))
                    customers.push_back(c);
            }
    
        if ((n != -1ull) || data.fail())
            throw std::invalid_argument("parse_customers");
        return customers;
    }
    

    Live On Compiler Explorer

    提振精神

    让我们试试 Boost Spirit:

    auto marker  = no_case["number of customers"];
    auto header  = *(char_ - eol); //one whole line
    auto id      = uint_;
    auto SSN     = uint_;
    auto record  = rule<void, Customer> {} = id >> SSN;
    auto grammar
        = seek[marker] >> eol
        >> omit[uint_ >> eol] // ignored
        >> omit[header] >> eol // ignored
        >> record % eol
        ;
    

    假设任何合适的客户类型:

    #include <boost/fusion/adapted/std_pair.hpp>
    using Customer = std::pair<int /*id*/, uintmax_t /*ssn*/>;
    

    (你也可以使用结构体,重点是以强类型的方式“自动”传播成员)。

    Live On Compiler Explorer打印

    解析了 25 个客户:{(1, 1234), (2, 3456), (3, 3457), (4, 3458), (5, 3459), (6, 3460), (7, 3461), (8, 3462), (9, 3463), (10, 3464), (11, 3465), (12, 3466), (13, 3467), (14, 3468), (15, 3469), (16) , 3470), (17, 3471), (18, 3472), (19, 3473), (20, 3474), (21, 3475), (22, 3476), (23, 3477), (24, 3478) ), (25, 123)}

    实际上,您可以(在这种特定情况下)简化为

    auto grammar = seek[(uint_ >> uint_) % eol];
    

    因为所有其余部分都应该被忽略并且不匹配。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-21
      相关资源
      最近更新 更多