Як прочитати весь потік у std :: рядок?


77

Я намагаюся прочитати цілий потік (кілька рядків) у рядок.

Я використовую цей код, і він працює, але це ображає моє почуття стилю ... Напевно, є простіший спосіб? Може, за допомогою струнних потоків?

void Obj::loadFromStream(std::istream & stream)
{ 
  std::string s;

  std::streampos p = stream.tellg();  // remember where we are

  stream.seekg(0, std::ios_base::end); // go to the end
  std::streamoff sz = stream.tellg() - p;  // work out the size
  stream.seekg(p);        // restore the position

  s.resize(sz);          // resize the string
  stream.read(&s[0], sz);  // and finally, read in the data.


Насправді, constпосилання на рядок також буде корисним, і це може полегшити ситуацію ...

const std::string &s(... a miracle occurs here...)

Відповіді:


119

Як щодо

std::istreambuf_iterator<char> eos;
std::string s(std::istreambuf_iterator<char>(stream), eos);

(може бути однокласник, якби не MVP)

після редагування після 2011 року, цей підхід тепер пишеться

std::string s(std::istreambuf_iterator<char>(stream), {});

2
Він по- , як і раніше може бути один лайнер , якщо ви хочете: string s = string(...).
Mike Seymour

1
Дякую. Чи можете ви детальніше розповісти, що це робить? Чи не потрібно eos якось ініціалізувати?
Roddy

13
@Roddy: рядок визначається діапазоном з istreambuf_iterator, який перебирає неформатовані символи, поки не стане рівним вбудованому ітератору введення за замовчуванням, він же "кінець потоку". Див. Скотт Мейєрс, Ефективний пункт STL 29: Розгляньте istreambuf_iterators для введення символів за символами
Куббі

1
Взято з документації std :: istream_iterator "Примітки. При читанні символів std :: istream_iterator за замовчуванням пропускає пробіли (якщо не вимкнено std :: noskipws або еквівалент), а std :: istreambuf_iterator - ні. Крім того, std :: istreambuf_iterator є більш ефективним, оскільки дозволяє уникнути накладних витрат на побудову та руйнування об'єкта охорони один раз на символ. "
Paul Solt

Читання байта за байтом є не зовсім ефективним з точки зору продуктивності POV. Чи є краще рішення, яке читається більшими шматками? Можливо, користуючись перевагами SSE?
ajeh,

27

Я запізнююсь на вечірку, але ось досить ефективне рішення:

std::string gulp(std::istream &in)
{
    std::string ret;
    char buffer[4096];
    while (in.read(buffer, sizeof(buffer)))
        ret.append(buffer, sizeof(buffer));
    ret.append(buffer, in.gcount());
    return ret;
}

Я зробив порівняльний аналіз, і виявляється, що std::istreambuf_iteratorтехніка ( використовувана прийнятою відповіддю ) насправді набагато повільніша. У gcc 4.4.5 with -O3це приблизно 4,5-кратна різниця на моїй машині, і розрив стає більшим із меншими налаштуваннями оптимізації.


5
Дійсно, ефективніший за мою відповідь, оскільки це було б належним блоковим прочитанням. OP хотів "легкий" спосіб, який часто протилежний "швидкому".
Куббі

8
Використання string :: reserve (size_t) зробить це ще більш ефективним.
Тім

Джої, оптимізуй за допомогою -O2. Варіант -O3 не для найшвидшого, а для компактного коду, як я пам’ятаю.
Barney Szabolcs

2
@BarnabasSzabolcs: -Os для компактного коду, -O3 для агресивної оптимізації, тоді як -O2 менш агресивний. Див. Gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
Джої Адамс,

У деяких випадках потрібно експериментувати з різними налаштуваннями. Деякі оптимізації в певних випадках зменшують швидкість. (Мій коментар щодо O2 / O2 також помилковий, базуючись на цьому аргументі.) Див., Наприклад, stackoverflow.com/questions/19470873/…
Барні Саболч

20

Ви могли б це зробити

std::string s;
std::ostringstream os;
os<<stream.rdbuf();
s=os.str();

але я не знаю, чи це ефективніше.

Альтернативна версія:

std::string s;
std::ostringstream os;
stream>>os.rdbuf();
s=os.str();

1
Дякую. як рішення я вважаю це справді простим і читабельним, і я його використовую. Однак я прийняв відповідь Куббі, оскільки багато чому навчився!
Родді

Так, це єдиний метод "читання до eofbit". Інші методи ( istream::get(streambuf*)і std::getline(istream, string)) читаються лише до заданого символу роздільника.
Tanz87

11

Ви можете спробувати використовувати щось із алгоритмів. Я маю підготуватися до роботи, але ось дуже швидкий удар у справи (повинен бути кращий спосіб):

copy( istreambuf_iterator<char>(stream), istreambuf_iterator<char>(), back_inserter(s) );

0

Добре, якщо ви шукаєте простий і зрозумілий спосіб зробити це. Я рекомендую додати / використати якісь рамки високого рівня для вашого проекту. Для цього я завжди використовую Poco та Boost у всіх своїх проектах. У цьому випадку з Poco:

    string text;
    FileStream fstream(TEXT_FILE_PATH);
    StreamCopier::copyToString(fstream, text);

0

Як щодо використання getline з роздільником? Наступний код допомагає мені прочитати цілий std :: cin у рядок на ubuntu з g ++ - 10.

#include <iostream>
#include <string>

using namespace std;

int main() {
    string s;

    getline(cin, s, {}); //the whole stream into variable s

    return 0;
}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.