C ++ файл ввода-вывода и разделение по разделителю - PullRequest
8 голосов
/ 06 ноября 2008

У меня есть файл со следующими данными:

0,       2,    10
10,       8,    10
10,       10,   10
10,       16,   10
15,       10,   16
17,       10,   16

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

По какой-то причине я не могу найти простой способ сделать это в C ++. Единственный успех, который у меня был, - это ввод каждой строки в массив, а затем регулярное выражение всех пробелов и последующее разбиение. Весь этот процесс занял у меня 20-30 строк кода, и мне было трудно изменить, скажем, другой разделитель (например, пробел) и т. Д.

Это Python-эквивалент того, что я хотел бы иметь в C ++:

f = open('input_hard.dat')
lines =  f.readlines()
f.close()

#declarations
inint, inbase, outbase = [], [], []

#input parsing
for line in lines:
    bits = string.split(line, ',')
    inint.append(int(bits[0].strip()))
    inbase.append(int(bits[1].strip()))
    outbase.append(int(bits[2].strip()))

Простота использования этого в python - одна из причин, по которой я перешел к нему в первую очередь. Однако сейчас мне нужно сделать это на C ++, и я не хотел бы использовать мой уродливый 20-30-строчный код.

Любая помощь будет оценена, спасибо!

Ответы [ 7 ]

6 голосов
/ 06 ноября 2008

Нет реальной необходимости использовать boost в этом примере, так как потоки отлично справятся с задачей:

int main(int argc, char* argv[])
{
    ifstream file(argv[1]);

    const unsigned maxIgnore = 10;
    const int delim = ',';
    int x,y,z;

    vector<int> vecx, vecy, vecz;

    while (file)
    {
        file >> x;
        file.ignore(maxIgnore, delim);
        file >> y;
        file.ignore(maxIgnore, delim);
        file >> z;

        vecx.push_back(x);
        vecy.push_back(y);
        vecz.push_back(z);
    }
}

Хотя, если бы я собирался использовать boost, я бы предпочел простоту tokenizer , чтобы regex ... :)

4 голосов
/ 06 ноября 2008

Нет ничего плохого в fscanf, который, вероятно, является самым быстрым решением в этом случае. И он такой же короткий и читаемый, как и код Python:

FILE *fp = fopen("file.dat", "r");
int x, y, z;
std::vector<int> vx, vy, vz;

while (fscanf(fp, "%d, %d, %d", &x, &y, &z) == 3) {
  vx.push_back(x);
  vy.push_back(y);
  vz.push_back(z);
}
fclose(fp);
2 голосов
/ 06 ноября 2008

Что-то вроде:

vector<int> inint;
vector<int> inbase;
vector<int> outbase;
while (fgets(buf, fh)) {
   char *tok = strtok(buf, ", ");
   inint.push_back(atoi(tok));
   tok = strtok(NULL, ", ");
   inbase.push_back(atoi(tok));
   tok = strtok(NULL, ", ");
   outbase.push_back(atoi(tok));
}

За исключением проверки ошибок.

1 голос
/ 06 ноября 2008

почему не тот же код, что и в python :)?

std::ifstream file("input_hard.dat");
std::vector<int> inint, inbase, outbase;

while (file.good()){
    int val1, val2, val3;
    char delim;
    file >> val1 >> delim >> val2 >> delim >> val3;

    inint.push_back(val1);
    inbase.push_back(val2);
    outbase.push_back(val3);
}
1 голос
/ 06 ноября 2008

Если вы не возражаете против использования библиотек Boost ...

#include <string>
#include <vector>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>

std::vector<int> ParseFile(std::istream& in) {
    const boost::regex cItemPattern(" *([0-9]+),?");
    std::vector<int> return_value;

    std::string line;
    while (std::getline(in, line)) {
        string::const_iterator b=line.begin(), e=line.end();
        boost::smatch match;
        while (b!=e && boost::regex_search(b, e, match, cItemPattern)) {
            return_value.push_back(boost::lexical_cast<int>(match[1].str()));
            b=match[0].second;
        };
    };

    return return_value;
}

Это извлекает строки из потока, а затем использует библиотеку Boost :: RegEx (с группой захвата) для извлечения каждого числа из строк. Он автоматически игнорирует все, что не является допустимым числом, хотя это можно изменить, если вы пожелаете.

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

РЕДАКТИРОВАТЬ: Ой, вы хотели три отдельных вектора. Попробуйте вместо этого небольшую модификацию:

const boost::regex cItemPattern(" *([0-9]+), *([0-9]+), *([0-9]+)");
std::vector<int> vector1, vector2, vector3;

std::string line;
while (std::getline(in, line)) {
    string::const_iterator b=line.begin(), e=line.end();
    boost::smatch match;
    while (b!=e && boost::regex_search(b, e, match, cItemPattern)) {
        vector1.push_back(boost::lexical_cast<int>(match[1].str()));
        vector2.push_back(boost::lexical_cast<int>(match[2].str()));
        vector3.push_back(boost::lexical_cast<int>(match[3].str()));
        b=match[0].second;
    };
};
1 голос
/ 06 ноября 2008

std :: getline позволяет вам читать строку текста, и вы можете использовать поток строк для анализа отдельной строки:

string buf;
getline(cin, buf); 
stringstream par(buf);

char buf2[512];
par.getline(buf2, 512, ','); /* Reads until the first token. */

Как только вы вставите строку текста в строку, вы можете использовать любую функцию синтаксического анализа, какую захотите, даже sscanf (buf.c_str (), "% d,% d '% d", & i1, & i2, & i3) с помощью atoi на подстроке с целым числом или с помощью другого метода.

Вы также можете игнорировать нежелательные символы во входном потоке, если знаете, что они есть:

if (cin.peek() == ',')
    cin.ignore(1, ',');
cin >> nextInt;  
0 голосов
/ 06 ноября 2008

Если вы хотите иметь возможность масштабировать до более сложных форматов ввода, вам следует подумать о духе, улучшить библиотеку синтаксических анализаторов.

На этой странице есть пример, который почти делает то, что вам нужно (хотя с реалами и одним вектором)

...