【问题标题】:Insert from stringstream with implict conversion使用隐式转换从字符串流插入
【发布时间】:2017-03-13 07:52:39
【问题描述】:

我正在从 ASCII 文件中以科学计数法读取以空格分隔的单精度值。每行有多个值。我通过流填充向量;向量的原始类型可能与数据的类型不同:

// Illustrates typical string from file
std::string string_with_floats("-2.750000e+001 2.750000e+001 3.450000e+001");

// Template parameter is the desired return type
vector<int> data = ReadValues<int>(string_with_floats);

template <class T>
vector<T>& ReadValues(std::string& string_with_data)
{
    std::stringstream ss(string_with_data);
    std::vector<T> values;
    T val;
    while(stream >> val)
    {
        values.push_back(val);
    }
    return values;
}

上面的示例导致向量仅填充第一个值,截断为 -2,大概是因为一旦遇到非数字字符,循环就会终止。当传入的字符串包含 int 值时,它会按预期工作,即使模板参数是 float。

有没有办法配置字符串流以执行隐式转换并舍入到最接近的整数,还是我需要先插入原始原始类型(浮点)并执行显式转换为 T?理想情况下,我不想告诉 ReadValues 关于 string_with_data 中数据的类型 - 它始终是 double、float、int、short 或 long 之一,并且请求的类型也可以是这些类型中的任何一种。

谢谢。

【问题讨论】:

    标签: c++ implicit-conversion stringstream


    【解决方案1】:

    为什么不直接从文件中提取数据作为浮点数,而不是获取第一行,然后是浮点数,然后是整数?

    double f;
    std::vector<int> out;
    std::ifstream file("numbers.txt");
    
    file >> std::scientific;
    
    while (file >> f)
        out.push_back(out);
    

    或者,如果您想对迭代器更友好:

    std::vector<int> out;
    std::ifstream file("numbers.txt");
    
    file >> std::scientific;
    
    std::copy(std::istream_iterator<double>(file), 
              std::istream_iterator<double>(),
              std::back_inserter(out)));
    

    如果,正如@SergeBallesta 所说,您想以类似迭代器的方式创建一个不知道“原始”类型,只知道目标类型的泛型函数:

    template <class T>
    std::vector<T> ReadValues(std::string& string_with_data)
    {
      std::istringstream ss(string_with_data);
      std::vector<T> values;
    
      using iss_it = std::istream_iterator<std::string>;
    
      std::transform(iss_it(string_with_data), iss_it(), std::back_inserter(values),
                     [](const std::string& x) { return boost::lexical_cast<T>(x) });
    
      return values;
    }
    

    【讨论】:

    • 谢谢。我已经有效地选择了您建议的第一个选项,尽管是在模板方法中。顺便说一句,我过去在将 istream_iterator 用于大文件时遇到了“错误分配”错误,即使使用具有大量可用 RAM 的 64 位版本也是如此。
    【解决方案2】:

    如果您知道 string_with_data 中数据的类型,那就更简单了:只需读取该类型的一个值,然后将其转换为所需的类型,然后再将其推入向量:

    template <class T>
    vector<T>& ReadValues(std::string& string_with_data)
    {
        std::stringstream ss(string_with_data);
        std::vector<T> values;
        float val; // use original type not required one
        while(stream >> val)
        {
            values.push_back((T) val);
        }
        return values;
    }
    

    如果你不知道原始类型,那会有点困难,因为在 C++ 和标准库中都没有单一的类型可以大到可以接受所有其他类型。因此,您必须将每个数据作为字符串读取,然后使用辅助字符串流将其解码为所需的类型:

    template <class T>
    std::vector<T> ReadValues(std::string& string_with_data)
    {
        std::stringstream ss(string_with_data);
        std::vector<T> values;
        std::string str;
        T val;
        while(ss >> str)         // first store the value into a string whatever it is
        {
            std::stringstream(str) >> val;   // then read it as the required type
            values.push_back(val);
        }
        return values;
    }
    

    这样你就可以确定:

    • 在读取 int 时,您没有将 float 的尾随部分留在流中
    • 您在读取原始字符串时使用所需类型允许的最大精度

    注意:我假设您接受默认转换...

    【讨论】:

    • 谢谢 - 我最后采用了你建议的第一种方法,将类型指定为模板参数。我用 static_cast(val) 替换了 C 风格的演员表。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-08
    • 2021-03-08
    • 2019-02-15
    • 2017-10-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多