Làm cách nào để đọc toàn bộ luồng thành một chuỗi std ::?


77

Tôi đang cố đọc toàn bộ luồng (nhiều dòng) thành một chuỗi.

Tôi đang sử dụng mã này, và nó hoạt động, nhưng nó làm mất phong cách của tôi ... Chắc chắn có một cách dễ dàng hơn? Có thể sử dụng chuỗi ký tự?

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.


Trên thực tế, một consttham chiếu đến một chuỗi cũng sẽ tốt và điều đó có thể làm cho mọi thứ dễ dàng hơn ...

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

Câu trả lời:


119

Làm thế nào về

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

(có thể là một lớp lót nếu không cho MVP)

chỉnh sửa sau năm 2011, phương pháp này hiện được viết chính tả

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

2
Nó vẫn có thể là một lớp lót nếu bạn muốn: string s = string(...).
Mike Seymour

1
Cảm ơn. Bạn có thể nói rõ hơn về những gì đang làm không? Eos không cần khởi tạo bằng cách nào đó?
Roddy

13
@Roddy: Chuỗi được cấu trúc theo phạm vi từ istreambuf_iterator, sẽ lặp qua các ký tự chưa được định dạng cho đến khi nó trở thành bằng với một trình lặp đầu vào được xây dựng mặc định, hay còn gọi là "end of stream". Xem Scott Meyers, hiệu quả STL mục 29: Xem xét istreambuf_iterators cho nhân vật theo ký tự đầu vào
Cubbi

1
Lấy từ tài liệu std :: istream_iterator "Lưu ý: Khi đọc ký tự, std :: istream_iterator bỏ qua khoảng trắng theo mặc định (trừ khi bị tắt với std :: noskipws hoặc tương đương), trong khi std :: istreambuf_iterator thì không. Ngoài ra, std :: istreambuf_iterator hiệu quả hơn, vì nó tránh được chi phí xây dựng và phá hủy đối tượng lính canh một lần cho mỗi ký tự. "
Paul Solt

Đọc từng byte không hoàn toàn hiệu quả từ POV hiệu suất. Có giải pháp nào tốt hơn mà đọc ở phần lớn hơn không? Có lẽ đang tận dụng SSE?
ajeh

27

Tôi đến muộn bữa tiệc, nhưng đây là một giải pháp khá hiệu quả:

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;
}

Tôi đã thực hiện một số phép đo điểm chuẩn, và hóa ra std::istreambuf_iteratorkỹ thuật ( được sử dụng bởi câu trả lời được chấp nhận ) thực sự chậm hơn nhiều. Trên gcc 4.4.5 với -O3, nó chênh lệch khoảng 4,5 lần trên máy của tôi và khoảng cách trở nên rộng hơn với các cài đặt tối ưu hóa thấp hơn.


5
Quả thực hiệu quả hơn câu trả lời của tôi, như một cách đọc theo khối thích hợp. OP muốn cách "dễ dàng", điều này thường trái ngược với "nhanh".
Cubbi

8
Sử dụng chuỗi :: dự trữ (size_t) sẽ làm cho nó thậm chí còn hiệu quả hơn.
Tim

Joey, tối ưu hóa với -O2. Tùy chọn -O3 không phải dành cho nhanh nhất mà dành cho mã nhỏ gọn như tôi nhớ.
Barney Szabolcs

2
@BarnabasSzabolcs: -Os dành cho mã nhỏ gọn, -O3 là để tối ưu hóa tích cực, trong khi -O2 ít tích cực hơn. Xem gcc.gnu.org/onlineocs/gcc/Optimize-Options.html
Joey Adams

Trong một số trường hợp, bạn cần thử nghiệm với các cài đặt khác nhau. Một số tối ưu hóa trong một số trường hợp nhất định làm giảm tốc độ. (Comment của tôi trên O2 / O2 cũng sai dựa trên lập luận này.) Xem ví dụ stackoverflow.com/questions/19470873/...
Barney Szabolcs

20

Bạn có thể làm

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

nhưng tôi không biết liệu nó có hiệu quả hơn không.

Phiên bản thay thế:

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

1
Cảm ơn. như một giải pháp, tôi thấy điều này thực sự đơn giản và dễ đọc, và tôi đang sử dụng nó. Tuy nhiên, tôi chấp nhận câu trả lời của Cubbi vì tôi đã học được rất nhiều điều từ nó!
Roddy

Có, đây là phương pháp "đọc cho đến khi eofbit" duy nhất. Các phương thức khác ( istream::get(streambuf*)std::getline(istream, string)) chỉ đọc cho đến khi một ký tự phân cách nhất định.
Tanz87

11

Bạn có thể thử sử dụng một cái gì đó từ các thuật toán. Tôi phải sẵn sàng cho công việc nhưng đây là một bản tóm tắt rất nhanh về mọi thứ (phải có một cách tốt hơn):

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

0

Chà, nếu bạn đang tìm kiếm một cách đơn giản và dễ đọc. Tôi khuyên bạn nên thêm / sử dụng một số khuôn khổ cấp cao trong dự án của bạn. Vì vậy, tôi luôn sử dụng Poco và Boost trong tất cả các dự án của mình. Trong trường hợp này, với Poco:

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

0

Điều gì về sử dụng getline với dấu phân cách? Đoạn mã tiếp theo giúp tôi đọc toàn bộ std :: cin thành chuỗi trên ubuntu với g ++ - 10.

#include <iostream>
#include <string>

using namespace std;

int main() {
    string s;

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

    return 0;
}

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.