1
votes

Comment lire un fichier texte et le stocker dans un tableau sans connaître la taille?

Je suis nouveau ici.

Comment lire un fichier texte et le stocker dans un tableau, sans connaître le nombre de données dans le fichier texte?

#include <iostream>
#include <fstream>
using namespace std;

int main(){
    ifstream data;
    int a[100];
    int i=0;

    data.open("test.txt");
    while(data>>a[i]){
        cout << a[i] << endl;
        i++;
    }
    data.close();
}

Dans mon code, la taille du tableau est fixe. Existe-t-il une solution sans utiliser la bibliothèque ? Je peux augmenter la taille du tableau aussi grand que possible, mais cela ne semble pas être une bonne solution.


4 Réponses :


0
votes

L'approche la plus simple serait d'utiliser simplement un std::vector.


Si vous n'êtes pas prêt à le faire, vous devez conceptuellement

  1. Comptez le nombre de données: X
  2. Allouez suffisamment de mémoire pour stocker les éléments de données X .

Quelque chose comme ce qui suit devrait faire l'affaire (Remarque: je n'ai pas testé le code)

#include <iostream>
#include <fstream>
using namespace std;

size_t data_size(const string name)
{
    size_t c = 0;
    ifstream data;
    data.open(name);

     while(data>>a[i])
        c++;

    data.close();
    return c;
}

int main(){
    string name = "test.txt"

    int* a = new int [data_size(name)] ;
    int i=0;

    ifstream data;
    data.open("test.txt");
    while(data>>a[i]){
        cout << a[i] << endl;
        i++;
    }
    data.close();
}


1 commentaires

Pour compter les éléments du flux, vous pouvez std :: distance (std :: istream_iterator (data), std :: istream_iterator {}); Plutôt que d'ouvrir le fichier deux fois simple cherchez au début.




0
votes

Vous pouvez agrandir votre tableau, chaque fois qu'il est trop petit. Utilisez une taille qui, selon vous, convient le mieux et ne l'agrandit que lorsque plus de données sont fournies.

#include <iostream>
#include <fstream>
int main() {
    std::ifstream data;
    int size = 100;
    int* a = new int[size];
    int i = 0;

    data.open("test.txt");
    if (data.is_open()) // check if file is open
        return -1;

    while (data.peek() != EOF) {
        if (i >= size) {
            int old_size = size;
            size *= 2; // double the size, or what ever. just make it bigger
            int* new_array = new int[size]; // create new array with new size
            std::copy(a, a + old_size, new_array); // copy data to new array
            int* tmp1 = a; // assign old array to a tmp pointer, to not lose the memory
            a = new_array; // assign the newly allocated memory to the pointer
            delete[] a; a = nullptr; // delete the old memory
        }
        data >> a[i];
        std::cout << a[i] << '\n';
        i++;
    }
    data.close();
    // after you don't need the data anymore; delete[]
    delete[] a; a = nullptr;
    return 0;
}

Une chose que j'ai apprise à la dure, n'utilisez JAMAIS en utilisant namespace std ; . Vous ouvrez un espace de noms extrêmement grand. Si vous définissez, par accident, une fonction, qui a le même nom que dans std , vous obtiendrez une erreur de compilation, ce qui est très difficile à obtenir, de quoi il s'agit.


Un autre conseil, vous devriez vraiment jeter un œil à std :: vector et à tous les autres conteneurs STL. Ils atténueront une grande partie de la douleur.


15 commentaires

Malheureusement, int copy [size]; n'est pas un C ++ standard. Aussi pourquoi voudriez-vous copier dans un fichier temporaire, puis copier vers la destination. Créez simplement la nouvelle copie de version là-bas, puis publiez l'ancienne version.


Notez également si i == size puis while (data >> a [i]) vient d'écrire une valeur une après la fin de votre mémoire allouée et vous avez écrasé la mémoire qui ne vous appartient pas et a donc invoqué un comportement indéfini.


Mettre a à null ici delete [] a; a = nullptr; est en fait une mauvaise idée. Dans les situations où vous avez une double suppression, l'environnement de débogage ne pourra pas attraper et donc vous avertir de ce type d'erreur de débogage. Appelez simplement delete sur la variable a puis laissez-la tomber hors de portée.


Préférez "\ n" sur std :: endl sauf si vous voulez vraiment forcer un vidage, ce qui n'est généralement pas le cas.


@MartinYork bien, vos deux premiers commentaires, merci pour cela. les deux autres ... je ne suis pas d'accord avec


Définir une valeur sur nullptr ne vous bye en fait rien. Donc c'est un gaspillage. Il existe de nombreuses situations où la version de débogage de la bibliothèque standard sur plusieurs compilateurs détectera en fait l'utilisation incorrecte des pointeurs publiés, donc ne pas la définir sur null vous aidera en fait à trouver des problèmes plutôt qu'à résoudre quoi que ce soit.


Le problème avec std :: endl est le flush supplémentaire. Le problème est que la plupart des programmeurs sont en fait terribles à comprendre quand vider la mémoire tampon et le font toujours au mauvais moment. Si vous voulez forcer un vidage, utilisez toujours un flux sans tampon (std :: cerr !!!). La bonne chose à propos des ruisseaux est qu'ils se rinceront automatiquement quand il est approprié de le faire, forçant une chasse d'eau est un gaspillage. Il n'y a aucune situation où vous avez besoin de forcer un vidage (exception déboguant une erreur).


L'un des plus gros problèmes que nous constatons chez les débutants est que les flux C ++ ne sont pas aussi efficaces que les flux C. Bien sûr, cela est généralement causé par une utilisation incorrecte du flux par le débutant et par un vidage continu du tampon lorsqu'il n'est pas nécessaire. Veuillez jeter un œil à codereview.stackexchange.com . C'est une recommandation courante dans toutes les revues de code C ++ où std :: endl est utilisé.


@MartinYork nulltpr - En attendant, j'ai essayé plusieurs versions de gcc (4.8 - 10.0), clang (3.5 - 10) et Visual Studio Compiler (vs 2013-2019). Jamais je n'ai eu aucune sorte d'erreur ou de mauvaise conduite. Tous les indicateurs communs activés - rien. Je ne peux pas partager vos préoccupations à ce sujet. Vous devriez peut-être partager vos inquiétudes avec Bjarne. Pourquoi? Il est directeur du site Web suivant, où delete ptr; ptr = nullptr; est en effet recommandé. Pourquoi ne supprime-t-il pas null de son opérande? Peut-être ai-je totalement mal lu cette FAQ, mais là, il est explicitement recommandé.


@MartinYork Vous avez peut-être raison à propos de la couleur. Mais est-ce vraiment un problème là-bas? OP ne veut pas optimiser, OP veut avoir un code fonctionnel. S'il s'agit d'optimisation, soyez mon invité. Sinon, je suggérerais de ne pas surcharger les débutants.


@ skratchi.at: onlinegdb.com/SJIV84EnB Exécutez ceci. Affiche que l'environnement de débogage vous avertira de ce type d'erreur. Cela vous aidera à découvrir des erreurs qui, autrement, ne seraient pas détectées et qui entraîneraient plus tard des problèmes de production plus importants.


@ skratchi.at Je dirais que vous êtes le seul à surcharger les débutants en enseignant la méthode incorrecte. Le but est de leur apprendre d'abord la bonne méthode. Ensuite, vous pouvez introduire des fonctionnalités avancées comme le rinçage une fois qu'ils ont compris les bases. Puisque le rinçage se fait automatiquement, c'est quelque chose que nous n'avons pas besoin de présenter aux débutants. Les mauvaises habitudes sont difficiles à changer pour les débutants (comme le montre cette conversation avec vous), nous ne devons donc pas enseigner de mauvaises habitudes pour commencer.


@MartinYork La prochaine fois, donnez simplement quelques références et n'essayez pas de me convaincre avec des arguments louches. Arrêtez d'utiliser std :: endl Ce n'est pas que je ne veux pas pour apprendre de nouvelles choses ... mais pas en prenant le mot de quelqu'un, sans révérence ni preuve!


@ skratchi.at Argument loufoque! Je vous ai indiqué un site plein d'experts C ++ qui recommandent de ne pas l'utiliser. codereview.stackexchange.com


PS. Si vous voulez des performances plutôt que de simplement le faire chug, vous devriez consulter sync_with_stdio



0
votes

Le moyen le plus simple est de compter les éléments du fichier. Ensuite, allouez la bonne quantité de mémoire. Copiez ensuite les données dans cette mémoire.

#include <iterator>
#include <algorithm>

int main()
{
    std::ifstream    data("test.txt");

    std::size_t  count = std::distance(std::istream_iterator<int>(data),
                                       std::istream_iterator<int>{});

    data.fseek(0); // Post C++11 this works as expected in older version you
                   // will need to reset the flags as well.

    int data = new int[count];
    std::copy(std::istream_iterator<int>(data), std::istream_iterator<int>{},
              data);
}


0 commentaires