【问题标题】:How to read file into vector of tuple?如何将文件读入元组向量?
【发布时间】:2021-03-09 18:36:05
【问题描述】:

我想读取一个文本文件,其中包含以下数据:

Wogger John 2   6.2
Bilbo   111 81.3
Mary    29  154.8

制表符分隔的数据。问题是如果字符串包含空格(例如:'Wogger John'),那么程序就不能工作。如果我将字符串“Wogger John”替换为“Wogger”或“John”,该程序确实有效。如何解决问题?如何使用 getline() 函数。代码如下:

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cerrno>
#include <cstring>
#include <tuple>

std::vector<std::tuple<std::string, int, double>> Readfile()
{
    std::ifstream File("file_read_v3.txt");
    std::vector<std::tuple<std::string, int, double>> data;
    std::string name;
    int a;
    double b;

    while (File >> name >> a >> b)
    {
        
        data.push_back(std::tuple<std::string, int, double>(name, a, b));
    }
    return data;
}

int main()
{
    auto vt = Readfile();
    
    for (const auto& i : vt) {
        std::cout << std::get<0>(i) << ", " << std::get<1>(i) << ", " << std::get<2>(i) << std::endl;
    }
    
    return 0;
    system("pause");
}

【问题讨论】:

  • 问题不是代码问题,而是设计问题。显然,尝试将Wogger John 2 6.2 作为stringintdouble格式化 提取发送是行不通的。无论您是否意识到,您都在使用空格作为实体之间的分隔符,同时希望允许它作为您的格式化项目之一的一部分。这永远都行不通。是的,可以破解一个对 int 提取失败采取措施的 getline 解决方案,但这正是它的...... hack。
  • 如果不使用元组,它是否按预期工作?问题的关键在于它有助于减少问题,这也是为什么需要minimal reproducible example
  • 也许comma-separated value representation 更适合您的数据。如果不清楚部分是如何分离的,则很难解析。

标签: c++ file vector tuples


【解决方案1】:
while (std::getline(File, name, '\t') >> a >> b)
{        
    data.push_back(std::tuple<std::string, int, double>(name, a, b));
}

std::getline 最多读取并包括一个分隔符,然后丢弃该分隔符。它默认分隔符是换行符;在这里,我们将其设为选项卡。

【讨论】:

  • 谢谢!这是一个很好且简短的解决方案!
【解决方案2】:

如果分隔符始终相同,并且与名称之间的分隔符不同,您可以在 getline 中使用它作为分隔符,例如:

while (std::getline(File, name, '\t')){
    File >> a >> b;
    data.push_back(std::tuple<std::string, int, double>(name, a, b));          
}

但是根据您的示例文件内容,您似乎随机放置了制表符和空格,因此getline 并没有太大帮助,问题仍然存在。

例如,您可以通过char 读取文件char 并在遇到一个数字时停止读取,将那个数字放回去然后解析数值,大致如下:

std::vector<std::tuple<std::string, int, double>> Readfile()
{
    std::ifstream File("file_read_v3.txt");
    std::vector<std::tuple<std::string, int, double>> data;

    if (File.is_open()) // always check if file was opened
    {
        int a;
        double b;
        char c;

        do
        {
            std::string name;
            while (!std::isdigit(c = File.get())) // get character, check if is digit
            {
                name.push_back(c); // add characters until a digit is found
            }
            File.unget(); // put digit back
            File >> a >> b; // parse the int and double
            data.push_back(std::tuple<std::string, int, double>(name, a, b));
            c = File.get(); // get next character
        } while (c != EOF); // if there is nothing else to read
    }
    return data;
}

要做到这一点,请删除name 末尾的空格或制表符。

【讨论】:

  • 谢谢!如果我在 while 循环中添加另一个条件,那就完全没问题了:&amp;&amp; c != '\t'.
  • @Gabriel90 gald 你解决了它,如果该行中唯一的选项卡是将名称与值分开的选项卡,那么它更简单,您在问题中发布的示例文本文件看起来不就像那样,尽管你的描述暗示了那个方向。您可以看到以下答案还假设行中有更多选项卡。
【解决方案3】:

这条线:

while (File >> name >> a >> b)

导致您的问题。您是说将我的行剪切成 1 个字符串和 2 个由空格分隔的整数,所以行:

Wogger John 2   6.2

无法解析,因为它包含 2 个字符串 + 2 个整数,并且流运算符将返回 false 并且您的 while 循环正在退出。

要以本机方式解决此问题,您需要解析每个字符,直到找到\t。将解析后的字符放入字符串中,然后解析数字直到\t再次出现并解析下一个数字。

【讨论】:

  • 显然每行只有一个制表符分隔名称和值,使其更简单,我还假设它更复杂。如果不是这样,这将是正确的策略。而且我想我已经欠了 1 很长一段时间了;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-21
  • 2017-04-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多