2009-08-24 6 views
18

мне нужно прочитать в файлах данных, которые выглядят следующим образом:Как читать числа из текстового файла (C++)

* SZA: 10.00 
2.648 2.648 2.648 2.648 2.648 2.648 2.648 2.649 2.650 2.650 
2.652 2.653 2.652 2.653 2.654 2.654 2.654 2.654 2.654 2.654 
2.654 2.654 2.654 2.655 2.656 2.656 2.657 2.657 2.657 2.656 
2.656 2.655 2.655 2.653 2.653 2.653 2.654 2.658 2.669 2.669 
2.667 2.666 2.666 2.664 2.663 2.663 2.663 2.662 2.663 2.663 
2.663 2.663 2.663 2.663 2.662 2.660 2.656 2.657 2.657 2.657 
2.654 2.653 2.652 2.651 2.648 2.647 2.646 2.642 2.641 2.637 
2.636 2.636 2.634 2.635 2.635 2.635 2.635 2.634 2.633 2.633 
2.633 2.634 2.634 2.635 2.637 2.638 2.637 2.639 2.640 2.640 
2.639 2.640 2.640 2.639 2.639 2.638 2.640 2.640 2.638 2.639 
2.638 2.638 2.638 2.638 2.637 2.637 2.637 2.634 2.635 2.636 
2.637 2.639 2.641 2.641 2.643 2.643 2.643 2.642 2.643 2.642 
2.641 2.642 2.642 2.643 2.645 2.645 2.645 2.645 

Что бы самый элегантный способ чтения этого файла в массив поплавки?

Я знаю, как читать каждую строчку в строке, и я знаю, как преобразовать строку в float, используя atof(). Но как я могу сделать все остальное самым легким?

Я слышал о строковых буферах, может это мне помочь?

ответ

11

Поскольку это помечено как C++, наиболее очевидным способом будет использование потоков. С верхней части моей головы, что-то подобное может сделать:

std::vector<float> readFile(std::istream& is) 
{ 
    char chdummy; 
    is >> std::ws >> chdummy >> std::ws; 
    if(!is || chdummy != '*') error(); 
    std::string strdummy; 
    std::getline(is,strdummy,':'); 
    if(!is || strdummy != "SZA") error(); 

    std::vector<float> result; 
    for(;;) 
    { 
    float number; 
    if(!is>>number) break; 
    result.push_back(number); 
    } 
    if(!is.eof()) error(); 

    return result; 
} 

Почему float, кстати? Обычно double намного лучше.

Edit, так как был задан вопрос о том, возвращающая копию vector является хорошей идеей:

Для первого решения, я бы, конечно, сделать очевидное. Функция : чтение файла в vector, и наиболее очевидной задачей для функции является возвращение ее результата. Является ли это результатом заметного замедления, зависит от множества вещей (размер вектора, как часто вызывается функция и откуда, от скорости чтения этого диска, может ли компилятор применять RVO). Я бы не хотел испортить очевидное решение с оптимизацией, но если профилирование действительно показывает, что это должно замедляться, вектор должен быть передан в неконстантной ссылке.

(Также обратите внимание, что C++ 1x с поддержкой rvalue, надеюсь, что скоро будет доступен с помощью компилятора рядом с вами, сделает это обсуждение спорным, так как это предотвратит копирование вектора при возврате из функции.)

+0

Общий «прочитанный все поплавки» -loop будет «float number; while (is >> number) result.push_back (number); ' – sth

+0

Хотя ваш, конечно, эквивалентен. – sth

+0

@sth: Действительно, это более красноречиво, хотя мне не нравится, что переменная «number» «течет» из цикла. – sbi

2

Я хотел бы сделать что-то вроде этого:

std::ifstream input("input.txt"); 
std::vector<float> floats; 
std::string header; 
std::getline(input, header); // read in the "* SZA: 10.00" line 
if(header_is_correct(header)) { 
    float value; 
    // while we could successfully read in a float from the file... 
    while(input >> value) { 
     // store it in the vector. 
     floats.push_back(value); 
    } 
} 

ПРИМЕЧАНИЕ:header_is_correct(header) это просто пример, вам необходимо будет выполнять какую-либо ошибку проверки для этой первой строки вручную там.

+0

почему нисходящий? Я протестировал это, и он корректно считывает каждый float из файла в вектор. –

18

String Toolkit Library (Strtk) имеет следующее решение проблемы:

#include <iostream> 
#include <string> 
#include <deque> 
#include <iterator> 

#include "strtk.hpp" 

int main() 
{ 
    std::deque<float> flist; 
    strtk::for_each_line("file.txt", 
         [&flist](const std::string& line) 
         { strtk::parse(line," ",flist); } 
         ); 
    std::copy(flist.begin(),flist.end(), 
       std::ostream_iterator<float>(std::cout,"\t")); 
    return 0; 
} 

Больше примеров можно найти в C++ String Toolkit (StrTk) Tokenizer.

алгоритмов с использованием STL
+0

интересно, хотя вам должно быть ясно, что это только для компиляторов C++ 0x. –

+18

Очень верно, но лямбда так же легко помещается в предикат стиля структуры. Я думал о стиле и будущих ссылках (давайте посмотрим правде в глаза через 1-2 года, приведенный выше код и, соответственно, будет нормой), было бы неплохо иметь другое представление о том, как это можно сделать. – 2009-08-25 01:19:07

+12

Мне это понравилось. Хорошее использование новых лямбдов, даже если это не может быть ответом. –

2

Простого решения:

#include <vector> 
#include <iostream> 
#include <string> 
#include <iterator> 

struct data 
{ 
    float first; // in case it is required, and assuming it is 
       // different from the rest 
    std::vector<float> values; 
}; 

data read_file(std::istream& in) 
{ 
    std::string tmp; 
    data d; 
    in >> tmp >> tmp >> d.first; 
    if (!in) throw std::runtime_error("Failed to parse line"); 

    std::copy(std::istream_iterator<float>(in), std::istream_iterator<float>(), 
     std::back_inserter<float>(d.values)); 

    return data; 
} 

Если вам действительно нужно использовать массив, вы должны сначала выделить его (либо динамически или статический, если вы знаете размер), а затем вы можете использовать ту же копию алгоритм

// parsing the first line would be equivalent 
float data[128]; // assuming 128 elements known at compile time 
std::copy(std::istream_iterator<float>(is), std::istream_iterator<float>(), 
     data); 

Но я бы рекомендовал использовать зЬй :: вектор, даже в этом случае, если вам нужно передать данные в функцию, которая принимает массив, который вы всегда можете передать его как указатель на первый элемент:

void f(float* data, int size); 
int main() 
{ 
    std::vector<float> v; // and populate 
    f(&v[0], v.size()); // memory is guaranteed to be contiguous 
} 
Смежные вопросы