Những lưu ý trong nhập/ xuất xâu ký tự:Trong tất cả các kiểu dữ liệu, theo mình thì nhập xuất với xâu (string) là thể loại củ chuối và rắc rối nhất Trước hết mình nói qua một chút về thư viện chuẩn của C++. Nếu bạn nào đã học qua ngôn ngữ C rồi thì đều biết xâu ký tự là một mảng chứa các ký tự (kiểu char) và phần tử cuối cùng của mảng là NULL (hay '\n') để đánh dấu sự kết thúc xâu. Tuy nhiên, sử dụng mảng để lưu trữ xâu có phần phức tạp vì mảng là một cấu trúc tĩnh phải biết rõ kích thước ngay khi khai báo. Điều này làm cho chương trình “cứng nhắc” và không “kinh tế”. Ví dụ nếu khai báo ít quá thì khi muốn chứa thêm nhiều ký tự hơn sẽ không được, mà nếu khai báo nhiều quá, không dùng hết sẽ lãng phí bộ nhớ. Để khắc phục người ta dùng biện pháp quản lý mảng bằng con trỏ và cấp bộ nhớ phát động cho mảng (dynamic memory allocation). Tuy nhiên nếu việc này diễn ra thường xuyên thì chương trình sẽ rất rối rắm, gây mệt mỏi cho lập trình viên và dễ dẫn đến lỗi. C++ giải quyết tốt vấn đề này bằng cách xây dựng lớp string. Một đối tượng của lớp string có thể lưu trữ các ký tự “tốt hơn” mảng trong C rất nhiều. Muốn sử dụng nó ta phải include header file . Trong các bài viết, nếu không có lý do gì đặc biệt, thì mình sẽ dùng string để lưu trữ xâu.
Bây giờ trở lại vấn đề của chúng ta, đó là nhập/ xuất xâu ký tự. Xuất thì ok, không có vấn đề gì phải bàn luận nhiều, nhưng nhập thì lại có nhiều điều để nói.
I. cin không cho phép nhập dấu trắngXét chương trình sau đây:
- Code:
-
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
cin>> str;
cout << str << endl;
return 0;
}
Nếu ta nhập vào đoạn văn bản sau: “Osama Binladen” thì xâu str chỉ ghi nhận đoạn đầu “Osama” vì dấu cách là một khoảng trắng mà cin coi các dấu trắng là ký tự báo hiệu kết thúc việc nhập dữ liệu. Để khắc phục điều này C++ cung cấp một số cách thức để nhập toàn bộ xâu ký tự. Nhưng trước hết ta phải xem xét một vấn đề về bộ đệm.
II. Bộ đệm (buffer)Khi ta nhập dữ liệu vào bàn phím thì dữ liệu không được đọc ngay vào biến mà được đẩy lên trên bộ đệm (buffer). Dữ liệu sẽ tồn tại trong bộ đệm cho tới khi một lệnh nào đó gửi yêu cầu đến bộ đệm “xin phép” được load dữ liệu về. Ví dụ khi chương trình gặp câu lệnh:
cin >> x; // với x là một biến kiểu int
thì nó sẽ mò lên buffer để load dữ liệu về, nếu như trên bộ đệm có số 100 thì nó sẽ đọc 100 vào biến x mà không đợi ta nhập gì cả, còn nếu bộ đệm trống hoặc có những dữ liệu không phải số nguyên (ví dụ mã phím Enter của lần nhập trước) thì nó mới dừng lại đợi ta nhập dữ liệu vào. Như vậy ta hoàn toàn không cần để ý nhiều việc sử dụng cin >> để nhập các dữ liệu số (nguyên hoặc dấu chấm động – floating point), nhưng để nhập dữ liệu ký tự thì lại hoàn phải hết sức chú ý. Trong đoạn chương trình test nhập xâu ký tự bên trên ta nhận thấy biến str chỉ lưu trữ phần “Osama”, phần còn lại thì vẫn nằm trên buffer để cho lần nhập sau. C++ cung cấp getline cho phép nhập toàn bộ xâu ký tự kể cả khoảng trắng. Cú pháp sử dụng hàm getline như sau.
getline(cin, str, delimiter);Câu lệnh trên sẽ thực hiện đọc toàn bộ xâu nhập từ bàn phím vào biến str, cho tới khi bắt gặp ký tự kết thúc (delimiter) hoặc EOF (end-of-file). Nếu không viết delimiter thì mặc định là ký tự xuống dòng – ‘\n’. Xét chương trình sau:
- Code:
-
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
getline(cin,str);
cout << str << endl;
return 0;
}
Nếu ta nhập vào bàn phím xâu “Osama Binladen” rồi nhấn Enter thì kết quả thu được trên màn hình sẽ là trọn vẹn xâu “Osama Binladen”. Điều gì xảy ra với ký tự Enter, nó có nằm lại trên bộ đệm không? Câu trả lời là getline đã đọc mã của ký tự Enter nhưng không gắn nó vào trong xâu str và cũng không để lại nó trên bộ đệm mà hủy nó đi.
III. Hiện tượng trôi lệnhXét chương trình sau:
- Code:
-
#include <iostream>
#include <string>
using namespace std;
int main()
{
int num;
string str;
cout << "Input an integer: ";
cin >> num;
cout << num << endl;
cout << "Input a string: ";
getline(cin,str);
cout << str << endl;
cout << "End program" << endl;
return 0;
}
Bạn chạy thử chương trình trên sẽ thấy ngay. Sau khi nhập dữ liệu cho biến num, chương trình không dừng lại cho ta nhập dữ liệu cho str. Mà in ngay ra thông báo “End program”. Nguyên nhân là do sau khi nhập dữ liệu cho biến num ta gõ phím Enter. Mã của Enter được lưu trong bộ đệm (chính là ký tự xuống dòng ‘\n’) và do đó khi chương trình gặp câu lệnh:
nó sẽ đọc ngay ký tự này và nhận thấy đây là ký tự kết thúc nên nó sẽ loại bỏ ký tự này ra khỏi bộ đệm mà không đọc vào xâu str. Sau đó nó sẽ chạy thẳng đến câu lệnh tiếp theo là:
cout << "End program" << endl;
Người ta thường gọi đó là hiện tượng “trôi lệnh”. Để khắc phục hiện tượng trôi lệnh này thì trước mỗi lệnh getline ta nên đặt câu lệnh:
fflush(stdin); Câu lệnh này có tác dụng xóa bộ đệm và do đó ta có thể yên tâm là sẽ không bị trôi lệnh nữa.
Trên đây mình đã đưa ra một số chú ý về nhập xuất với dữ liệu ký tự và xâu ký tự. Hy vọng có thể giúp các bạn tránh gặp phải những lỗi không mong muốn khi nhập xuất xâu ký tự trong C++.
(sưu tầm)