Какой самый элегантный способ прочитать текстовый файл с C ++? - PullRequest
56 голосов
/ 12 октября 2008

Я бы хотел прочитать все содержимое текстового файла в объект std::string с помощью c ++.

С Python я могу написать:

text = open("text.txt", "rt").read()

Это очень просто и элегантно. Я ненавижу уродливые вещи, поэтому я хотел бы знать - каков самый элегантный способ чтения текстового файла с C ++? Спасибо.

Ответы [ 5 ]

123 голосов
/ 12 октября 2008

Есть много способов, вы выбираете, какой самый элегантный для вас.

Чтение в символ *:

ifstream file ("file.txt", ios::in|ios::binary|ios::ate);
if (file.is_open())
{
    file.seekg(0, ios::end);
    size = file.tellg();
    char *contents = new char [size];
    file.seekg (0, ios::beg);
    file.read (contents, size);
    file.close();
    //... do something with it
    delete [] contents;
}

Into std :: string:

std::ifstream in("file.txt");
std::string contents((std::istreambuf_iterator<char>(in)), 
    std::istreambuf_iterator<char>());

В вектор :

std::ifstream in("file.txt");
std::vector<char> contents((std::istreambuf_iterator<char>(in)),
    std::istreambuf_iterator<char>());

В строку, используя stringstream:

std::ifstream in("file.txt");
std::stringstream buffer;
buffer << in.rdbuf();
std::string contents(buffer.str());

file.txt - это просто пример, все отлично работает и для двоичных файлов, просто убедитесь, что вы используете ios :: binary в конструкторе ifstream.

11 голосов
/ 12 октября 2008

Есть другая тема на эту тему.

Мои решения из этой темы (обе однострочные):

Ницца (см. Второе решение Милана):

string str((istreambuf_iterator<char>(ifs)), istreambuf_iterator<char>());

и быстро:

string str(static_cast<stringstream const&>(stringstream() << ifs.rdbuf()).str());
4 голосов
/ 12 октября 2008

Кажется, вы говорите об элегантности как об определенном свойстве «маленького кода». Это, конечно, субъективно в некоторой степени. Кто-то скажет, что пропуск обработки ошибок не очень элегантен. Кто-то скажет, что понятный и компактный код, который вы понимаете сразу, элегантен.

Напишите свою собственную однострочную функцию / метод, которая читает содержимое файла, но делает его строгим и безопасным под поверхностью, и вы охватите оба аспекта элегантности.

Всего наилучшего

/ Robert

1 голос
/ 23 сентября 2010

Но имейте в виду, что строка c ++ (или более конкретная: строка STL) так же мала, как и строка C, способная содержать строку произвольной длины - конечно, нет!

Взгляните на член max_size (), который дает вам максимальное количество символов, которое может содержать строка. Это номер, определяемый реализацией, и он не может быть переносимым между различными платформами. Visual Studio дает около 4 гигабайт для строк, другие могут дать вам только 64 КБ, а на 64-битных платформах это может дать вам что-то действительно огромное! Это зависит и, конечно, обычно вы столкнетесь с ошибкой bad_alloc из-за исчерпания памяти задолго до достижения предела в 4 гигабайта ...

Кстати: max_size () также является членом других STL-контейнеров! Он даст вам максимальное количество элементов определенного типа (для которого вы создали экземпляр контейнера), которое этот контейнер (теоретически) сможет хранить.

Итак, если вы читаете из файла неизвестного происхождения, вам следует:
- Проверьте его размер и убедитесь, что он меньше, чем max_size ()
- Поймать и обработать bad_alloc-исключения

И еще один момент: Почему вы заинтересованы в чтении файла в строку? Я ожидал бы дальнейшей обработки, постепенно анализируя это или кое-что, правильно? Таким образом, вместо того, чтобы читать это в строку, вы могли бы также прочитать это в поток строки (который в основном является просто синтаксическим сахаром для строки) и выполнить обработку. Но тогда вы можете выполнить обработку непосредственно из файла. Потому что при правильном программировании поток строк может быть легко заменен потоком файлов, т.е. е. самим файлом. Или с помощью любого другого входного потока, они все используют одни и те же члены и операторы и, таким образом, могут быть легко заменены!

И для самой обработки: компилятором также можно автоматизировать многое! Например скажем, вы хотите токенизировать строку. При определении правильного шаблона выполняются следующие действия:
- Чтение из файла (или строки, или любого другого входного потока)
- токенизация контента
- положить все найденные токены в STL-контейнер
- сортировать токены по алфавиту
- уничтожение любых двойных значений
все (!!) может быть достигнуто в одной (!) строке C ++ - кода (не говоря уже о самом шаблоне и обработке ошибок)! Это всего лишь один вызов функции std :: copy ()! Просто поищите в Google «итератор токенов», и вы поймете, что я имею в виду. Так что мне кажется, что это даже более "элегантно", чем просто чтение из файла ...

0 голосов
/ 12 октября 2008

Мне нравится миланский символ *, но с std :: string.


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

string& getfile(const string& filename, string& buffer) {
    ifstream in(filename.c_str(), ios_base::binary | ios_base::ate);
    in.exceptions(ios_base::badbit | ios_base::failbit | ios_base::eofbit);
    buffer.resize(in.tellg());
    in.seekg(0, ios_base::beg);
    in.read(&buffer[0], buffer.size());
    return buffer;
}

int main(int argc, char* argv[]) {
    if (argc != 2) {
        cerr << "Usage: this_executable file_to_read\n";
        return EXIT_FAILURE;
    }
    string buffer;
    cout << getfile(argv[1], buffer).size() << "\n";
}

(с или без ios_base :: binary, в зависимости от того, хотите ли вы переводить новые строки или нет. Вы также можете изменить getfile так, чтобы он просто возвращал строку, чтобы вам не приходилось передавать строку буфера. Затем проверьте чтобы увидеть, оптимизирует ли компилятор копию при возврате.)

Однако это может выглядеть немного лучше (и намного медленнее):


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

string getfile(const string& filename) {
    ifstream in(filename.c_str(), ios_base::binary);
    in.exceptions(ios_base::badbit | ios_base::failbit | ios_base::eofbit);
    return string(istreambuf_iterator<char>(in), istreambuf_iterator<char>());
}

int main(int argc, char* argv[]) {
    if (argc != 2) {
        cerr << "Usage: this_executable file_to_read\n";
        return EXIT_FAILURE;
    }
    cout << getfile(argv[1]).size() << "\n";
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...