lecture fichier réel/entier mélanger

Le problème exposé dans ce sujet a été résolu.

Bonjours,

je souhaite lire un fichier texte dans lequel sont mélanger des entiers et des réel. Je fais cela de façon suivante :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
 while (  fgets(buf, CACHE_SIZE, F) != NULL)
{
     i = 1;
     while (buf[i] != 0 && buf[i] != '\n') 
     {
           j = 0;
           while(buf[i] != '>' && buf[i] != ',')
           {
                buf2[j] = buf[i];
                j++;
                i++;
           }

           poid = atof(buf2);

           poids.push_back(poid);

           i++;
     }
}

seulement, après quelques essaie je me suis aperçu que les réels de ma liste se retrouvent avec des chiffres aléatoire après la virgule (0 deviens par exemple 0.950001 ou 0.800002 au lieu de 0.000000 comme je m’y attendais).

Est-il possible d’empêcher atof (car je suppose que le problème viens de la) de rajouter autre chose que des 0 à la fin de mon nouveau flottant?

+0 -0

J’ai du mal à cerner ce que fait ton code concrètement. Quel est ton but ? À quoi ressemble ton fichier texte ?

Tu as mis le tag [c++], cependant en voyant fgets, atof, NULL et des tableau C-style, je vois plutôt du langage C. Donc par défaut, si tu veux lire un float dans un fichier en C++ c’est std::ifstream >>, sinon en C c’est fscanf.

oui, en effet ayant l’habitude du c et maîtrisant peu le c++ j’ai tendance à mélanger les deux quand j’utilise ce dernier, il s’agit d’une mauvaise habitude.

je n’ai mis qu’une partie du code de la fonction afin de limiter la lecture (la fonction fait prés de 80 lignes), j’ai vérifier que le problème venais bien de cette partie du code. Le but est de lire un fichier qui à cette forme :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#couche 0 :
< 0.549998, 1.100000, 0.100003> 1
< 0.399998, 1.000002, 0.950001> 0
< 1.350003, 0.700001, 1.349998> 1

#couche 1 :
< 0.450001, 0.850000, 0.450000> 0.800001
< 1.500003, 0.600001, 0.749999> 0.850001
< 0.600002, 1.050004, 0.650000> 0
< 0.750000, 0.500004, 1.000001> 1.050001
< 0.950000, 0.850001, 0.800002> 0

...

mon but est de récupérer les différentes valeurs contenue dans ce fichier (elles correspondent au poids et au seuil d’activations de neurones). Je ne connais pas la structure exacte du fichier (je sais combien il y a de couche et combien de neurones ( = ligne dans le fichier) mais je ne connais pas le nombre de poids attaché à chaque neurone. ce sont les chevrons qui m’indiquent le début et la fin de la liste des poids. je ne sais pas non plus ou se trouvent des entiers ou des réel). pour les récupérer je stocke une ligne à la fois (un neurone dans mon modèle) dans un buffer (buf[], qui est un tableau de char) puis je récupère les données qui m’intéressent depuis ce buffer.

je ne connaissais pas std::ifstream, je vais regarder sa, merci. Pour ce qui est d’atof il me semble que c’est bien une fonction du c++ (en tout cas j’ai trouver de la doc c++ pour cette fonction) ?

edit : si je comprend bien comment marche istream je devrais fonctionner de la même manière (c’est a dire en copiant les données dans un buffer puis en les récupérant après) puisque je dois les tester au passage (pour détecter les chevrons et éliminer les virgules) ? Si il s’agit bien de cela sa ne me permettra à priori pas de résoudre mon problème (bien que j’ai conscience que mon code sera pus dans l’esprit du c++) ? Dans tout les cas je ferais la migration demain.

+0 -0

Ok, donc je pars du principe que tu comptes bel et bien coder en C++, alors autant ne pas se priver de ses avantages. :) (Même si tu as déjà résolu ton problème.)

En fait, l’avantage d’un std::istream, c’est qu’il permet de lire et interpréter automatiquement les données, par exemple :

1
2
3
std::ifstream file("a.txt");
float f;
file >> f; // lit le premier nombre du fichier et le stocke dans un float

Du coup, plus besoin de s’ennuyer avec atof et compagnie. Le problème, c’est qu’il lit les nombres séparés par un espace. Or ton fichier est dans un format plus spécifique, et cela ne fonctionnera pas aussi simplement.

Ton code est loin d’exploiter les avantages du C++ (et notamment de sa bibliothèque standard). Trois boucles imbriquées pour simplement lire des valeurs dans un fichier, il y a de quoi se perdre. Aussi, en C++ on utilise std::stof et non plus atof qui est une fonction héritée de la bibliothèque C.

Reprenons le problème de base : on veut extraire tous les nombres réels se trouvant entre des chevrons.

Il y a forcément plusieurs moyen d’y parvenir. À mon avis, le plus simple est d’utiliser une expression régulière. Par exemple :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// on stocke tout le contenu du fichier dans "buf"
std::istreambuf_iterator<char> file_beg(file);
std::istreambuf_iterator<char> file_end{};
std::string buf(file_beg, file_end);

// on définit notre regex
std::regex reg("[\\d.]+(?=.*>)");

// on définit des itérateurs pour parcourir les correspondances
std::sregex_iterator reg_beg(buf.begin(), buf.end(), reg);
std::sregex_iterator reg_end{};

// on définit notre tableau des poids
std::vector<float> poids;

// on transforme les correspondances en float (std::stof)
// et on les insert à la fin de "poids" (std::back_inserter)
std::transform(reg_beg, reg_end, std::back_inserter(poids),
    [](auto match){ return std::stof(match.str()); });

Ce code fait la même chose que le tiens à la sauce C++, mais en plus de ça, il tient sur cinq lignes et ne comporte aucune boucle. ^^

Note : la regex fonctionne, mais elle pourrait être un peu plus peaufinée.

+0 -0

en effet ton code est beaucoup plus simple et je vais certainement m’en inspirer pour réécrire le miens. par contre charger la totalité du fichier en mémoire m’est difficilement possible (le fichier peut faire plusieurs Go).

Merci beaucoup, notamment pour la regex à laquelle je n’avais pas du tout penser.

[…] par contre charger la totalité du fichier en mémoire m’est difficilement possible (le fichier peut faire plusieurs Go).

pantadora

En effet, si la taille du fichier est considérablement grande, il vaut peut-être mieux le lire ligne par ligne. Le processus reste le même :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
std::vector<float> poids;
std::regex reg("[\\d.]+(?=.*>)");

std::string line;
while(std::getline(file, line))
{
    std::sregex_iterator reg_beg(line.begin(), line.end(), reg);
    std::sregex_iterator reg_end{};

    std::transform(reg_beg, reg_end, std::back_inserter(poids),
        [](auto match){ return std::stof(match.str()); });
}
+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte