274 votes

Comment lire un fichier entier dans une chaîne std::string en C++ ?

Comment lire un fichier dans un std::string c'est-à-dire lire tout le fichier en une seule fois ?

Le mode texte ou binaire doit être spécifié par l'appelant. La solution doit être conforme aux normes, portable et efficace. Elle ne doit pas copier inutilement les données de la chaîne et doit éviter les réaffectations de mémoire lors de la lecture de la chaîne.

Une façon d'y parvenir serait de statuer sur la taille des fichiers, de redimensionner les std::string y fread() dans le std::string 's const_cast<char*>() 'ed data() . Cela nécessite le std::string Les données de l'utilisateur sont contiguës, ce qui n'est pas requis par la norme, mais semble être le cas pour toutes les implémentations connues. Pire encore, si le fichier est lu en mode texte, la fonction std::string La taille du fichier peut ne pas être égale à la taille du fichier.

Une solution tout à fait correcte, conforme aux normes et portable pourrait être construite en utilisant std::ifstream 's rdbuf() en un std::ostringstream et de là, dans un std::string . Cependant, cela pourrait copier les données de la chaîne et/ou réallouer inutilement de la mémoire.

  • Toutes les implémentations pertinentes de la bibliothèque standard sont-elles suffisamment intelligentes pour éviter toute surcharge inutile ?
  • Y a-t-il un autre moyen de le faire ?
  • Ai-je manqué une fonction cachée de Boost qui fournit déjà la fonctionnalité souhaitée ?

    void slurp(std::string& data, bool is_binary)

0voto

kiroma Points 91

Une fonction actualisée qui s'appuie sur la solution du CTT :

#include <string>
#include <fstream>
#include <limits>
#include <string_view>
std::string readfile(const std::string_view path, bool binaryMode = true)
{
    std::ios::openmode openmode = std::ios::in;
    if(binaryMode)
    {
        openmode |= std::ios::binary;
    }
    std::ifstream ifs(path.data(), openmode);
    ifs.ignore(std::numeric_limits<std::streamsize>::max());
    std::string data(ifs.gcount(), 0);
    ifs.seekg(0);
    ifs.read(data.data(), data.size());
    return data;
}

Il y a deux différences importantes :

tellg() n'est pas garanti de retourner le décalage en octets depuis le début du fichier. Comme l'a souligné Puzomor Croatia, il s'agit plutôt d'un jeton qui peut être utilisé dans les appels fstream. gcount() cependant fait retourne la quantité d'octets non formatés extraits en dernier. Nous ouvrons donc le fichier, extrayons et jetons tout son contenu avec ignore() pour obtenir la taille du fichier, et construire la chaîne de sortie en fonction de cela.

Deuxièmement, nous évitons d'avoir à copier les données du fichier depuis un std::vector<char> à un std::string en écrivant directement dans la chaîne de caractères.

En termes de performances, cette méthode devrait être la plus rapide, en allouant la chaîne de taille appropriée à l'avance et en appelant read() une fois. Il est intéressant de noter que l'utilisation de ignore() y countg() au lieu de ate y tellg() sur gcc se compile en presque la même chose petit à petit.

0voto

hanshenrik Points 192

C'est la fonction que j'utilise, et lorsqu'on a affaire à de gros fichiers (1GB+), pour une raison quelconque, std::ifstream::read() est beaucoup plus rapide que std::ifstream::rdbuf() lorsque vous connaissez la taille du fichier, donc le fait de "vérifier la taille du fichier en premier" est en fait une optimisation de la vitesse.

#include <string>
#include <fstream>
#include <sstream>
std::string file_get_contents(const std::string &$filename)
{
    std::ifstream file($filename, std::ifstream::binary);
    file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    file.seekg(0, std::istream::end);
    const std::streampos ssize = file.tellg();
    if (ssize < 0)
    {
        // can't get size for some reason, fallback to slower "just read everything"
        // because i dont trust that we could seek back/fourth in the original stream,
        // im creating a new stream.
        std::ifstream file($filename, std::ifstream::binary);
        file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
        std::ostringstream ss;
        ss << file.rdbuf();
        return ss.str();
    }
    file.seekg(0, std::istream::beg);
    std::string result(size_t(ssize), 0);
    file.read(&result[0], std::streamsize(ssize));
    return result;
}

0voto

Vous pouvez utiliser le rst La bibliothèque C++ que j'ai développée pour faire ça :

#include "rst/files/file_utils.h"

std::filesystem::path path = ...;  // Path to a file.
rst::StatusOr<std::string> content = rst::ReadFile(path);
if (content.err()) {
  // Handle error.
}

std::cout << *content << ", " << content->size() << std::endl;

0voto

Ritesh Saha Points 1
#include <string>
#include <fstream>

int main()
{
    std::string fileLocation = "C:\\Users\\User\\Desktop\\file.txt";
    std::ifstream file(fileLocation, std::ios::in | std::ios::binary);

    std::string data;

    if(file.is_open())
    {
        std::getline(file, data, '\0');

        file.close();
    }
}

-1voto

Jason Cohen Points 36475

Notez que certaines choses ne sont toujours pas spécifiées. Par exemple, quel est l'encodage des caractères du fichier ? Allez-vous essayer de le détecter automatiquement (ce qui ne fonctionne que dans quelques cas spécifiques) ? Allez-vous respecter, par exemple, les en-têtes XML qui vous indiquent l'encodage du fichier ?

De même, il n'existe pas de "mode texte" ou de "mode binaire" - pensez-vous à FTP ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X