Как я могу прочитать файл со строками разного количества чисел

0

Я пытаюсь прочитать в файле данных около 2000 строк, файл выглядит примерно так:

1.1 1.2 1.3 1.4 1.5
1.6     1.7 1.8 1.9 
2.0
2.1 2.2 2.3 2.4 2.5

Фактически пустое (пробел) и 1.3/1.7 находятся в одном столбце

Способ, которым я устанавливаю его как хранение, представляет собой вектор структур, где

struct num
{
    double d1, d2, d3, d4, d5;
};

То, что я пытаюсь достичь, - это

num A;
vector<num> data
for (int i = 0; i < 4; i++)
{
    File >> A.d1 >> A.d2 >> A.d3 >> A.d4 >> A.d5;
    data.push_back(A);
}

и найти логику для распознавания пробела во второй строке и сохранить d1 = 1.6, d2 = 0, d3 = 1.7 и т.д., а третья строка - d1 = 2.0 и d2, d3, d4, d5 = 0 Я просто запутался в том, как проверить/получить логику для реализации этого, если это возможно. Я нахожусь в C++ VS2010. Посмотрев на первый ответ, я должен предоставить дополнительную информацию, каждая строка в файле принадлежит спутнику, и каждый число представляет собой наблюдение на определенной длине волны, поэтому, если оно пустое, это означает, что у него нет наблюдений на этой длине волны.

Поэтому, чтобы разработать, первая строка представляет собой спутник 1 имеет наблюдение на всех 5 длинах волн, строки 2, ресиверы satelittle 2, и имеет наблюдения на длине волны 1,3,4,5 и ни одна на длине волны 4.

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

Теги:
file-io
readfile

3 ответа

2

Наблюдение за вашими данными:

  • Каждая точка данных хранится в следующем шаблоне: data, space.
  • Если точка данных не существует, она представляется пробелом, если только она не является последней несуществующей точкой данных, где все остальные выходные данные усекаются до новой строки.

Вот что я придумал:

#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#include <sstream>
#include <iomanip>
#include <cctype>
using namespace std;

//note all the lines are stored WITH newlines at the end of them.
//This is merely an artifact of the methodology I am using,
//as the newline is a flag that truncates output (as per your problem)
vector<string> preparse_input(const std::string& filename) {
    vector<string> lines;

    ifstream ifile;

    ifile.open(filename.c_str(), ios::in);
    if (!ifile.is_open()) {
        exit(1);
    }

    string temp, chars, line;
    char ch;

    while(getline(ifile, temp)) {
        temp += "\n";//getline removes the newline: because we need it, reinsert it
        istringstream iss(temp);

        //first read in the line char by char
        while(iss >> noskipws >> ch) {
            chars += ch;
        }

        bool replaced_newline = false;
        int nargs = 0;

        //I could have used iterators here, but IMO, this way is easier to read. Modify if need be.
        for (int i = 0; i < chars.size(); ++i) {
            if (isdigit(chars[i]) && chars[i+1] == ' ') {
                nargs += 1;
            }
            else if(isspace(chars[i]) && isspace(chars[i+1])) {
                if (chars[i+1] == '\n') {
                    replaced_newline = true;
                }
                //this means that there is no value set
                //hence, set the value to 0 for the value part:
                chars[i+1] = '0';
                line += chars[i];
                ++i;//now, skip to the next character since 1 is for spacing, the other is for the value
                nargs += 1;
            }

            //now rebuild the line:
            line += chars[i];

            if(isdigit(chars[i]) && chars[i+1] == '\n') {
                nargs += 1;
                //check nargs:
                for (int i = nargs; i < 5; ++i) {
                    line += " 0";
                    nargs += 1;
                }
            }

            if (replaced_newline) {
                line += '\n';
            }
            replaced_newline = false;
        }

        lines.push_back(line);
        chars.clear();
        line.clear();
    }
    ifile.close();

    return lines;
}

//this way, it much easier to adapt to any type of input that you may have
template <typename T>
vector< vector<T> > parse_input (const vector<string>& lines) {
    vector< vector<T> > values;
    T val = 0;

    for(vector<string>::const_iterator it = lines.begin(); it != lines.end(); ++it) {
        vector<T> line;
        istringstream iss(*it);
        string temp;

        while(getline(iss, temp, ' ')) {
            if (istringstream(temp) >> val) {
                line.push_back(val);
            }
            else {
                line.push_back(0);//this is the value that badly parsed values will be set to.
                            //you have the option of setting it to some sentinel value, say -1, so you can go back and correct it later on, if need be. Depending on how you want to treat this error - hard or soft (stop program execution vs adapt and continue parsing), then you can adapt it accordingly
                            //I opted to treat it as a soft error but without a sentinel value - so I set it to 0 (-1 as that is probably more applicable in a general case), and informed the user that an error occurred
                            //The flipside of that is that I could have treated this as a hard error and have 'exit(2)' (or whatever error code you wish to set).
                cerr << "There was a problem storing:\"" << temp << "\"\n";
            }
        }
        values.push_back(line);
    }
    return values;
}

int main() {
    string filename = "data.dat";
    vector<string> lines = preparse_input(filename);

    vector < vector<double> > values = parse_input<double>(lines);

    for (int i = 0; i < values.size(); ++i) {
        for (int j = 0; j < values[i].size(); ++j) {
            cout << values[i][j] << " ";
        }
        cout << endl;
    }

    return 0;
}

В общем, я сломал строку, прочитав каждый символ линии по символу, а затем перестроил каждую строку, заменив пробелы на 0 для упрощения синтаксического анализа. Зачем? Потому что без такого значения невозможно определить, какой параметр был сохранен или пропущен (используя методологию ifstream_object >> type по умолчанию).

Таким образом, если я затем использую объекты stringstream для синтаксического анализа ввода, я могу правильно определить, какой параметр установлен или не установлен; затем сохраните результаты, и все будет денди. Это то, чего вы желаете.

И, используя его по следующим данным:

1.1 1.2 1.3 1.4 1.5
1.6   1.7 1.8 1.9
2.0        
2.0
2.1 2.2 2.3 2.4 2.5
2.1     2.4

Дает вам выход:

1.1 1.2 1.3 1.4 1.5
1.6 0 1.7 1.8 1.9
2 0 0 0 0
2 0 0 0 0
2.1 2.2 2.3 2.4 2.5
2.1 0 0 2.4 0

ПРИМЕЧАНИЕ. Строка 3 имеет 8 пробелов (1 без данных и 1 для интервала). Строка 4 - это строка из исходных данных. Строка 6 содержит 5 пробелов (по приведенному шаблону).

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

  • 1
    Мне трудно найти больше безумных форматов, на ум приходит xml, но люди не склонны соглашаться со мной по этому вопросу.
  • 0
    Я ценю это, это огромная помощь! Что касается хранения данных, я не знал другого способа сохранить их и получить к ним эффективный доступ, поскольку каждая линия представляет 1 спутник, а их 2000 строк. Будете ли вы иметь какие-либо рекомендации о том, как хранить его, чтобы каждая строка была отделена друг от друга?
Показать ещё 1 комментарий
1

Учитывая, что ваш формат файла ограничен пространством, вы можете извлечь столбцы, используя регулярное выражение. Я предположил, что вы можете использовать С++ 11 или если не Boost regex.

Затем вы можете использовать следующую функцию для разделения строки на токены.

std::vector<std::string> split(const std::string& input, const std::regex& regex) {
    // passing -1 as the submatch index parameter performs splitting
    std::sregex_token_iterator
        first(input.begin(), input.end(), regex, -1),
        last;
    return std::vector<std::string>(first, last);
}

Например, если ваши данные находятся в "data.txt", я использовал его таким образом, чтобы получить значения:

#include <iostream>
#include <fstream>
#include <string>
#include <regex>
#include <vector>

using namespace std;

std::vector<std::string> split(const string& input, const regex& regex) {
    // passing -1 as the submatch index parameter performs splitting
    std::sregex_token_iterator
        first(input.begin(), input.end(), regex, -1),
        last;
    return vector<std::string>(first, last);
}

int main()
{
    ifstream f("data.txt");

    string s;
    while (getline(f, s))
    {
        vector<string> values = split(s, regex("\\s"));
        for (unsigned i = 0; i < values.size(); ++i)
        {
            cout << "[" << values[i] << "] ";
        }
        cout << endl;
    }

    return 0;
}

Это дает следующие результаты:

[1.1] [1.2] [1.3] [1.4] [1.5]
[1.6] [] [1.7] [1.8] [1.9]
[2.0] [] [] []
[2.1] [2.2] [2.3] [2.4] [2.5]

Обратите внимание: в строке 4 отсутствует столбец, но это потому, что я не совсем уверен, сколько белых пробелов у вас на этой линии. Если вы знаете, что на выходном каскаде может быть исправлено не более 5 столбцов.

Надеюсь, вы найдете этот подход полезным.

0

Почему бы просто не использовать std:vector для хранения массива поплавков.

Чтобы добавить новый элемент в вектор, который вы используете:

станд :: вектор :: push_back

Когда вы читаете в каждом персонаже, посмотрите, является ли это цифрой или периодом.

Если это так, добавьте это в std::string, а затем используйте atof с mystring.c_str() в качестве параметра, чтобы преобразовать его в float.

Это также может помочь преобразовать строку в float:

std :: string для float или double

Итак, прочитайте строку, затем нажмите float на вектор и повторите, пропуская символы, которые не являются цифрой или периодом.

В конце строки ваш вектор имеет все поплавки, и если вы хотите присоединиться к ним в строку с пользовательским разделителем, вы можете посмотреть ответы на этот вопрос:

std :: vector в строку с пользовательским разделителем

  • 0
    Потому что способ, которым мне нужен доступ к информации, мне нужен отдельно. Каждая строка представляет 5 наблюдений с одного спутника на разных длинах волн, если она пустая, это означает, что она не имеет наблюдения на этой длине волны.
  • 0
    Таким образом, если отсутствует пропущенное наблюдение, просто создайте пустой элемент и просто используйте вместо этого значение с плавающей точкой [5] и обязательно вызывайте memset перед каждым циклом (чтобы быстро очистить массив).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню