Trong bất cứ một ngôn ngữ lập trình nào, việc tương tác trực tiếp với máy tính là rất quan trọng và cần thiết. Một chương trình được viết ra cần có khả năng nhận dữ liệu từ người dùng và hiển thị những kết quả thu được sau tính toán. Do đó việc thành thạo trong việc nhập/xuất dữ liệu là điều không thể thiếu đối với mỗi lập trình viên. Bài viết này sẽ giúp hiểu rõ hơn về thao tác nhập/xuất dữ liệu thông qua phương tiện nhập xuất chuẩn.
Tổng quan
Trong C hay bất cứ ngôn ngữ lập trình nào khác, việc nhập, xử lý và xuất dữ liệu có thể được thực hiện theo hai cách:
- Thông qua phương tiện nhập/xuất chuẩn [Standard Input/Output].
- Thông qua tập tin [File]
Bài viết này sẽ chỉ đề cập đến Standard Input/Output.
Thư viện nhập xuất chuẩn trong C là , do đó các lập trình viên cần khai báo thư viện này trước khi thực hiện việc nhập xuất dữ liệu bằng chỉ thị #include.
cung cấp cho hai hàm nhập và xuất dữ liệu có định dạng:
printf[]
: Hàm xuất có định dạng.scanf[]
: Hàm nhập có định dạng.
Output với printf
Hàm printf trong C có nguyên mẫu của hàm [prototype] như sau:
int printf[“”, ];
Trong đó, int là kiểu trả về của hàm, là giá trị đại diện cho hàm sau khi hết phạm vi của hàm. Chuỗi định dạng [Format string] có nhiệm vụ định dạng dữ liệu xuất ra màn hình. Danh sách tham số có thể bao gồm biến, hằng số, biểu thức và hàm [function] và được phân cách bằng dấu “
%[flags][width][.precision][length]specifier0”.
Chuỗi định dạng
Chuỗi định dạng phải tương ứng với danh sách tham số về số lượng, kiểu dữ liệu và thứ tự. Chuỗi định dạng luôn được đặt bên trong cặp dấu “” và bao gồm một hoặc nhiều thành phần sau:
- Ký tự văn bản [Text characters]: là những ký tự in được, bao gồm các chữ cái, chữ số và các ký tự đặc biệt trong bảng mã ASCII.
- Các ký tự không in được: bao gồm một số ký tự điều khiển như tab [
%[flags][width][.precision][length]specifier
1], xuống dòng [%[flags][width][.precision][length]specifier
2], khoảng trắng, … Khoảng trắng thường được sử dụng để phân cách các trường [field] dữ liệu được xuất ra. - Lệnh định dạng: là các lệnh quy định cách mà dữ liệu được xuất ra màn hình.
Lệnh định dạng có cấu trúc như sau:
%[flags][width][.precision][length]specifier
Trong đó,
%[flags][width][.precision][length]specifier3,
%[flags][width][.precision][length]specifier4,
%[flags][width][.precision][length]specifier5,
%[flags][width][.precision][length]specifier6 là một số bổ từ tùy chọn để thay đổi đặc tả gốc cho phù hợp với mong muốn của lập trình viên.
Specifier là đặc tả cho kiểu dữ liệu. Một số đặc tả thường dùng:
%[flags][width][.precision][length]specifier7,
%[flags][width][.precision][length]specifier8Số nguyên có dấu
%[flags][width][.precision][length]specifier9Số nguyên không dấu
#include int main [] { printf ["String only\n"]; //'\n' isn't shown printf ["%s\n", "Also string"]; printf ["int, float, char: %d, %f, %c\n", 7411, 3.14, 'R']; printf ["Preceding with blanks: d\n", 7411]; printf ["float rounding: %.2f\n", 3.1415929]; return 0; }0Số thực
#include int main [] { printf ["String only\n"]; //'\n' isn't shown printf ["%s\n", "Also string"]; printf ["int, float, char: %d, %f, %c\n", 7411, 3.14, 'R']; printf ["Preceding with blanks: d\n", 7411]; printf ["float rounding: %.2f\n", 3.1415929]; return 0; }1Ký tự
#include int main [] { printf ["String only\n"]; //'\n' isn't shown printf ["%s\n", "Also string"]; printf ["int, float, char: %d, %f, %c\n", 7411, 3.14, 'R']; printf ["Preceding with blanks: d\n", 7411]; printf ["float rounding: %.2f\n", 3.1415929]; return 0; }2Chuỗi ký tự
Ngoài ra còn một số bổ từ và lệnh định dạng khác đề cập đến.
#include int main [] { printf ["String only\n"]; //'\n' isn't shown printf ["%s\n", "Also string"]; printf ["int, float, char: %d, %f, %c\n", 7411, 3.14, 'R']; printf ["Preceding with blanks: d\n", 7411]; printf ["float rounding: %.2f\n", 3.1415929]; return 0; }
Input với scanf
Prototype của hàm
#include int main [] { printf ["String only\n"]; //'\n' isn't shown printf ["%s\n", "Also string"]; printf ["int, float, char: %d, %f, %c\n", 7411, 3.14, 'R']; printf ["Preceding with blanks: d\n", 7411]; printf ["float rounding: %.2f\n", 3.1415929]; return 0; }3 trong C như sau:
int scanf[“”, ];
Tương tự như hàm
#include int main [] { printf ["String only\n"]; //'\n' isn't shown printf ["%s\n", "Also string"]; printf ["int, float, char: %d, %f, %c\n", 7411, 3.14, 'R']; printf ["Preceding with blanks: d\n", 7411]; printf ["float rounding: %.2f\n", 3.1415929]; return 0; }4, danh sách tham số của
#include int main [] { printf ["String only\n"]; //'\n' isn't shown printf ["%s\n", "Also string"]; printf ["int, float, char: %d, %f, %c\n", 7411, 3.14, 'R']; printf ["Preceding with blanks: d\n", 7411]; printf ["float rounding: %.2f\n", 3.1415929]; return 0; }3 cũng được phân cách bằng dấu “
%[flags][width][.precision][length]specifier0”. Tuy nhiên, tham số phải được truyền vào dưới dạng tham chiếu, tức truyền vào địa chỉ của biến. Tham chiếu của các kiểu dữ liệu cơ bản [primitive data type] như
#include int main [] { printf ["String only\n"]; //'\n' isn't shown printf ["%s\n", "Also string"]; printf ["int, float, char: %d, %f, %c\n", 7411, 3.14, 'R']; printf ["Preceding with blanks: d\n", 7411]; printf ["float rounding: %.2f\n", 3.1415929]; return 0; }7,
#include int main [] { printf ["String only\n"]; //'\n' isn't shown printf ["%s\n", "Also string"]; printf ["int, float, char: %d, %f, %c\n", 7411, 3.14, 'R']; printf ["Preceding with blanks: d\n", 7411]; printf ["float rounding: %.2f\n", 3.1415929]; return 0; }8,
#include int main [] { printf ["String only\n"]; //'\n' isn't shown printf ["%s\n", "Also string"]; printf ["int, float, char: %d, %f, %c\n", 7411, 3.14, 'R']; printf ["Preceding with blanks: d\n", 7411]; printf ["float rounding: %.2f\n", 3.1415929]; return 0; }9, … là
int scanf[“”, ];0 [address-of operator] cùng với tên biến. Đối với các kiểu dữ liệu dẫn xuất [ví dụ như chuỗi ký tự], tham chiếu đơn giản là tên biến.
Chuỗi định dạng
Chuỗi định dạng của hàm
int scanf[“”, ];1 có thể bao gồm những thành phần sau:
- Whitespace character: bao gồm khoảng trắng, ký tự xuống dòng và tab. Hàm
int scanf[“”, ];
1 sẽ tự động bỏ qua bất kỳ whitespace character nào có mặt trong chuỗi định dạng. - Lệnh định dạng: có chức năng định dạng dữ liệu nhập vào từ bàn phím. Nó sẽ quyết định nhận lượng dữ liệu bao nhiêu cho biến tương ứng.
- Các ký tự khác: dùng để phân cách các lệnh định dạng. Các ký tự này dùng để nhập vào dữ liệu một cách có định dạng [ví dụ như ngày tháng năm]. Khi nhập bắt buộc phải nhập đúng theo định dạng đó. Do đó chỉ nên sử dụng trong một số trường hợp đơn giản, quen thuộc và phải có chú thích để người sử dụng không bị nhầm lẫn.
Tương tự như hàm
int scanf[“”, ];3, chuỗi định dạng của
int scanf[“”, ];1 có cấu trúc như sau:
%[*][width][length]specifier
Hiện thực ví dụ sau để nắm được cách sử dụng hàm
#include int main [] { printf ["String only\n"]; //'\n' isn't shown printf ["%s\n", "Also string"]; printf ["int, float, char: %d, %f, %c\n", 7411, 3.14, 'R']; printf ["Preceding with blanks: d\n", 7411]; printf ["float rounding: %.2f\n", 3.1415929]; return 0; }3 cũng như các lệnh định dạng thông dụng:
#include int main [] { printf ["Welcome to Stdio!\n"]; int a; float b; printf ["Enter an integer and a float: "]; scanf ["%d %f", &a, &b]; printf ["The integer you've entered is %d\nThe float you've entered is %f\n", a, b]; char *s = new char[100]; printf ["Enter a string: "]; scanf ["%s", s]; printf ["The string you've entered is %s\n", s]; delete[] s; int dd, mm, yyyy; printf ["Enter a date: "]; //Format: dd/mm/yyyy scanf ["%d/%d/%d", &dd, &mm, &yyyy]; printf ["The date you've entered is %2d/%2d/%4d\n", dd, mm, yyyy]; return 0; }
Một số vấn đề thường gặp
Không truyền tham chiếu vào hàm scanf
Đây là lỗi thường gặp nhất. Với những project nhỏ thì lỗi này có thể phát hiện được dễ dàng. Tuy nhiên, khi làm việc với những dự án lớn, việc phát hiện và khắc phục lỗi này là cực kỳ khó khăn, vì đây không phải là lỗi trong quá trình Build nên không xác định được vị trí dòng code bị lỗi. Do đó lỗi này là đặc biệt nghiêm trọng và cần được khắc phục trong quá trình học tập.
Cách khắc phục: thêm toán tử
int scanf[“”, ];0 vào trước tên biến [primitive data type].
Undeclared variables/Uninitialize variables
Sử dụng biến mà không khai báo hoặc quên khởi tạo giá trị ban đầu cho biến. Đây không phải là lỗi nghiêm trọng và rất dễ khắc phục, tuy nhiên nó cho thấy sự thiếu cẩn thận trong công việc, hãy cố gắng khắc phục sớm.
Trôi dòng lệnh khi xử lý chuỗi
Trong C, dòng [stream] vào tiêu chuẩn là
int scanf[“”, ];7. Các hàm như
int scanf[“”, ];1,
int scanf[“”, ];9,
%[*][width][length]specifier0, … đều nhận dữ liệu từ
int scanf[“”, ];7. Khi trên
int scanf[“”, ];7 không còn dữ liệu, các hàm nhập dữ liệu sẽ yêu cầu người dùng nhập vào từ bàn phím. Các hàm trên chỉ nhận đủ dữ liệu mà chúng yêu cầu [trong trường hợp này,
#include int main [] { printf ["String only\n"]; //'\n' isn't shown printf ["%s\n", "Also string"]; printf ["int, float, char: %d, %f, %c\n", 7411, 3.14, 'R']; printf ["Preceding with blanks: d\n", 7411]; printf ["float rounding: %.2f\n", 3.1415929]; return 0; }3 chỉ nhận chuỗi không có khoảng trắng], do đó một phần dữ liệu còn sót lại trên
int scanf[“”, ];7, có thể là ký tự
%[flags][width][.precision][length]specifier2 hoặc phần dữ liệu sau khoảng trắng. Điều này ảnh hưởng đến các hàm nhập dữ liệu phía sau.
Có thể khắc phục bằng cách làm rỗng bộ đệm
int scanf[“”, ];7 trước mỗi hàm nhập để đảm bảo độ chính xác của chương trình. Câu lệnh như sau:
fflush[stdin];
Hàm
%[*][width][length]specifier7 nằm trong thư viện
nên có thể dễ dàng sử dụng mà không phải include thêm thư viện khác.Error C4996 trong Visual Studio 2012 về sau
Từ Visual Studio 2012 về sau, một số hàm đã được Microsoft loại bỏ hoặc thay thế bởi những hàm tương tự. Lý do là những hàm này không còn an toàn nữa.