Đọc file trong C++ (fgetc, fgets, fscanf, sscanf) | Laptrinhcanban.com

HOME › >>

Đọc file trong C++ (fgetc, fgets, fscanf, sscanf)

Cùng tìm hiểu về cách đọc file trong C++. Ngôn ngữ C++ kế thừa các hàm dùng để đọc file từ ngôn ngữ C như fgetc, fgets,fscanf hay sscanf, và bạn sẽ học được cách sử dụng chúng để đọc số từ file trong C++, đọc chuỗi từ file trong C++, cũng như là để đọc mảng từ file trong C++ sau bài học này.

Chúng ta có 4 phương pháp để đọc file trong C++ bằng cách sử dụng các hàm kế thừa từ C như sau:

  • Đọc từng ký tự trong file bằng hàm fgetc
  • Đọc từng dòng file trong C++ bằng hàm fgets
  • Đọc từng dòng file theo định dạng chỉ định bằng hàm fscanf
  • Đọc từng dòng file theo định dạng chỉ định bằng hàm fgets kết hợp hàm sscanf

Bài viết này sẽ chú trọng về cách đọc file trong C++. Về quy trình tổng quát xử lý file trong C++, hãy tham khảo tại bài sau:

Trước khi đọc file trong C++

Để đọc file trong C++, bạn cần phải mở nó trước bằng một trong hai hàm đọc file là fopen hoặc fopen_s mà Kiyoshi đã hướng dẫn trong bài Mở file trong C++.

Lưu ý là tùy thuộc vào mục đích của việc đọc file mà mode dùng để mở file cũng sẽ khác nhau, do vậy chúng ta cần hết sức chú ý khi lựa chọn mode khi mở file.

Ở đây, mode đọc file chính là thông tin về những việc cần làm với một file. Các mode có thể dùng để đọc file trong C++ như sau:

ModeXử lýChức năng
rMở để đọcChỉ cho phép đọc file
Nếu file không tồn tại thì trả về NULL
r+Mở để đọc và ghi đèCho phép cả đọc và ghi đè
Nếu file không tồn tại thì trả về NULL
w+Mở để đọc và ghi đèCho phép cả đọc và ghi đè
Nếu file không tồn tại thì tạo file mới
a+Mở để đọc và ghi chènCho phép cả đọc và ghi chèn
Nếu file không tồn tại thì tạo file mới

Ví dụ, nếu bạn chỉ muốn mở để đọc file, hãy dùng tới mode r như sau:

FILE * fp = NULL; 
fp = fopen("sample.txt", "r");

Tuy nhiên nếu bạn muốn mở file để vừa đọc và vừa ghi đè nội dung mới vào file đó, lúc này mode cần dùng để mở file không phải là mode r, mà sẽ là mode r+ chẳng hạn.

FILE * fp = NULL; 
fp = fopen("sample.txt", "r+");

Chi tiết về các mode đọc file cũng được trình bày đầy đủ trong bài Mở file trong C++.

Sau khi đã mở file thành công bằng một trong hai hàm trên, lúc này chúng ta đã có thể tiến hành đọc dữ liệu từ file trong C++ với các phương pháp sau đây.

Đọc từng ký tự trong file bằng hàm fgetc

Hàm fgetc trong C++ là một hàm có sẵn trong thư viện chuẩn, có tác dụng đọc từng ký tự trong file chỉ định. Tên hàm fgetc được viết tắt bởi cụm từ file, getcharacter, được dịch theo tiếng Việt chính xác là hàm đọc từng ký tự trong file.

Chúng ta sử dụng hàm fgetc trong C++ với cú pháp sau đây:

int fgetc(FILE * fp);

Trong đó fp là con trỏ của file cần đọc, được tạo ra từ việc mở file ở phần trên.

Hàm fgetc sẽ trả về mã ASCII của 1 ký tự được đọc ra từ file. Trong trường hợp vị trí đọc ký tự đã là cuối file, hoặc là việc đọc file thất bại thì giá trị EOF sẽ được trả về.

Ví dụ cụ thể, chúng ta có file sample.txt với nội dung sau đây:

Hello
World    

Chúng ta sẽ dùng hàm fopen để mở file, sau đó đọc 1 ký tự trong file bằng hàm fgetc như sau. Lưu ý là để chuyển kết quả hàm fgetc là một mã ASCII sang kiểu ký tự thì chúng ta sử dụng thêm hàm char() bên trong chương trình.

#include <iostream>
using namespace std;

int main()
{
FILE * fp = NULL;

//Mở file bằn hàm fopen
fp= fopen("sample.txt", "r");

cout << char(fgetc(fp)) <<endl;
cout << char(fgetc(fp)) <<endl;
}

Kết quả:

H
e

Bạn có thể thấy chúng ta chỉ có thể đọc từng ký tự từ file trong mỗi lần chạy hàm fgetc mà thôi.

Để có thể đọc tất cả các ký tự từ trong file, chúng ta sẽ cần tạo ra một vòng lặp để đọc từng ký tự từ đầu file cho tới cuối file, cho tới khi kết quả trả về là giá trị EOF như sau:

#include <iostream>
using namespace std;

int main()
{
FILE * fp = NULL;
//Mở file bằn hàm fopen
fp= fopen("sample.txt", "r");

char a;
//Đọc từng ký tự từ file cho tới khi gặp EOF
while ((a = fgetc(fp)) != EOF)
{
//cout << fgetc(fp)<<endl;
//Xuất từng ký tự ra màn hình
cout << a;
}

fclose(fp);

return 0;
}

Kết quả, toàn bộ các ký tự được đọc và xuất lần lượt ra màn hình như sau:

Hello
World

Lưu ý ở đây, các ký tự xuống dòng cũng được đọc từ file và do đó kết quả xuất ra màn hình cũng được xuống dòng như trên.

Đọc từng dòng file trong C++ bằng hàm fgets

Hàm fgets trong C++

Việc đọc từng ký tự bằng hàm fgetc thật là vất vả phải không nào? Đó là lý do mà hàm fgets với chức năng đọc từng dòng trong file đã được ra đời.

Hàm fgets trong C++ là một hàm có sẵn trong thư viện chuẩn, có tác dụng đọc từng dòng trong file chỉ định. Tên hàm fgets được viết tắt bởi cụm từ file, getstring, được dịch theo tiếng Việt chính xác là hàm đọc từng dòng trong file.

Chúng ta sử dụng hàm fgets trong C++ với cú pháp sau đây:

char * fgets(char * buf, int size, FILE * fp);

Trong đó:

  • fp là con trỏ của file cần đọc, được tạo ra từ việc mở file
  • buf là con trỏ tới nơi lưu trữ chuỗi đã đọc từ dòng trong file. Thông thường chúng ta chỉ định buf bằng một mảng.
  • size là kích thước (số ký tự) lớn nhất có thể đọc từ dòng trong file.

Hàm fgets sẽ trả về con trỏ lưu địa chỉ trên bộ nhớ của chuỗi được đọc từ dòng trong file. Trong trường hợp vị trí đọc ký tự đã là cuối file, hoặc là việc đọc file thất bại thì con trỏ NULL sẽ được trả về.

Lại nữa, hàm fgets sẽ đọc một dòng từ file, và dòng đó được tính từ đầu dòng cho tới khi gặp ký tự xuống dòng \n.

Ví dụ dùng hàm fgets để đọc từng dòng file trong C++

Ví dụ cụ thể, chúng ta có file nums.txt với nội dung sau đây:

1234567
89abc
def

Chúng ta sẽ dùng hàm fopen để mở file, sau đó đọc từng dòng trong file bằng hàm fgets như sau:

#include <iostream>
using namespace std;

int main()
{
FILE * fp = NULL;
char arr[128];

//Mở file bằn hàm fopen
fp= fopen("nums.txt", "r");

//Đọc dòng 1
fgets(arr, 128, fp);
cout << arr;

//Đọc dòng 2
fgets(arr, 128, fp);
cout << arr;

return 0;
}

Kết quả:

1234567
89abc

Bạn có thể thấy chúng ta chỉ có thể đọc từng dòng từ file trong mỗi lần chạy hàm fgetc mà thôi.

Để có thể đọc tất cả các dòng từ trong file, chúng ta sẽ cần tạo ra một vòng lặp để đọc từng dòng từ đầu file cho tới cuối file, cho tới khi kết quả trả về là giá trị NULL như sau:

#include <iostream>
using namespace std;

int main()
{
FILE * fp = NULL;
char arr[128];

//Mở file bằn hàm fopen
fp= fopen("nums.txt", "r");

//Đọc từng dòng từ file cho tới khi gặp NULL
while (fgets(arr, 128, fp) != NULL)
{
//Xuất từng dòng ra màn hình
cout << arr;
}

fclose(fp);

return 0;
}

Kết quả, toàn bộ các dòng được đọc và xuất lần lượt ra màn hình như sau:

1234567
89abc
def

Đọc từng dòng file khi đối số size nhỏ hơn số ký tự

Đối số size trong hàm fgets giúp chúng ta chỉ định số ký tự lớn nhất có thể được đọc từ một dòng trong file. Thông thường chúng ta chỉ định giá trị của size bằng với số phần tử của mảng chuẩn bị để lưu nội dung đọc từ dòng đó.

Lưu ý trong trường hợp giá trị của size nhỏ hơn số ký tự thực có trong dòng, thì chỉ có size -1 ký tự thực được đọc ra từ dòng đó mà thôi. Chỗ trống của ký tự còn lại sẽ được tự động lấp chỗ bằng một ký tự kết thúc chuỗi \0.

Ví dụ, chúng ta sẽ đọc lại file nums.txt ở trên, nhưng với chỉ định số phần tử lớn nhất có thể đọc lại nhỏ hơn số ký tự thực trong dòng như sau:

#include <iostream>
using namespace std;

int main()
{
FILE * fp = NULL;
char arr[128];

//Mở file bằng hàm fopen
fp= fopen("nums.txt", "r");

//Đọc dòng 1
fgets(arr, 3, fp);
cout << arr <<endl;

//Đọc dòng 2
fgets(arr, 4, fp);
cout << arr <<endl;

return 0;

}

Kết quả:

12
345

Vì sao lại chỉ đọc được 2 ký tự ở lần một từ dòng, và tại sao ở lần hai lại bắt đầu đọc dòng từ ký tự 3 chứ?

Câu trả lời là ở lần đọc 1 chúng ta chỉ định đọc 3 ký tự từ dòng, tuy nhiên chỉ có 2 ký tự thực là 12 được đọc, chỗ trống còn lại được dành để điền ký tự kết thúc chuỗi \0 rồi.

Và ở lần đọc thứ 2, hàm fgets cũng sẽ bắt đầu đọc từ cùng một dòng ban đầu do dòng này chưa được đọc hết ký tự. Và vị trí bắt đầu được đọc chính là từ ký tự kết thúc chuỗi \0 mới được điền ở lần đọc đầu tiên.

Đọc từng dòng file theo định dạng chỉ định bằng hàm fscanf

Sự lợi hại của hàm fscanf trong C++

Ở phần trên chúng ta đã biết cách đọc từng dòng trong file bằng hàm fgets rồi.

Tuy nhiên còn có một hàm lợi hại hơn nữa giúp chúng ta đọc từng dòng trong file với nội dung chọn lọc, ví dụ như khi chúng ta chỉ muốn đọc chuỗi từ file trong C++, hoặc là khi chỉ muốn đọc số từ file trong C++.

Đó chính là hàm fscanf giúp chúng ta đọc từng dòng file theo định dạng chỉ định trong C++.

Hàm này đặc biệt hữu dụng khi cần đọc các file được viết theo định dạng cố định. Ví dụ như khi chúng ta cần đọc file csv trong C++ với các ô trong file được phân cách bởi dấu phẩy, hoặc là bằng dấu cách chẳng hạn.

Hàm fscanf trong C++ là gì

Hàm fscanf trong C++ là một hàm có sẵn trong thư viện chuẩn, có tác dụng đọc từng dòng file theo định dạng chỉ định. Hàm fscanf không đọc toàn bộ nội dung của dòng, mà sẽ phân tách nội dung dòng đó theo từng định dạng chỉ định, qua đó có thể truy xuất các thông tin cần thiết mà người dùng muốn lấy ra từ dòng đó, ví dụ như là chỉ lấy các số trong dòng, hoặc là chỉ lấy các chuỗi trong dòng được viết theo một quy luật (định dạng) cụ thể.

Các dữ liệu được truy xuất từ dòng theo định dạng chỉ định cũng sẽ được lưu trữ với định dạng tương ứng trong chương trình, giúp chúng ta đọc các dữ liệu từ file một cách có chọn lọc và nâng cao hiệu suất sử dụng chương trình.

Ví dụ cụ thể, chúng ta có thể chỉ đọc số từ file trong C++ hoặc là chỉ đọc chuỗi từ file trong C++ và bỏ qua các loại dữ liêu khác trong dòng.

Chúng ta sử dụng hàm fscanf trong C++ với cú pháp sau đây:

int fscanf(FILE * fp, "fo1 fo2 fo3", add1, add2, add3);

Trong đó

  • fp là con trỏ của file cần đọc, được tạo ra từ việc mở file ở phần trên.
  • Các cặp foadd tương ứng là định dạng (format) của dữ liệu cần đọc từ dòng, và địa chỉ của tên biến (con trỏ biến) dùng để lưu dữ liệu đó trong bộ nhớ.

Hàm fscanf sẽ trả về một số thuộc kiểu int, chính là số mục có thể đọc được từ dòng. Trong trường hợp dòng được đọc đã là dòng cuối file, hoặc là việc đọc file thất bại thì giá trị EOF sẽ được trả về.

Lưu ý là giống với hàm fgets thì hàm fscanf cũng chỉ có thể đọc lần lượt từng dòng trong file, do đó chúng ta cần phải sử dụng tới một vòng lặp để có thể đọc toàn bộ các dòng trong file.

Lại nữa, định dạng (format) của dữ liệu cần đọc cũng như kiểu của biến lưu dữ liệu được tóm tắt trong bảng dưới đây:

Định dạng chuyển đổiKiểu biếnChi tiết
%hhdchar
unsigned char
Chuyển về dạng thập phân và lưu trữ trong biến 1 byte
%hdshort
unsigned short
Chuyển về dạng thập phân và lưu trữ trong biến 2 byte
%dint
unsigned int
Chuyển về dạng thập phân và lưu trữ trong biến kiểu int
%ldlong
unsigned long
Chuyển về dạng thập phân và lưu trữ trong biến 4 byte
%hhxchar
unsigned char
Chuyển về hệ thập lục phân và lưu trữ trong biến 1 byte
%hxshort
unsigned short
Chuyển về hệ thập lục phân và lưu trữ trong biến 2 byte
%xint
unsigned int
Chuyển về hệ thập lục phân và lưu trữ trong biến kiểu int
%lxlong
unsigned long
Chuyển về hệ thập lục phân và lưu trữ trong biến 4 byte
%ffloatChuyển về số thực dấu phẩy động và lưu trữ trong biến kiểu float
%lfdoubleChuyển về số thực dấu phẩy động và lưu trữ trong biến kiểu double
%c++charChuyển về 1 ký tự và lưu trữ trong biến kiểu char
%schar *Chuyển về chuỗi ký tự và lưu trữ trong biến kiểu mảng char
%pvoid *Chuyển về địa chỉ và lưu trữ trong con trỏ

Sau đây chúng ta sẽ cùng tìm hiểu các cách sử dụng hàm fscanf để mở file với định dạng chỉ định như sau:

Đọc số từ file trong C++

Bằng cách chỉ định định dạng dữ liệu cần đọc dưới dạng số trong hàm fscanf, chúng ta có thể tiến hành đọc số từ file trong C++.

Ví dụ, chúng ta có file user.txt với nội dung sau đây:

Kiyoshi 32 172.5cm A
Honda 24 185.3cm O
Suzuki 63 153.8cm B

Giả sử chúng ta chỉ muốn đọc dữ liệu số bao gồm cột tuổichiều cao trong từng dòng file trên, khi đó chúng ta dùng hàm fscanf như sau:

#include <iostream>
using namespace std;

int main(){
FILE * fp = NULL;
char name[32] = { 0 }; // Tên
int age = 0; // Tuổi
double height = 0; // Chiều cao
char blood = 0; // Nhóm máu

//Mở file bằng hàm fopen
fp= fopen("user.txt", "r");


//Đọc từng dòng trong file cho tới khi gặp EOF
while (fscanf(fp, "%s %d %lfcm %c", name, &age, &height, &blood) != EOF)
{
//Xuất các dữ liệu số cần đọc
cout << age <<" "<< height <<endl;
}
return 0;
}

Kết quả:

32 172.5
24 185.3
63 153.8

Lưu ý là khi đọc từng dòng trong file bằng hàm fscanf thì chúng ta cần đọc tất cả các mục có thể đọc ra, tuy nhiên khi cần xuất dữ liệu số thì chúng ta chỉ cần chỉ định các dữ liệu đó mà thôi.

Đọc chuỗi từ file trong C++

Tương tự như trên thì bằng cách chỉ định định dạng dữ liệu cần đọc dưới dạng số trong hàm fscanf, chúng ta có thể tiến hành đọc chuỗi từ file trong C++.

Ví dụ, chúng ta cũng đọc file user.txt với nội dung sau đây:

Kiyoshi 32 172.5cm A
Honda 24 185.3cm O
Suzuki 63 153.8cm B

Giả sử chúng ta chỉ muốn đọc dữ liệu chuỗi bao gồm cột tênnhóm máu trong từng dòng file trên, khi đó chúng ta dùng hàm fscanf như sau:

#include <iostream>
using namespace std;

int main(){
FILE * fp = NULL;
char name[32] = { 0 }; // Tên
int age = 0; // Tuổi
double height = 0; // Chiều cao
char blood = 0; // Nhóm máu

//Mở file bằng hàm fopen
fp= fopen("user.txt", "r");

//Đọc từng dòng trong file cho tới khi gặp EOF
while (fscanf(fp, "%s %d %lfcm %c", name, &age, &height, &blood) != EOF)
{
//Xuất các dữ liệu số cần đọc
cout << name <<" "<< blood <<endl;
}
return 0;
}

Kết quả:

Kiyoshi A
Honda O
Suzuki B

Lưu ý là khi đọc từng dòng trong file bằng hàm fscanf thì chúng ta cần đọc tất cả các mục có thể đọc ra, tuy nhiên khi cần xuất dữ liệu chuỗi thì chúng ta chỉ cần chỉ định các dữ liệu đó mà thôi.

Đọc từng dòng file theo định dạng chỉ định bằng hàm sscanf

Hàm sscanf trong C++ là một hàm có sẵn trong thư viện chuẩn, có tác dụng truy xuất thông tin theo định dạng chỉ định từ một chuỗi ký tự. Bằng cách ứng dụng hàm sscanf, chúng ta có thể lấy ra các thông tin cần thiết từ chuỗi theo một định dạng chỉ định nào đó.

Cú pháp sử dụng hàm sscanf trong C++ như sau:

int sscanf (buff, "fo1 fo2 fo3", add1, add2, add3 );

Trong đó

  • buff là con trỏ tới chuỗi ký tự cần phân tích để lấy ra thông tin theo định dạng
  • Các cặp foadd tương ứng là định dạng (format) của dữ liệu cần đọc từ chuỗi ký tự, và địa chỉ của tên biến dùng để lưu thông tin được tách ra từ chuỗi trên bộ nhớ.

Và định dạng (format) sử dụng trong hàm sscanf thì cũng tương tự như với hàm fscanf mà Kiyoshi đã giới thiệu ở trên.

Ứng dụng hàm sscanf, chúng ta có thể đọc từng dòng trong file và lấy ra các thông tin theo một chỉ định cụ thể, bằng cách chỉ định chuỗi ký tự cần phân tích trong hàm sscanf chính là nội dung từng dòng trong file được đọc bằng hàm gets.

Nói một cách dễ hiểu thì cách sử dụng của nó rất giống với hàm fscanf ở trên, ngoại trừ việc nó sẽ xử lý nội dung dòng được đọc bởi hàm fgets mà thôi.

Ví dụ cụ thể, chúng ta có file test.txt với nội dung sau đây:

test01 1.0 1.1 1.2 1.3 1.4
test02 2.0 2.1 2.2 2.3 2.4
test03 3.0 3.1 3.2 3.3 3.4

Bằng việc sử dụng hàm fgets để đọc từng dòng trong file này, sau đó dùng hàm sscanf để lấy thông tin cần thiết theo định dạng từ dòng, mà chúng ta có thể đọc từng dòng trong file với định dạng chỉ định như sau:

#include <iostream>
using namespace std;

#define N 256 //Chỉ định số ký tự lớn nhất có thể đọc từ một dòng

int main() {
FILE *fp;
char fname[] = "test.txt";
char line[N];
char str[16];
float f1, f2, f3, f4, f5;

//Mở file bằng hàm fopen, và trả về NULL nếu mở file thất bại.
fp = fopen(fname, "r");
if(fp == NULL) {
cout << fname<<" file not open!\n";
return -1;
}

//Đọc từng dòng trong file bằng hàm fgets
while(fgets(line, N, fp) != NULL) {
//Truy xuất thông tin cần thiết từ nội dung đọc được bằng hàm sscanf
sscanf(line, "%s %f %f %f %f %f", str, &f1, &f2, &f3, &f4, &f5);
cout << str <<" " << f1<<" "<< f2<<" "<< f3<<" "<< f4<<" "<< f5<< endl;
}

fclose(fp); //Đóng file

return 0;
}

Kết quả:

test01 1 1.1 1.2 1.3 1.4
test02 2 2.1 2.2 2.3 2.4
test03 3 3.1 3.2 3.3 3.4

Đọc file csv trong C++

File csv thường có định dạng cố định, trong đó giữa các dữ liệu trong một hàng có thể được phân cách bởi một dấu phẩy, hoặc là bằng dấu cách.

Và để đọc các file có định dạng cố định như thế này thì chúng ta có thể dùng hàm fscanf, hoặc là tổ hợp hàm fgets và hàm sscanf mà Kiyoshi đã giới thiệu ở trên.

Sau đây, hãy cùng tìm hiểu cách dùng hàm fscanf để đọc file csv trong C++ nhé.

Mở file CSV có các cột phân tách bởi dấu cách

Giả sử chúng ta có file test.csv với nội dung sau đây:

test01 1.0 1.1 1.2 1.3 1.4
test02 2.0 2.1 2.2 2.3 2.4
test03 3.0 3.1 3.2 3.3 3.4

Chúng ta sẽ mở file này và đọc từng dòng trong file theo định dạng như sau:

#include <iostream>
using namespace std;

int main() {
FILE *fp;
char fname[] = "test.csv";
char str[16];
float f1, f2, f3, f4, f5;

fp = fopen(fname, "r"); //Mở file bằng hàm fopen
//Xử lý lỗi khi mở file thất bại
if(fp == NULL) {
cout << fname<<" file not open!\n";
return -1;
}

//Đọc từng dòng theo định dạng bằng hàm fscan
while(fscanf(fp, "%s %f %f %f %f %f", str, &f1, &f2, &f3, &f4, &f5) != EOF) {
cout << str <<" " << f1<<" "<< f2<<" "<< f3<<" "<< f4<<" "<< f5<< endl;
}

fclose(fp); //Đóng file

return 0;
}

Và kết quả:

test01 1.0 1.1 1.2 1.3 1.4
test02 2.0 2.1 2.2 2.3 2.4
test03 3.0 3.1 3.2 3.3 3.4

Có thể thấy bằng cách thêm dấu cách bên trong định dạng dữ liệu mở file mà chúng ta đã có thể đọc các dữ liệu cách nhau bởi dấu cách như thể từ từng dòng trong file CSV.

Mờ file CSV có các cột phân tách bởi dấu phẩy

Giả sử chúng ta có file test.csv với nội dung sau đây:

test01,1.0,1.1,1.2,1.3,1.4
test02,2.0,2.1,2.2,2.3,2.4
test03,3.0,3.1,3.2,3.3,3.4

Một cách tương tự thì chúng ta cũng mở file này và đọc từng dòng trong file theo định dạng như sau:

#include <iostream>
using namespace std;

int main() {
FILE *fp;
char fname[] = "test.csv";
char str[16];
float f1, f2, f3, f4, f5;

fp = fopen(fname, "r"); //Mở file bằng hàm fopen
//Xử lý lỗi khi mở file thất bại
if(fp == NULL) {
cout << fname<<" file not open!\n";
return -1;
}

//Đọc từng dòng theo định dạng bằng hàm fscan
while(fscanf(fp, "%[^,],%f,%f,%f,%f,%f", str, &f1, &f2, &f3, &f4, &f5) != EOF) {
cout << str <<" " << f1<<" "<< f2<<" "<< f3<<" "<< f4<<" "<< f5;
}

fclose(fp); //Đóng file

return 0;
}

Và kết quả:

test01 1 1.1 1.2 1.3 1.4
test02 2 2.1 2.2 2.3 2.4
test03 3 3.1 3.2 3.3 3.4

Có thể thấy bằng cách thêm dấu phẩy bên trong định dạng dữ liệu mở file mà chúng ta đã có thể đọc các dữ liệu cách nhau bởi dấu phẩy như thể từ từng dòng trong file.

Đọc mảng từ file trong C++

Đối với các file có định dạng cố định như là CSV, thì khi chúng ta cũng có thể đọc mảng từ file trong C++.

Ví dụ, chúng ta có file test.csv có nội dung sau đây:

test01,1.0,1.1,1.2,1.3,1.4
test02,2.0,2.1,2.2,2.3,2.4
test03,3.0,3.1,3.2,3.3,3.4

Có thể thấy với file CSV này thì các ô trong hàng được cách nhau bởi dấu phẩy, trong đó ô đầu tiên ở dạng chuỗi ký tự, và các ô sau thì ở dạng số thực.

Do các dòng trong file đều có chung cấu trúc như vậy, nên sau khi đọc từng dòng của file bằng hàm fscanf, hoặc là tổ hợp hàm fgets và hàm sscanf mà Kiyoshi đã giới thiệu ở trên, chúng ta có thể lưu chuỗi ký tự đọc được từ ô đầu tiên vào một mảng chuỗi, và các số đọc được từ các ô còn lại thì lưu vào trong mảng số khi đọc từng dòng trong file. Đây là cách đọc mảng từ file trong C++.

Và với từng dòng như vậy, do tồn tại 2 mảng với 2 kiểu dữ liệu khác nhau, nên chúng ta sẽ nhóm hai loại dữ liệu khác nhau này trong một tập hợp cho dễ quản lý thông qua kiểu cấu trúc trong C++.

Đây cũng chính là cách Đọc cấu trúc từ file trong C++. Nói một cách dễ hiểu thì chúng ta sẽ chỉnh lý các dữ liệu đọc được từ file và lưu chúng vào các mảng và kiểu dữ liệu để dễ dàng xử lý chúng trong chương trình.

Tất nhiên sau khi đã lưu chúng vào mảng hoặc cấu trúc rồi, chúng ta hoàn toàn có thể sử dụng các phương pháp xử lý mảng hoặc phương pháp xử lý cấu trúc để xử lý dữ liệu đọc được từ chuỗi. Bạn cũng có thể tham khảo các phương pháp này tại hai chuyên đề là Mảng trong C++Kiểu cấu trúc trong C++.

Và chúng ta sẽ đọc mảng từ file với chương trình như sau:

#include <iostream>
using namespace std;

#define N 256 //Định nghĩa N là số ký tự lớn nhất có thể đọc từ một dòng
#define ROW 3 //Định nghĩa số dòng sẽ đọc trong file

// Khai báo cấu trúc mảng để lưu chuỗi và số trích xuất từ dòng
typedef struct str {
char str[16];
float f_data[5];
} data;

int main() {
FILE *fp; //
char fname[] = "test.csv";
char line[N];
char str[16];
float f1, f2, f3, f4, f5;
int i = 0;

//Khởi tạo thực thể từ kiểu cấu trúc data
data data[ROW];

fp = fopen(fname, "r"); //Mở file bằng hàm fopen, trả về NULL khi thất bại
if(fp == NULL) {
cout << fname<<" file not open!\n";
return -1;
}
//Đọc từng dòng trong file bằng hàm fgets
while(fgets(line, N, fp) != NULL) {
//Trích xuất thông tin từ nội dung file bằng hàm sscanf
sscanf(line, "%[^,],%f,%f,%f,%f,%f", str, &f1, &f2, &f3, &f4, &f5);


//Gán các nội dung trích xuất được lần lượt vào trong thực thể của cấu trúc
for(int j = 0; j < sizeof(data[i].str) / sizeof(data[i].str[0]); j++) {
data[i].str[j] = str[j];
}
float tmp[] = {f1, f2, f3, f4, f5};
for(int j = 0; j < sizeof(data[i].f_data) / sizeof(data[i].f_data[0]); j++) {
data[i].f_data[j] = tmp[j];
}

//Xuất giá trị các thành viên trong thực thể của cấu trúc
cout << data[i].str<<" "<< data[i].f_data[0]<<" "<< data[i].f_data[1]<<" "<<
data[i].f_data[2]<<" "<< data[i].f_data[3]<<" "<< data[i].f_data[4]<<endl;
i++;
}

fclose(fp); //Đóng file

return 0;
}

Và kết quả đọc mảng từ file trong C++ như sau:

test01 1 1.1 1.2 1.3 1.4
test02 2 2.1 2.2 2.3 2.4
test03 3 3.1 3.2 3.3 3.4

Lưu ý trong định dạng dữ liệu sử dụng trong hàm sscanf, chúng ta đã dùng tới biểu thức chính quy với cách viết %[^,], có tác dụng lấy các dữ liệu chuỗi ngoại trừ dấu phẩy. Lý do là dấu phẩy được công nhận là một phần của chuỗi, do đó, chúng ta cần dùng biểu thức chính quy để loại bỏ dấu phẩy này từ trong kết quả đọc chuỗi.

Tổng kết

Trên đây Kiyoshi đã hướng dẫn các bạn về cách đọc file trong C++ rồi. Để nắm rõ nội dung bài học hơn, bạn hãy thực hành viết lại các ví dụ của ngày hôm nay nhé.

Và hãy cùng tìm hiểu những kiến thức sâu hơn về C++ trong các bài học tiếp theo.

URL Link

https://laptrinhcanban.com/cpp/lap-trinh-cpp-co-ban/file-trong-cpp/doc-file-trong-cpp/

Hãy chia sẻ và cùng lan tỏa kiến thức lập trình Nhật Bản tại Việt Nam!

HOME  › >>

Profile
きよしです!笑

Tác giả : Kiyoshi (Chis Thanh)

Kiyoshi là một cựu du học sinh tại Nhật Bản. Sau khi tốt nghiệp đại học Toyama năm 2017, Kiyoshi hiện đang làm BrSE tại Tokyo, Nhật Bản.