Domanda

Ho un file con i dati elencati come segue:

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

Voglio poter inserire il file e dividerlo in tre array, tagliando tutti gli spazi in eccesso e convertendo ogni elemento in numeri interi.

Per qualche motivo non riesco a trovare un modo semplice per farlo in c ++. L'unico successo che ho avuto è stato quello di inserire ciascuna riga in un array, quindi di regexare tutti gli spazi e quindi di dividerlo. L'intero processo mi ha portato a 20-30 buone righe di codice ed è difficile modificarlo, ad esempio un altro separatore (ad es. Spazio), ecc.

Questo è l'equivalente di Python di quello che vorrei avere in 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()))

La facilità d'uso di fare questo in Python è uno dei motivi per cui mi sono trasferito ad esso in primo luogo. Tuttavia, ora devo farlo in C ++ e odio dover usare il mio brutto codice di 20-30 righe.

Qualsiasi aiuto sarebbe apprezzato, grazie!

È stato utile?

Soluzione

Non c'è davvero nulla di sbagliato in fscanf, che probabilmente è la soluzione più veloce in questo caso. Ed è breve e leggibile come il codice 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);

Altri suggerimenti

Non c'è davvero bisogno di usare boost in questo esempio poiché i flussi faranno bene il trucco:

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);
    }
}

Tuttavia, se avessi intenzione di usare boost, preferirei la semplicità di tokenizer to regex ... :)

Qualcosa del tipo:

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));
}

Tranne che per il controllo degli errori.

std :: getline ti permette di leggere una riga di testo e puoi usare un flusso di stringhe per analizzare la singola riga:

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

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

Dopo aver inserito la riga di testo nella stringa, puoi effettivamente utilizzare qualsiasi funzione di analisi desiderata, anche sscanf (buf.c_str (), "% d,% d '% d " ;, & amp; i1, & amp ; i2, & amp; i3), usando atoi sulla sottostringa con il numero intero o tramite qualche altro metodo.

Puoi anche ignorare i caratteri indesiderati nel flusso di input, se sai che sono lì:

if (cin.peek() == ',')
    cin.ignore(1, ',');
cin >> nextInt;  

Se non ti dispiace usare le librerie 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;
}

Che estrae le linee dallo stream, quindi usa la libreria Boost :: RegEx (con un gruppo di acquisizione) per estrarre ogni numero dalle linee. Ignora automaticamente tutto ciò che non è un numero valido, anche se può essere modificato se lo desideri.

Sono ancora una ventina di righe con i #include , ma puoi usarlo per estrarre essenzialmente qualsiasi cosa dalle righe del file. Questo è un esempio banale, sto usando un codice praticamente identico per estrarre tag e valori opzionali da un campo di database, l'unica grande differenza è l'espressione regolare.

EDIT: Oops, volevi tre vettori separati. Prova invece questa leggera modifica:

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

perché non lo stesso codice di 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);
}

Se vuoi essere in grado di ridimensionare in formati di input più difficili, dovresti considerare lo spirito, aumentare la libreria del combinatore parser.

Questa pagina ha un esempio che fa quasi quello di cui hai bisogno (con reals e un vettore però)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top