Как читать в файле данных неизвестных измерений в C / C ++ - PullRequest
2 голосов
/ 08 февраля 2010

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

В верхней части файла находится строка с комментариями, представляющая ряд чисел, относящихся к тому, что содержит каждый столбец. Каждая строка содержит данные для каждого числа в определенный момент времени, поэтому пример файла данных (маленький - те, которые я использую, намного больше!) Может выглядеть так:

# 1 4 6 28
21.2 492.1 58201.5 586.2
182.4 1284.2 12059. 28195.2
.....

В настоящее время я использую Python для чтения данных, используя numpy.loadtxt, который удобно разбивает данные в форме строки / столбца независимо от размера массива данных, но это становится довольно медленным. Я хочу быть в состоянии сделать это надежно в C или C ++.

Я вижу некоторые варианты:

  1. Добавить тег заголовка с размерами из моей программы извлечения

    # 1 4 6 28
    # xdim, ydim
    21.2 492.1 58201.5 586.2
    182.4 1284.2 12059. 28195.2
    .....
    

    но это требует переписывания моих программ извлечения и программ, которые используют извлеченные данные, что довольно интенсивно.

  2. Хранить данные в файле базы данных, например. MySQL, SQLite и т. Д. Затем данные могут быть извлечены по требованию. Это может быть требованием в дальнейшем в процессе разработки, так что в любом случае было бы хорошо разобраться.

  3. Используйте Python для чтения данных и переноса кода C для анализа. Это может быть проще всего в краткосрочной перспективе.

  4. Используйте wc для linux, чтобы найти количество строк и количество слов в заголовке, чтобы найти размеры.

    echo $((`cat FILE | wc -l` - 1)) # get number of rows (-1 for header line)
    echo $((`cat FILE | head -n 1 | wc -w` - 1)) # get number of columns (-1 for '#' character)
    
  5. Использовать код C / C ++

Этот вопрос в основном относится к пункту 5 - если есть простой и надежный способ сделать это в C / C ++. В противном случае приветствуются любые другие предложения

Спасибо

Ответы [ 5 ]

12 голосов
/ 08 февраля 2010

Создать таблицу как вектор векторов:

std::vector<std::vector<double> > table;

Внутри бесконечного (while(true)) цикла:

Считать строку:

std::string line;
std::getline(ifs, line);

Если что-то пошло не так (возможно, EOF), выйдите из цикла:

if(!ifs) 
    break;

Пропустите эту строку, если это комментарий:

if(line[0] == '#')
    continue;

Считать содержимое строки в вектор:

std::vector<double> row;
std::copy(std::istream_iterator<double>(ifs),
          std::istream_iterator<double>(),
          std::back_inserter(row));

Добавить строку в таблицу;

table.push_back(row);

В то время, когда вы находитесь вне цикла, «таблица» содержит данные:

  • table.size () - количество строк

  • таблица [i] - строка i

  • table [i] .size () - количество столбцов. в строке я

  • table [i] [j] - элемент в j-ом столбце. строки i

9 голосов
/ 08 февраля 2010

Как насчет:

  1. Загрузить файл.
  2. Подсчет количества строк и столбцов.
  3. Закройте файл.
  4. Выделите необходимую память.
  5. Загрузите файл снова.
  6. Заполнить массив данными.

Каждый загрузчик .obj (файл 3D-модели), который я видел, использует этот метод. :)

0 голосов
/ 08 февраля 2010

Я видел твой ответ, и хотя он не плохой, я не думаю, что он тоже идеален. По крайней мере, как я понимаю ваш первоначальный вопрос, первый комментарий в основном указывает, сколько столбцов у вас будет в каждой из оставшихся строк. например тот, который вы дали («1 4 6 28»), содержит четыре числа, которые можно интерпретировать как выражение, что каждая последующая строка будет содержать 4 числа.

Предполагая, что это правильно, я бы использовал эти данные для оптимизации чтения данных. В частности, после этого (опять же, насколько я понимаю) файл просто содержит строку за строкой чисел. В таком случае я бы собрал все числа в один вектор и использовал бы количество столбцов из заголовка для индексации по остальным:

class matrix { 
    std::vector<double> data;
    int columns;
public:
    // a matrix is 2D, with fixed number of columns, and arbitrary number of rows.
    matrix(int cols) : columns(cols) {}

    // just read raw data from stream into vector:    
    std::istream &read(std::istream &stream) { 
        std::copy(std::istream_iterator<double>(stream), 
                  std::istream_iterator<double>(), 
                  std::back_inserter(data));
        return stream;
   }

   // Do 2D addressing by converting rows/columns to a linear address
   // If you want to check subscripts, use vector.at(x) instead of vector[x].
   double operator()(size_t row, size_t col) { 
       return data[row*columns+col];
   }
};

Это все довольно просто - матрица знает, сколько у нее столбцов, поэтому вы можете выполнять индексирование x, y в матрице, даже если она хранит все свои данные в одном векторе. Чтение данных из потока просто означает копирование этих данных из потока в вектор. Чтобы иметь дело с заголовком и упростить создание матрицы из данных в потоке, мы можем использовать простую функцию, подобную этой:

matrix read_data(std::string name) { 
    // read one line from the stream.
    std::ifstream in(name.c_str());
    std::string line;
    std::getline(in, line);

    // break that up into space-separated groups:
    std::istringstream temp(line);
    std::vector<std::string> counter;
    std::copy(std::istream_iterator<std::string>(temp), 
              std::istream_iterator<std::string>(),
              std::back_inserter(counter));

    // the number of columns is the number of groups, -1 for the leading '#'.
    matrix m(counter.size()-1);

    // Read the remaining data into the matrix.
    m.read(in);
    return m;
}

Как написано прямо сейчас, это зависит от того, как ваш компилятор реализует «оптимизацию именованных возвращаемых значений» (NRVO). Без этого компилятор скопирует весь matrix (возможно, пару раз), когда он будет возвращен из функции. При оптимизации компилятор предварительно выделяет место для матрицы и read_data() создает матрицу на месте.

0 голосов
/ 08 февраля 2010

Придумал, как это сделать. В основном спасибо Мануэлю, так как это был самый информативный ответ.

std::vector< std::vector<double> > readIn2dData(const char* filename)
{
    /* Function takes a char* filename argument and returns a 
     * 2d dynamic array containing the data
     */

    std::vector< std::vector<double> > table; 
    std::fstream ifs;

    /*  open file  */
    ifs.open(filename);

    while (true)
    {
        std::string line;
        double buf;
        getline(ifs, line);

        std::stringstream ss(line, std::ios_base::out|std::ios_base::in|std::ios_base::binary);

        if (!ifs)
            // mainly catch EOF
            break;

        if (line[0] == '#' || line.empty())
            // catch empty lines or comment lines
            continue;


        std::vector<double> row;

        while (ss >> buf)
            row.push_back(buf);


        table.push_back(row);


    }

    ifs.close();

    return table;
}

В основном создайте вектор векторов. Единственной трудностью было разбиение по пробелам, которые решаются с помощью объекта stringstream. Возможно, это не самый эффективный способ сделать это, но он, безусловно, работает в краткосрочной перспективе!

Также я ищу замену устаревшей функции atof, но это не важно. Нужна только проверка утечки памяти (не должно быть, поскольку большинство объектов std объекты), и я закончил.

Спасибо за вашу помощь

0 голосов
/ 08 февраля 2010

Вам нужна квадратная или рваная матрица? Если последнее, создайте структуру, подобную этой:

 std:vector < std::vector <double> > data;

Теперь читайте каждую строку за раз в:

 vector <double> d;

и добавьте вектор к рваной матрице:

 data.push_back( d );

Все задействованные структуры данных являются динамическими и будут расти по мере необходимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...