2016-10-31 3 views
0

Я читаю значения одиночной точности с разделителями пробелов в научной нотации из файла 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.

Есть ли способ настроить stringstream для выполнения неявного преобразования и округления до ближайшего целого числа, или мне нужно сначала вставить исходный примитивный тип (float) и выполнить явное преобразование в T? В идеале, я бы не хотел рассказывать ReadValues ​​о типе данных в string_with_data - он всегда будет одним из double, float, int, short или long, а запрошенный тип также может быть любым из этих типов.

Спасибо.

ответ

1

Если вы знаете тип данных в 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++ нет ни одного типа, ни в стандартной библиотеке, которая достаточно велика, чтобы принимать все остальные типы. Таким образом, вы должны прочитать каждые данные в виде строки, а затем декодировать его в требуемом тип с вспомогательным stringstream:

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
  • использовать максимальную точность допускаемой требуемого типа при чтении оригинальной строки

ПРИМЕЧАНИЯ: Я предположим, что вы принимаете конверсии по умолчанию ...

+0

Спасибо. Я сделал первый подход, который вы предлагаете в конце, указав тип, исходящий из параметра шаблона. Я заменил C-стиль на static_cast (val). – pdm2011

0

Почему вы не извлекаете данные непосредственно из своего файла как плавающие, а не получаете первые строки, затем плаваете, а затем целые числа?

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; 
} 
+1

Спасибо. Я действительно пошел на первый вариант, который вы предлагаете, хотя и в методе шаблона. Кстати, в прошлом я столкнулся с ошибками «плохого выделения» при использовании istream_iterator с большими файлами, даже используя 64-битную сборку с большим количеством доступной оперативной памяти. – pdm2011