Лучший способ прочитать весь файл, открытый в текстовом режиме, в строковую переменную - PullRequest
5 голосов
/ 12 декабря 2010

Это то, что я не могу изменить:

  • Язык - C ++
  • Однако файл открывается со старым добрым fopen()
  • Файл не открывается в двоичном режиме

Вот что я должен сделать:

  • Напишите функцию, которая загружает весь файл в std::string. Строки должны быть разделены только \n, а не другими вариантами.

Вот что я сделал:

string ReadWhole()
{
    Seek(0);
    char *data = new char[GetSize()];

    if (1 != fread(data, GetSize(), 1, mFile))
        FATAL(Text::Format("Read error: {0}", strerror(errno)));

    string ret(data, GetSize());
    delete[] data;
    return ret;
}

Для справки: GetSize, но он просто возвращает размер файла (в кеше):

int GetSize()
{
    if (mFileSize)
        return mFileSize;

    const int current_position = ftell(mFile);
    fseek(mFile, 0, SEEK_END);
    mFileSize = ftell(mFile);
    fseek(mFile, current_position, SEEK_SET);

    return mFileSize;
}

Это проблема

fread() завершается неудачно, потому что файл имеет \r\n окончания строк, и они считаются только одним символом вместо 2, поэтому он пытается прочитать больше символов в файле.

Я мог бы исправить это с помощью fgets, но мне было интересно, есть ли лучший способ. Спасибо.

Ответы [ 4 ]

3 голосов
/ 12 декабря 2010

После того, как fread вернет, что не смог прочитать запрошенное количество байтов, вы должны просто проверить ferror(mFile). Если это 0 (false), то fread просто остановился в конце файла, и вы не должны воспринимать это как ошибку. Вы должны переключить два аргумента, чтобы вы могли получить фактически прочитанное число байтов:

size_t number_of_bytes_read = fread(data, 1, GetSize(), mFile);
2 голосов
/ 12 декабря 2010

Существует тривиальный идиоматический способ выполнения этой операции.

#include <string>
#include <fstream>
#include <sstream>
std::string load_file ( const std::string& path ) 
{
    std::ostringstream contents;
    std::ifstream file(path);
    if ( !file.is_open() ) {
        // process error.
    }
    contents << file.rdbuf();
    return (contents.str());
}

Примечание : эта функция не использует поиск размера (вбайт) входного файла.Это имеет обратную сторону (несколько) перераспределений для увеличения буфера, поскольку больше входных данных становится доступным.Он имеет преимущество при работе с другими std::istream реализациями, которые могут не иметь возможности заранее предоставить размер содержимого (т. Е. Чтение из сокета).

Редактировать : так как ваши требования требуют использования FILE*, который уже открыт и вы не можете изменить, вы можете реализовать реализацию std::streambuf, которая использует существующий FILE*, чтобы разрешитьповторное использование операций высокого уровня std::istream и std::ostream.

Пример реализации доступен прямо здесь, в StackOverflow .

PS : Если вы никогда не использовали реализации потокового буфера нестандартной библиотеки, вот краткий обзор того, как написать функцию с учетом реализации, на которую я указывал.

#include <string>
#include <istream>
#include <sstream>
#include "FILEbuf.h"
std::string load_file ( ::FILE * opened_c_file ) 
{
    FILEbuf buffer(opened_c_file);
    std::istream file(&buffer);
    std::ostringstream contents;
    contents << file.rdbuf();
    return (contents.str());
}
0 голосов
/ 12 декабря 2010

Просто используйте fgetc() для чтения по одному символу за раз.Вы можете использовать особый случай для преобразования окончаний '\ r \ n' в простые значения \ n.

std::string ReadWhole() {
    std::string ret;
    char prev = 0, c;
    while ((c = fgetc(mFile)) != EOF) {
        if (prev == '\r' && c == '\n') {
            ret.erase(ret.rend()); // erase the previous \r
        }
        ret += c;
        prev = c;
    }
    return ret;
}
0 голосов
/ 12 декабря 2010

Вы можете выделить буфер фиксированного размера и многократно fread максимум из файла и добавить это к строке с помощью string::apeend(char*, size_type).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...