Cách đọc cho đến khi EOF từ cin trong C ++


80

Tôi đang viết mã một chương trình đọc dữ liệu trực tiếp từ đầu vào của người dùng và tự hỏi làm cách nào tôi có thể (không có vòng lặp) đọc tất cả dữ liệu cho đến khi EOF từ đầu vào chuẩn. Tôi đã xem xét sử dụng cin.get( input, '\0' )nhưng '\0'không thực sự là ký tự EOF, chỉ đọc cho đến khi EOF hoặc '\0', tùy điều kiện nào đến trước.

Hay sử dụng vòng lặp là cách duy nhất để làm điều đó? Nếu vậy, cách tốt nhất là gì?

Câu trả lời:


78

Cách duy nhất bạn có thể đọc một lượng dữ liệu khác nhau stdinlà sử dụng các vòng lặp. Tôi luôn thấy rằng std::getline()chức năng hoạt động rất tốt:

std::string line;
while (std::getline(std::cin, line))
{
    std::cout << line << std::endl;
}

Theo mặc định getline()đọc cho đến một dòng mới. Bạn có thể chỉ định một ký tự kết thúc thay thế, nhưng bản thân EOF không phải là một ký tự nên bạn không thể chỉ thực hiện một lệnh gọi đến getline().


8
Một điều cần cẩn thận ở đây là khi bạn đang đọc từ các tệp đã được nhập vào qua stdin, điều này và hầu hết các câu trả lời được đưa ra sẽ thêm một nguồn cấp dữ liệu dòng bổ sung vào cuối đầu vào của bạn. Không phải tất cả các tệp đều kết thúc bằng một nguồn cấp dữ liệu dòng, nhưng mỗi lần lặp lại của vòng lặp sẽ thêm "std :: endl" vào luồng. Đây có thể không phải là vấn đề trong hầu hết các trường hợp, nhưng nếu tính toàn vẹn của tệp là một vấn đề, điều này có thể quay trở lại với bạn.
Zoccadoum

1
Đây không phải là câu trả lời được ủng hộ nhiều nhất. Xem câu trả lời của wiki cộng đồng bên dưới. Ngoài ra, có thể thấy câu hỏi này: stackoverflow.com/questions/3203452/...
loxaxs

52

Sử dụng vòng lặp:

#include <iostream>
using namespace std;
...
// numbers
int n;
while (cin >> n)
{
   ...
}
// lines
string line;
while (getline(cin, line))
{
   ...
}
// characters
char c;
while (cin.get(c))
{
   ...
}

nguồn


50

Bạn có thể làm điều đó mà không có vòng lặp rõ ràng bằng cách sử dụng trình vòng lặp luồng. Tôi chắc chắn rằng nó sử dụng một số loại vòng lặp nội bộ.

#include <string>
#include <iostream>
#include <istream>
#include <ostream>
#include <iterator>

int main()
{
// don't skip the whitespace while reading
  std::cin >> std::noskipws;

// use stream iterators to copy the stream to a string
  std::istream_iterator<char> it(std::cin);
  std::istream_iterator<char> end;
  std::string results(it, end);

  std::cout << results;
}

3
Đẹp. Đối với "Tôi chắc chắn rằng nó sử dụng một số loại vòng lặp nội bộ" - bất kỳ giải pháp nào cũng vậy.
Michael Burr

Điều này dường như loại bỏ các ký tự dòng mới khỏi đầu vào, ngay cả khi chúng xuất hiện ngay ở giữa.
Multisync

28

Sau khi nghiên cứu giải pháp của KeithB bằng cách sử dụng std::istream_iterator, tôi phát hiện ra std:istreambuf_iterator.

Chương trình kiểm tra để đọc tất cả đầu vào được ghép vào một chuỗi, sau đó viết lại:

#include <iostream>
#include <iterator>
#include <string>

int main()
{
  std::istreambuf_iterator<char> begin(std::cin), end;
  std::string s(begin, end);
  std::cout << s;
}

7
Đây có lẽ nên là câu trả lời kinh điển.
Kerrek SB

2
Bị phản đối. Vậy ưu nhược điểm so với giải pháp của KeithB là gì? Có, bạn đang sử dụng std::istreambuf_iteratorthay vì an std::istream_iterator, nhưng để làm gì?
Multisync

std :: istreambuf_iterator bỏ qua khoảng trắng theo mặc định và có thể nhanh hơn
Sopel

5

Có thể là đơn giản nhất và thường hiệu quả:

#include <iostream>
int main()
{
    std::cout << std::cin.rdbuf();
}

Nếu cần, hãy sử dụng luồng thuộc các loại khác như std::ostringstreambộ đệm thay vì luồng đầu ra tiêu chuẩn ở đây.


các bạn đã biết, nhưng có thể hữu ích đối với một số: std::ostringstream std_input; std_input << std::cin.rdbuf(); std::string s = std_input.str(). bạn có tất cả các đầu vào trong s(cẩn thận với std::stringnếu đầu vào là quá lớn)
ribamar


2

Lưu ý phụ đáng buồn: Tôi quyết định sử dụng C ++ IO để phù hợp với mã dựa trên tăng cường. Từ câu trả lời cho câu hỏi này tôi đã chọn while (std::getline(std::cin, line)). Sử dụng g ++ phiên bản 4.5.3 (-O3) trong cygwin (mintty), tôi nhận được thông lượng 2 MB / s. Microsoft Visual C ++ 2010 (/ O2) đã làm cho nó 40 MB / s cho cùng một mã.

Sau khi viết lại IO thành C thuần túy while (fgets(buf, 100, stdin)), thông lượng đã tăng lên 90 MB / s trong cả hai trình biên dịch được thử nghiệm. Điều đó tạo ra sự khác biệt cho bất kỳ đầu vào nào lớn hơn 10 MB ...


Không cần phải viết lại mã của bạn thành C thuần, chỉ cần thêm một std::ios::sync_with_stdio(false);trước vòng lặp while của bạn. cin.tie(NULL);cũng có thể hữu ích nếu bạn xen kẽ việc đọc và viết.
RD

0
while(std::cin) {
 // do something
}

8
Đây là một thảm họa khủng khiếp của một câu trả lời, vì nó hầu như được đảm bảo dẫn đến sai mã.
Kerrek SB

@KerrekSB tại sao nó hầu như được đảm bảo dẫn đến sai mã?
matusf

1
@ MatúšFerech: Bởi vì nó thực sự gợi ý rằng // do somethingkhông chứa kiểm tra bổ sung giá trị trả về của các hoạt động I / O và kiểm tra mà nó chứa là vô nghĩa.
Kerrek SB

0

Chờ đã, tôi hiểu bạn có đúng không? Bạn đang sử dụng cin để nhập bàn phím và bạn muốn dừng đọc đầu vào khi người dùng nhập ký tự EOF? Tại sao người dùng lại nhập ký tự EOF? Hay ý của bạn là bạn muốn ngừng đọc từ một tệp tại EOF?

Nếu bạn thực sự đang cố gắng sử dụng cin để đọc một ký tự EOF, thì tại sao không chỉ định EOF làm dấu phân cách?

// Needed headers: iostream

char buffer[256];
cin.get( buffer, '\x1A' );

Nếu bạn muốn dừng đọc từ một tệp tại EOF, thì chỉ cần sử dụng getline và một lần nữa chỉ định EOF làm dấu phân cách.

// Needed headers: iostream, string, and fstream

string buffer;

    ifstream fin;
    fin.open("test.txt");
    if(fin.is_open()) {
        getline(fin,buffer,'\x1A');

        fin.close();
    }

0

Một tùy chọn là sử dụng một thùng chứa, ví dụ:

std::vector<char> data;

chuyển hướng tất cả đầu vào vào bộ sưu tập này cho đến khi EOFnhận được, tức là

std::copy(std::istream_iterator<char>(std::cin),
    std::istream_iterator<char>(),
    std::back_inserter(data));

Tuy nhiên, vùng chứa đã sử dụng có thể cần phân bổ lại bộ nhớ quá thường xuyên, hoặc bạn sẽ kết thúc với một std::bad_allocngoại lệ khi hệ thống của bạn hết bộ nhớ. Để giải quyết những vấn đề này, bạn có thể dự trữ một lượng Nnguyên tố cố định và xử lý riêng lẻ lượng nguyên tố này, tức là

data.reserve(N);    
while (/*some condition is met*/)
{
    std::copy_n(std::istream_iterator<char>(std::cin),
        N,
        std::back_inserter(data));

    /* process data */

    data.clear();
}
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.