Lập trình C: Các hàm xử lý tập tin

Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực

Hàm feof()

Khi một tập tin được mở để đọc ở dạng nhị phân, một số nguyên có giá trị tương đương với EOF có thể được đọc. Trong trường hợp này, quá trình đọc sẽ cho rằng đã đến cuối tập tin, mặc dù chưa đến cuối tập tin thực sự. Một hàm feof() có thể được dùng những trong trường hợp này.

Nguyên mẫu của hàm là:

int feof(FILE *fp );

Nó trả về true nếu đã đến cuối tập tin, nếu không nó trả về false (0). Hàm này được dùng trong khi đọc dữ liệu nhị phân.

Đoạn lệnh sau đây đọc một tập tin nhị phân cho đến cuối tập tin.

. .
while (!feof(fp) )
  ch = fgetc(fp);
. .

Hàm rewind()

Hàm rewind() đặt lại con trỏ định vị trí bên trong tập tin về đầu tập tin. Nó lấy con trỏ tập tin làm đối số. Cú pháp của rewind() là:

rewind(fp);

Chương trình sau mở một tập tin ở chế độ đọc/ghi, sử dụng hàm fputs() với đầu vào là các chuỗi, đưa con trỏ quay về đầu tập tin và sau đó hiển thị các chuỗi giống như vậy bằng hàm fgets().

Ví dụ 1:

#include <stdio.h>
main() {
  FILE *fp;
  char str [80];
  /* Writing to File JAK */
  if ((fp = fopen("jak", "w+")) == NULL) {
    printf ("Cannot open file \n\n");
    exit(1);
  }
  do {
    printf ("Enter a string (CR to quit): \n");
    gets (str);
    if(*str != '\n') {
      strcat (str, "\n"); /* thêm một dòng mới */
      fputs (str, fp);
    }
  } while (*str != '\n');
  /* Reading from File JAK */
  printf ("\n\n Displaying Contents of File JAK\n\n");
  rewind (fp);
  while (!feof(fp)) {
    fgets (str, 81, fp);
    printf ("\n%s", str);
  }
  fclose(fp);
}

Một mẫu chạy chương trình trên như sau:

Enter a string (CR to quit):
This is input line 1
Enter a string (CR to quit) :
This is input line 2
Enter a string (CR to quit):
This is input line 3
Enter a string (CR to quit):
Displaying Contents of File JAK
This is input line 1
This is input line 2
This is input line 3

Hàm ferror()

Hàm ferror() xác định liệu một thao tác trên tập tin có sinh ra lỗi hay không. Nguyên mẫu của hàm là:

int ferror(FILE * fp) ;

, trong đó fp là một con trỏ tập tin hợp lệ. Nó trả về true nếu có xảy ra một lỗi trong thao tác cuối cùng trên tập tin ; ngược lại, nó trả về false. Vì mỗi thao tác thiết lập lại tình trạng lỗi, nên hàm ferror() phải được gọi ngay sau mỗi thao tác; nếu không, lỗi sẽ bị mất.

Chương trình trước có thể được sửa đổi để kiểm tra và cảnh báo về bất kỳ lỗi nào trong khi ghi như sau:

. .
do {
  printf(“ Enter a string (CR to quit): \n");
  gets(str);
  if(*str != '\n') {
    strcat (str, "\n"); /* thêm một dòng mới */
    fputs (str, fp);
  }
  if(ferror(fp))
    printf("\nERROR in writing\n");
} while(*str!='\n');
. .

Xóa tập tin

Hàm remove() xóa một tập tin đã định. Nguyên mẫu của hàm là:

int remove (char *filename);

Nó trả về nếu thành công ngược lại trả về một giá trị khác 0.

Ví dụ, xét đoạn mã lệnh sau đây:

……
printf ("\nErase file %s (Y/N) ? ", file1);
ans = getchar ();
……
if(remove(file1)) {
  printf ("\nFile cannot be erased");
  exit(1);
}

Làm sạch các stream

Thông thường, các tập tin xuất chuẩn được trang bị vùng đệm. Điều này có nghĩa là kết xuất cho tập tin được thu thập trong bộ nhớ và không thật sự hiển thị cho đến khi vùng đệm đầy. Nếu một chương trình bị treo hay kết thúc bất thường, một số ký tự vẫn còn nằm trong vùng đệm. Kết quả là chương trình có vẻ như kết thúc sớm hơn là nó thật sự đã làm. Hàm fflush() sẽ giải quyết vấn đề này. Như tên gọi của nó, nó sẽ làm sạch vùng đệm và chép những gì có trong vùng đệm ra ngoài. Hành động làm sạch tùy theo kiểu tập tin. Một tập tin được mở để đọc sẽ có vùng đệm nhập trống, trong khi một tập tin được mở để ghi thì vùng đệm xuất của nó sẽ được ghi vào tập tin.

Nguyên mẫu của hàm này là:

int fflush(FILE * fp);

Hàm fflush() sẽ ghi nội dung của bất kỳ vùng đệm dữ liệu nào vào tập tin kết hợp với fp. Hàm fflush(), không có đối số, sẽ làm sạch tất cả các tập tin đang mở để xuất. Nó trả về 0 nếu thành công, ngược lại, nó trả về EOF.

Các stream chuẩn

Mỗi khi một chương trình C bắt đầu thực thi dưới DOS, hệ điều hành sẽ tự động mở 5 stream đặc biệt bao gồm:

  1. Nhập chuẩn (stdin)
  2. Xuất chuẩn (stdout)
  3. Lỗi chuẩn (stderr)
  4. Máy in chuẩn (stdprn)
  5. Thiết bị hỗ trợ chuẩn (stdaux)

Trong đó, stdinstdout và stderr được gán mặc định cho các thiết bị nhập/xuất chuẩn của hệ thống trong khi stdprn được gán cho cổng in song song đầu tiên và stdaux được gán cho cổng nối tiếp đầu tiên. Chúng được định nghĩa như là các con trỏ cố định kiểu FILE, vì vậy chúng có thể được sử dụng ở bất kỳ nơi nào mà việc sử dụng con trỏ FILE là hợp lệ. Chúng cũng có thể được chuyển một cách hiệu quả cho các stream hay thiết bị khác mỗi khi cần định hướng lại.

Chương trình sau đây in nội dung của tập tin vào máy in.

Ví dụ 2:

#include <stdio.h>
main() {
  FILE *in;
  char buff[81], fname[13];
  printf("Enter the Source File Name:");
  gets(fname);
  if((in=fopen(fname, "r")) == NULL) {
    fputs("\nFile not found", stderr);
    /* display error message on standard error rather
    than standard output */
    exit(1);
  }
  while(!feof(in)) {
    if(fgets(buff, 81, in)) {
      fputs(buff, stdprn);
      /* Send line to printer */
    }
  }
  fclose(in);
}

Lưu ý cách sử dụng của stream stderr với hàm fputs() trong chương trình trên. Nó được sử dụng thay cho hàm printf vì kết xuất của hàm printf là ở stdout, nơi mà có thể định hướng lại. Nếu kết xuất củamột chương trình được định hướng lại và một lỗi xảy ra trong quá trình thực thi, thì tất cả các thông báo lỗi đưa ra cho stream stdout cũng phải được định hướng lại. Để tránh điều này, stream stderr được dùng để hiển thị thông báo lỗi lên màn hình vì kết xuất của stderr cũng là thiết bị xuất chuẩn, nhưng stream stderr không thể định hướng lại. Nó luôn luôn hiển thị thông báo lên màn hình.

Con trỏ kích hoạt hiện hành

Để lần theo vị trí nơi mà các thao tác nhập/xuất đang diễn ra, một con trỏ được duy trì trong cấu trúc FILE. Mỗi khi một ký tự được đọc ra hay ghi vào một stream, con trỏ kích hoạt hiện hành (current active pointer) (gọi là curp) được tăng lên. Hầu hết các hàm nhập xuất đều tham chiếu đến curp, và cập nhật nó sau các thủ tục nhập hoặc xuất trên stream. Vị trí hiện hành của con trỏ này có thể được tìm thấy bằng sự trợ giúp của hàm ftell(). Hàm ftell() trả về một giá trị kiểu long int biểu diễn vị trí của curp tính từ đầu tập tin trong stream đã cho. Nguyên mẫu của hàm ftell() là:

long int ftell(FILE *fp);

Câu lệnh trích từ một chương trình sẽ hiển thị vị trí của con trỏ hiện hành trong stream fp.

printf("The current location of the file pointer is : %1d ", ftell (fp));

Đặt lại vị trí hiện hành

Ngay sau khi mở stream, con trỏ kích hoạt hiện hành được đặt là 0 và trỏ đến byte đầu tiên của stream. Như đã thấy trước đây, mỗi khi có một ký tự được đọc hay ghi vào stream, con trỏ kích hoạt hiện hành sẽ tăng lên. Bên trong một chương trình, con trỏ có thể được đặt đến một vị trí bất kỳ khác với vị trí hiện hành vào bất kỳ lúc nào. Hàm rewind() đặt vị trí con trỏ này về đầu. Một hàm khác được sử dụng để đặt lại vị trí con trỏ này là fseek().

Hàm fseek() định lại vị trí của curp dời đi một số byte tính từ đầu, từ vị trí hiện hành hay từ cuối stream là tùy vào vị trí được qui định khi gọi hàm fseek(). Nguyên mẫu của hàm fseek() là:

int fseek(FILE *fp, long int offset, int origin);

, trong đó offset là số byte cần di chuyển vượt qua vị trí tập tin được cho bởi tham số origin. Tham số origin chỉ định vị trí bắt đầu tìm kiếm và phải có giá trị là 0, 1 hoặc 2, biễu diễn cho 3 hằng ký hiệu (được định nghĩa trong stdio.h) như thể hiện dưới đây:

  1. SEEK_SET hoặc 0 : Đầu tập tin
  2. SEEK_CUR hoặc 1 : Vị trí con trỏ của tập tin hiện hành
  3. SEEK_END hoặc 2 : Cuối tập tin

Hàm fseek() trả về giá trị 0 nếu đã thành công và giá trị khác 0 nếu thất bại.

Đoạn lệnh sau tìm mẫu tin thứ 6 trong tập tin:

struct addr {
  char name[40];
  char street[40];
  char city[40];
  char state[3];
  char pin[7];
} FILE *fp;
. . .
fseek(fp, 5L*sizeof(struct addr), SEEK_SET);

Hàm sizeof() được dùng để tìm độ dài của mỗi mẩu tin theo đơn vị byte. Giá trị trả về được dùng để xác định số byte cần thiết để nhảy qua 5 mẩu tin đầu tiên.

Hàm fprintf() và fscanf()

Ngoài các hàm nhập xuất đã được thảo luận, hệ thống nhập/xuất có vùng đệm còn bao gồm các hàm fprintf() và fscanf(). Các hàm này tương tự như hàm printf() và scanf() ngoại trừ rằng chúng thao tác trên tập tin. Nguyên mẫu của hàm fprintf() và fscanf() là:

int fprintf(FILE * fp, const char *control_string,..);
int fscanf(FILE *fp, const char *control_string,...);

, trong đó fp là con trỏ tập tin trả về bởi lời gọi hàm fopen(). Hàm fprintf() và fscanf() định hướng các thao tác nhập xuất của chúng đến tập tin được trỏ bởi fp. Đoạn chương trình sau đây đọc một chuỗi và một số nguyên từ bàn phím, ghi chúng vào một tập tin trên đĩa, và sau đó đọc thông tin và hiển thị trên màn hình.

..
printf("Enter a string and a number: ");
fscanf(stdin, "%s %d", str, &no);
/* đọc từ bàn phím */
fprintf(fp, "%s %d", str, no);
/* ghi ra tập tin */
fclose (fp);
. . fscanf(fp, "%s %d", str, &no)
/* đọc từ tập tin */
fprintf(stdout, "%s %d", str, no)
/* ghi ra màn hình */
. .

Nên nhớ rằng, mặc dù fprintf() và fscanf() thường là cách dễ nhất để ghi vào và đọc dữ liệu hỗn hợp ra các tập tin trên đĩa, nhưng chúng không phải luôn luôn là hiệu quả nhất. Nguyên nhân là mỗi lời gọi phải mất thêm một khoảng thời gian, vì dữ liệu được ghi theo dạng ASCII có định dạng (như nó sẽ xuất hiện trên màn hình) chứ không phải theo định dạng nhị phân. Vì vậy, nếu tốc độ và độ lớn của tập tin là đáng ngại, fread() và fwrite() sẽ là lựa chọn tốt hơn.

» Tiếp: Giải phương trình bậc 1
« Trước: Quản lý tập tin nhị phân
Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực
Copied !!!