Question

J'ai un fichier avec les données répertoriées comme suit:

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

Je veux pouvoir entrer le fichier et le diviser en trois tableaux, ce qui permet de réduire tous les espaces superflus et de convertir chaque élément en entier.

Pour une raison quelconque, je ne trouve pas de moyen facile de le faire en c ++. Le seul succès que j'ai eu consiste à entrer chaque ligne dans un tableau, puis à regexer tous les espaces et à les séparer. Tout ce processus m’a pris 20 à 30 lignes de code et c’est difficile à modifier, par exemple pour un autre séparateur (espace, par exemple), etc.

C’est l’équivalent en python de ce que je voudrais avoir en 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’utilisation de cette fonctionnalité en python est l’une des raisons pour lesquelles j’y suis parvenue tout de suite. Cependant, j’ai maintenant besoin de le faire en C ++ et je n’aimerais pas devoir utiliser mon horrible code de ligne de 20-30.

Toute aide serait appréciée, merci!

Était-ce utile?

La solution

Il n'y a vraiment rien de mal à fscanf, qui est probablement la solution la plus rapide dans ce cas. Et il est aussi court et lisible que le code 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);

Autres conseils

Il n'y a pas vraiment besoin d'utiliser boost dans cet exemple car les flux feront très bien l'affaire:

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

Bien que si je devais utiliser boost, je préférerais la simplicité de tokenizer to regex ...:)

Quelque chose comme:

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

Sauf avec vérification d'erreur.

std :: getline vous permet de lire une ligne de texte et vous pouvez utiliser un flux de chaîne pour analyser la ligne individuelle:

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

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

Une fois que vous avez inséré la ligne de texte dans la chaîne, vous pouvez utiliser n'importe quelle fonction d'analyse, même sscanf (buf.c_str (), "% d,% d"% d ", & amp; i1, & amp ; i2, & amp; i3), en utilisant atoi sur la sous-chaîne avec l'entier, ou par un autre moyen.

Vous pouvez également ignorer les caractères non désirés dans le flux d'entrée, si vous savez qu'ils sont présents:

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

Si cela ne vous dérange pas d'utiliser les bibliothèques 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;
}

Cela extrait les lignes du flux, puis utilise la bibliothèque Boost :: RegEx (avec un groupe de capture) pour extraire chaque nombre des lignes. Il ignore automatiquement tout ce qui n’est pas un nombre valide, bien que cela puisse être modifié si vous le souhaitez.

Il reste environ vingt lignes avec les éléments #include , mais vous pouvez l’utiliser pour extraire essentiellement quoi que ce soit à partir des lignes du fichier. Ceci est un exemple trivial, j'utilise un code à peu près identique pour extraire les balises et les valeurs optionnelles d'un champ de base de données, la seule différence majeure est l'expression régulière.

EDIT: Oups, vous vouliez trois vecteurs distincts. Essayez plutôt cette légère modification:

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

pourquoi ne pas utiliser le même code qu'en 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);
}

Si vous souhaitez être en mesure de passer à des formats d’entrée plus difficiles, vous devriez envisager la bibliothèque de combinateur d’analyseur, plus fort.

.

Cette page a un exemple qui fait presque ce dont vous avez besoin (avec des réels et un vecteur cependant)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top