HOWTO · C++

Lire le fichier en chaîne en C++

Cet article montre comment lire un fichier en chaîne de caractères en C++

Sur cette page

Pour lire un fichier dans un std::string, utilisez std::ifstream avec std::getline si vous souhaitez traiter les lignes, ou construisez la chaîne à partir de std::istreambuf_iterator<char> pour récupérer le flux entier. Les exemples ci-dessous créent leur propre fichier d’entrée temporaire, afin que chaque programme puisse être compilé et exécuté séparément.

Utiliser std::ifstream et std::getline pour lire un fichier dans une chaîne en C++

std::getline supprime le délimiteur de chaque ligne. Ajouter "\n" après chaque appel réussi rétablit les séparateurs, mais ajoute aussi un saut de ligne après la dernière ligne, même si le fichier source n’en contenait pas. Si cette différence est importante, vérifiez la présence d’un délimiteur ou utilisez l’une des méthodes qui lit le flux entier présentées ci-dessous.

Cet exemple complet écrit deux lignes dans un fichier temporaire, l’ouvre avec std::ifstream et ajoute chaque ligne à une chaîne. Le texte qui précède l’exemple décrit son fonctionnement normal lorsque tout se passe bien.

#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>

int main() {
    const auto path = std::filesystem::temp_directory_path() / "cpp-string-lines.txt";
    {
        std::ofstream sample(path);
        sample << "alpha\nbeta";
        if (!sample) {
            std::cout << "Could not create sample file\n";
            return 0;
        }
    }

    std::ifstream inputFile(path);
    if (!inputFile) {
        std::cout << "Could not open sample file\n";
        std::filesystem::remove(path);
        return 0;
    }

    std::string fileContent;
    std::string line;
    while (std::getline(inputFile, line)) {
        fileContent += line + "\n";
    }
    if (inputFile.bad()) {
        std::cout << "Read failed\n";
        inputFile.close();
        std::filesystem::remove(path);
        return 0;
    }

    std::cout << fileContent;
    inputFile.close();
    std::filesystem::remove(path);
}

La boucle s’arrête lorsque std::getline ne peut plus extraire de ligne. Vérifier bad() permet de distinguer une erreur d’E/S de bas niveau de la fin normale du flux. Par défaut, le fichier est ouvert en mode texte ; sur les systèmes qui convertissent les fins de ligne, les octets de la chaîne obtenue peuvent différer de ceux enregistrés dans le fichier. Utilisez le mode binaire si vous devez préserver les octets à l’identique.

Utiliser istreambuf_iterator pour lire un fichier dans une chaîne en C++

Pour récupérer le flux entier, construisez une chaîne à partir d’une paire de valeurs std::istreambuf_iterator<char>. Le premier itérateur lit depuis le tampon du flux et l’itérateur construit par défaut marque la fin. Cette méthode lit les données sans les découper en lignes et préserve donc les caractères de nouvelle ligne transmis par le flux.

#include <filesystem>
#include <fstream>
#include <iostream>
#include <iterator>
#include <string>

int main() {
    const auto path = std::filesystem::temp_directory_path() / "cpp-string-iterator.txt";
    {
        std::ofstream sample(path, std::ios::binary);
        sample << "alpha\nbeta";
        if (!sample) {
            std::cout << "Could not create sample file\n";
            return 0;
        }
    }

    std::ifstream inputFile(path, std::ios::binary);
    if (!inputFile) {
        std::cout << "Could not open sample file\n";
        std::filesystem::remove(path);
        return 0;
    }

    const std::string fileContent{
        std::istreambuf_iterator<char>(inputFile),
        std::istreambuf_iterator<char>()};
    if (inputFile.bad()) {
        std::cout << "Read failed\n";
    } else {
        std::cout << fileContent << '\n';
    }
    inputFile.close();
    std::filesystem::remove(path);
}
alpha
beta

Utilisez cette forme si le fichier entier tient en mémoire et qu’un traitement ligne par ligne n’est pas nécessaire. L’ouverture en mode binaire évite la conversion des fins de ligne propre au mode texte. Un std::string peut contenir des octets nuls, mais les fonctions qui traitent c_str() comme une chaîne de texte terminée par un caractère nul peuvent s’arrêter au premier d’entre eux.

Utiliser rdbuf pour lire un fichier dans une chaîne en C++

Une autre possibilité offerte par la bibliothèque standard consiste à insérer le rdbuf() du flux d’entrée dans un std::ostringstream, puis à appeler str() pour obtenir son contenu sous forme de std::string. Cette solution est pratique lorsque le flux de chaîne fait déjà partie du code environnant. Après le transfert, vérifiez l’état du flux d’entrée si vous devez signaler les erreurs de lecture.

#include <filesystem>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>

int main() {
    const auto path = std::filesystem::temp_directory_path() / "cpp-string-rdbuf.txt";
    {
        std::ofstream sample(path, std::ios::binary);
        sample << "alpha\nbeta";
        if (!sample) {
            std::cout << "Could not create sample file\n";
            return 0;
        }
    }

    std::ifstream inputFile(path, std::ios::binary);
    if (!inputFile) {
        std::cout << "Could not open sample file\n";
        std::filesystem::remove(path);
        return 0;
    }

    std::ostringstream stream;
    stream << inputFile.rdbuf();
    if (inputFile.bad()) {
        std::cout << "Read failed\n";
    } else {
        const std::string fileContent = stream.str();
        std::cout << fileContent << '\n';
    }
    inputFile.close();
    std::filesystem::remove(path);
}

L’insertion copie les caractères du tampon du fichier dans le flux de chaîne ; str() renvoie la chaîne accumulée. Pour construire directement une chaîne à partir du fichier entier, la méthode avec les itérateurs comporte moins d’étapes. Dans les deux cas, il reste nécessaire de vérifier que le fichier s’est ouvert et qu’aucune erreur de lecture ne s’est produite.

Utiliser la fonction fread pour lire un fichier dans une chaîne

fread est une fonction de la bibliothèque standard C que l’on peut utiliser en C++. Elle est utile si le code utilise déjà des flux FILE* ou nécessite une API C orientée octets. Elle n’est pas intrinsèquement plus rapide que les méthodes fondées sur les flux C++ ; les performances dépendent de la charge de travail et de l’implémentation.

Cet exemple utilise fseek et ftell pour déterminer la taille d’un fichier d’exemple pouvant être parcouru, alloue une chaîne de cette taille, puis y lit les octets. Pour les flux non parcourables ou les fichiers trop volumineux pour tenir en mémoire, lisez plutôt des blocs de taille fixe. La taille du fichier peut changer entre sa mesure et sa lecture ; il faut donc aussi vérifier le nombre d’octets renvoyé et ferror.

#include <cstdio>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>

int main() {
    const auto path = std::filesystem::temp_directory_path() / "cpp-string-fread.txt";
    {
        std::ofstream sample(path, std::ios::binary);
        sample << "alpha\nbeta";
        if (!sample) {
            std::cout << "Could not create sample file\n";
            return 0;
        }
    }

    std::FILE* file = std::fopen(path.string().c_str(), "rb");
    if (file == nullptr) {
        std::cout << "Could not open sample file\n";
        std::filesystem::remove(path);
        return 0;
    }

    if (std::fseek(file, 0, SEEK_END) != 0) {
        std::cout << "Could not seek sample file\n";
        std::fclose(file);
        std::filesystem::remove(path);
        return 0;
    }
    const long length = std::ftell(file);
    if (length < 0 || std::fseek(file, 0, SEEK_SET) != 0) {
        std::cout << "Could not determine sample size\n";
        std::fclose(file);
        std::filesystem::remove(path);
        return 0;
    }

    std::string content(static_cast<std::size_t>(length), '\0');
    const std::size_t bytesRead = std::fread(content.data(), 1, content.size(), file);
    const bool failed = std::ferror(file) != 0 || bytesRead != content.size();
    std::fclose(file);
    std::filesystem::remove(path);

    if (failed) {
        std::cout << "Read failed or file changed while reading\n";
        return 0;
    }
    std::cout << content << '\n';
}

Le troisième argument indique le nombre d’éléments et le deuxième leur taille ; avec une taille d’élément de 1, la valeur renvoyée correspond au nombre d’octets. fread peut renvoyer moins d’octets que demandé : ne supposez donc pas qu’un seul appel remplira le tampon. Cet exemple concerne un fichier parcourable et utilise la plage de valeurs long prise en charge par ftell ; les applications qui doivent gérer des fichiers plus volumineux devraient recourir aux API adaptées aux grands fichiers de la plateforme ou lire par blocs. La chaîne stocke les octets sans ajouter de caractère nul de terminaison pour les fonctions qui manipulent des chaînes C.

Utiliser read pour lire un fichier dans une chaîne

La fonction read de POSIX travaille avec un descripteur de fichier et un tampon fourni par l’appelant. Elle ne fait pas partie du C++ standard ; incluez les en-têtes POSIX appropriés et utilisez cette méthode uniquement sur les plateformes qui fournissent cette API. Un appel réussi peut renvoyer moins d’octets que demandé : répétez les appels jusqu’à la fin du fichier et gérez les interruptions et les erreurs.

Le programme POSIX ci-dessous lit par blocs, sans s’appuyer sur une mesure préalable de la taille du fichier. Ses appels système ne sont pas disponibles sur toutes les plateformes C++ : considérez-le comme un exemple POSIX, et non comme du C++ portable.

#include <cerrno>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
#include <fcntl.h>
#include <unistd.h>

int main() {
    const auto path = std::filesystem::temp_directory_path() / "cpp-string-read.txt";
    {
        std::ofstream sample(path, std::ios::binary);
        sample << "alpha\nbeta";
        if (!sample) {
            std::cout << "Could not create sample file\n";
            return 0;
        }
    }

    const int fd = ::open(path.c_str(), O_RDONLY);
    if (fd < 0) {
        std::cout << "Could not open sample file\n";
        std::filesystem::remove(path);
        return 0;
    }

    std::string content;
    char buffer[4096];
    bool failed = false;
    for (;;) {
        const ssize_t count = ::read(fd, buffer, sizeof buffer);
        if (count > 0) {
            content.append(buffer, static_cast<std::size_t>(count));
        } else if (count == 0) {
            break;
        } else if (errno != EINTR) {
            failed = true;
            break;
        }
    }
    if (::close(fd) != 0) {
        failed = true;
    }
    std::filesystem::remove(path);

    if (failed) {
        std::cout << "Read or close failed\n";
        return 0;
    }
    std::cout << content << '\n';
}

Pour un fichier ordinaire, read renvoie zéro à la fin du fichier. Pour les autres types de descripteurs, le comportement dépend du périphérique ou du flux. Chaque valeur de retour positive indique le nombre d’octets à ajouter ; une valeur négative signale une erreur, et EINTR signifie que l’appel peut être réessayé. Fermez toujours le descripteur et tenez compte des lectures partielles : un seul appel ne remplit pas nécessairement tout le tampon demandé. L’exemple utilise std::string::append pour préserver le nombre exact d’octets, y compris les éventuels octets nuls.

Conclusion

En C++ portable, choisissez std::istreambuf_iterator ou rdbuf() pour convertir le flux entier en chaîne, et utilisez std::getline si un traitement ligne par ligne est utile. Gardez à l’esprit que l’extraction des lignes supprime les délimiteurs et que les ajouter peut modifier une dernière ligne qui n’était pas terminée par un saut de ligne. Utilisez fread si une interface C FILE* convient au code environnant, et read de POSIX uniquement si son API de descripteurs propre à la plateforme est appropriée. Pour les fichiers volumineux, envisagez de les traiter par blocs au lieu de les conserver entièrement en mémoire, et vérifiez les résultats de l’ouverture comme ceux de la lecture.