Thứ Hai, 27 tháng 12, 2021

3: Nhập Và Xuất Dữ Liệu (Phần 2) - Nhập Xuất Chuẩn

 

3:  Nhập Và Xuất Dữ Liệu

- Nhập Xuất Chuẩn

1. Nhập xuất kí tự, chuỗi kí tự, định dạng và bản ghi:
Nhập xuất cấp 2 (nhập xuất chuẩn) cung cấp 4 cách đọc và ghi dữ liệu khác nhau (ngược lại nhập xuất câp1 chỉ dùng 1 trong 4 cách này) .
- Trước hết dữ liệu có thể đọc ghi mỗi lần một kí tự, tương tự như cách làm việc của putchar() và getche() để đọc đữ liệu từ bàn phím và hiển thị lên màn hình .
- Thứ hai, dữ liệu có thể nhập xuất theo chuỗi bằng các dùng các hàm gets() và puts()
- Thứ ba, dữ liệu có thể được nhập và xuất theo khuôn dạng bằng các hàm fprintf() và fscanf()
- Thứ tư, dữ liệu được đọc và ghi theo khối có chiều đài cố định thường dùng lưu trữ mảng hay cấu trúc bằng các hàm fread() và fwrite().

Tóm lại:
- Các hàm dùng chung cho hai kiểu nhị phân và văn bản
fopen : dùng mở tập tin
fclose : đóng tập tin
fclose : đóng tất cả các tập tin
fflush : dùng làm sạch vùng đệm của tập tin
flushall : dùng làm sạch vùng đệm của tất cả tập tin
ferror : cho biết có lỗi (khác không) hay không có lỗi ( bằng O)
perror : thong báo lỗi trên màn hình
foef : cho biết cuối tập tin hay chưa
unlink và remove : dùng để loại tập tin trên đĩa
fscek : di chuyển con trỏ đến vị trí bất kì trên tập tin
ftell : cho biết vị trí hiện tại của con trỏ

- Các hàm nhập xuất kí tự
putc và fputc : nhập kí tự vào tập tin
getc và fgetc : đọc kí tự từ tập tin
Ífprintf : dùng ghi dữ liệu định dạng lên tập tin
Ífscanf : dùng đọc dữ liệu định dạng từ tập tin
fputs : dùng ghi chuỗi lên tập tin
fgets : dùng đọc chuỗi từ tập tin

- Các hàm dùng cho kiểu xuất nhập nhị phân
putw : dùng ghi một số nguyên hai byte lên tập tin
gets : dùng đọc một số nguyên hai byte từ tập tin
fwrite : dùng ghi một mẫu tin lên tập tin
fread : dùng đọc một mẫu tin từ tập tin

2. Dạng văn bản và dạng nhị phân:
Cách khác để phân loại các thao tác nhập xuất tập tin là nó được mở theo kiểu văn bản hay nhị phân.
Điểm khác biệt giữa hai loại này là kí tự newline và end of line.
Điểm thứ hai để phân biệt hai kiểu tập tin là là cách lưu trữ các số
vào đĩa.
Đối với dạng văn bản thì các số được lưu trữ thành chuỗi các kí tự còn dạng nhị phân thì các số được lưu như trong bộ nhớ, nghĩa là dùng hai byte cho một số nguyên và 4 byte cho một số float.

3. Nhập xuất chuẩn:
Chương trình dùng các hàm nhập xuất cấp 2 thường dễ hiểu hơn nên chúng ta sẽ nghiên cứu trước.

- Nhập xuất kí tự:
    + Để nhập kí tự vào tập tin ta dùng hàm putc() hay fputc().
    + Để đọc kí tự từ tập tin ta dùng hàm getc() hay fgetc().

Chương trình ví dụ này là tạo lập các kí tự bằng cách gõ vào bàn phím mỗi lần một kí tự và ghi vào một tập tin trên đĩa.
Chương trình dùng hàm fopen() để mở một tập tin, dùng hàm putc() để ghi lên tập tin, dùng kí tự enter để kết thúc chương trình.

Chương trình 3-1:
// InOut_1
#include <stdio.h>
#include <conio.h>

int main()   
{     
    FILE *fp;
    char ch;
    printf("Nhap cac ki tu : ");
    
    fp=fopen("textfile","w");
    
    while ((ch=getche())!='\r')
        putc(ch,fp);
    
    fclose(fp);
    
    return 0;
}

- Mở một tập tin:
Trước khi ghi một tập tin lên đĩa ta phải mở tập tin đó đã. Để mở tập tin, trước hết ta phải khai báo một con trỏ chỉ tới FILE.
FILE là một structure chứa đựng các thông tin về cấu trúc của tập tin ví dụ như kích thước, vị trí của bộ đệm dữ liệu hiện hành.
Cấu trúc FILE được khai báo trong stdio.h nên ta cần include tập tin này.

Ngoài ra stdio.h còn xác định các tên và các biến khác được dùng trong chương trình hướng đến các tập tin. Do vậy trong chương trình ta có câu lệnh:

FILE *fp ;

Sau đó ta mở tập tin bằng lệnh:

fopen("textfile","w");

Khi viết như vậy sẽ làm cho hệ điều hành biết là mở một tập tin tên là textfile trong thư mục hiện hành để viết lên tập tin đó (nhờ “w”).
Ta có thể cho tên đường dẫn đầy đủ nếu muốn mở tập tin ở thư mục bất kì. Hàm fopen() trả về một con trỏ chỉ đến cấu trúc FILE cho tập tin và con trỏ này được cất giữ trong biến fp . Chuỗi “w” được gọi là kiểu, nó có nghĩa là ghi lên tập tin.

Các kiểu mở tập tin là:

+ “r”,”rt” mở để đọc , tập tin phải có trên đĩa
+ “w”,”wt” mở để ghi , nếu trên đĩa đã có tập tin thì nội dung bị ghi đè, nếu chưa có thì tập tin được tạo lập
+ “a”,”at” mở để nối thêm, thông tin được ghi vào cuối tập tin cũ nếu đã có tập tin hay tạo mới tập tin
+ “r+”,"r+t” mở để vừa đọc và ghi, tập tin phải có trên đĩa
+ “rb” mổ một tập tin để đọc theo kiểu nhị phân. Tập tin phải có sẵn trên đĩa
+ “r+b” mở một tập tin để đọc theo kiểu nhị phân. Tập tin phải có sẵn trên đĩa
+ “w+”,"*w+£” mở để vừa đọc và ghi, nội dung tập tin đã có trên đĩa sẽ bị ghi đè lên
+ “wb” mở để ghi theo kiểu nhị phân , nếu trên đĩa đã có tập tin thì nội dung bị ghi đè, nếu chưa có thì tập tin được tạo lập
+ “a+”,”a+t” mở để đọc và nối thêm, nếu tập tin chưa có thì nó sẽ được tạo ra
+ “ab” mở để đọc và nối thêm theo kiểu nhị phân , nếu tập tin chưa có thì nó sẽ được tạo ra

- Ghi lên tập tin:
Khi tập tin đã được mở, ta có thể ghi lên tập tin từng kí tự một bằng cách dùng hàm:

putc(ch,fp)

Hàm putc() tương tự các hàm putch() và putchar().
Hàm putc() ghi lên tập tin có cấu trúc.

FILE được ấn định bởi biến fp nhận được khi mở tập tin. Tiến trình ghi được tiến hành cho đến khi nhấn enfter.

- Đóng tập tin:
Khi không đọc ghi nữa ta cần đóng tập tin . Câu lệnh đóng tập tin là

fclose(fp);

Ta báo cho hệ thống biết là cần đóng tập tin chỉ bởi fp.

- Đọc tập tin:
Nếu ta có thể ghi lên tập tin thì ta cũng có thể đọc từ tập tin. Ta có ví
dụ sau:

Chương trình 3-2 :
// InOut_2
#include <stdio.h>
#include <conio.h>

int main()   
{     
    FILE * fp;
    int ch;

    fp = fopen("textfile", "r");
    
    while ((ch = getc(fp)) != EOF)
        printf("%c", ch);
        
    fclose(fp);
    getch();
    
    return 0;
}

- Kết thúc tập tin:
Sự khác nhâu chủ yếu giữa chương trình đọc và ghi là chương trình đọc phải phân biệt được đâu là kí tự EOF. Nó không phải là một kí tự ầm là một số nguyên do hệ điều hành gửi tới.
Khi hết tập tin ta gặp mã kết thúc tập tin EOF (định nghĩa trong stdio.h bằng -1) và hàm foef() cho trị khác không.
Người ta chọn -1 làm mã kết thúc vì nếu chưa gặp cuối tập tin thì sẽ đọc được một byte mà mã sẽ nằm trong khoảng 0-255.
Như vậy giá trị -1 không trùng với bất kì kí tự nào nào được đọc từ tập tin. Trong khi chương trình đang đọc và hiển thị các kí tự thì nó tìm kiếm mộ giá trị -1 hay EOF.

Khi thấy giá trị này , chương trình sẽ kết thúc . Chúng ta dùng một biến nguyên cất giữ một kí tự đọc được, do đó ta có thể hiểu dấu EOF như là một trị nguyên có trị là -1 . Nếu dùng một biến kiểu char, chúng ta có thể dùng tất cả các kí tự từ 0..255 - đó là tổ hợp 8 bit. Do đó nếu dùng
biến nguyên, ta bảo đảm rằng chỉ có một giá trị 16 bit là -1, đó là dấu EOE.

- Sự phiền phức khi mở tập tin:
Hai chương trình ta trình bày trên có một lỗi tiềm ẩn.
Nếu tập tin đã được chỉ định không mở được thì chương trình không chạy. Lỗi này có thể là do tập tin chưa có (khi đọc) hay đĩa không còn đủ chỗ(khi ghi).
Do đó vấn đề là phải kiểm tra xem tập tin có mở được hay không, nếu tập tin không mở được thì hàm fopen() trả về trị 0(0 là NULL trong stdio.h). Khi này C coi đây không phải là địa chỉ hợp lệ. Như vậy ta viết lại chương trình trên như sau:

Chương trình 3-3 :
// InOut_3
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

int main()   
{     
    FILE *fp;     
    int ch;     
   
    if ((fp=fopen("file","r"))==NULL)       
    {  
        printf("Khong mo duoc tap tin\n");  
        getch();  
        exit(1);       
    }     
    while ((ch=getc(fp))!=EOF)       
        printf("%c",ch);     
    fclose(fp);   
    
    return 0;
}

- Đếm số kí tự:
Khả năng đọc và ghi tập tin trên cơ sở các kí tự cho phép triển khai một số ứng dụng. Chúng ta xem xét chương trình đếm số kí tự sau:

Chương trình 3-4 :
// InOut_4
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

int main()
{     
    FILE *fp;
    
    char string[8];
    int count = 0;

    if ((fp = fopen("textfile", "r")) == NULL)
    {
        printf("Khong mo duoc tap tin\n");
        getch();
        exit(1);
    }
    
    while (getc(fp) != EOF)
        count++;
        
    fclose(fp);
    printf("Tap tin textfile co %d ki tu", count);
    getch();
    
    return 0;
}

- Đếm số từ:
Ta có thể sửa chương trình trên thành chương trình đếm số từ.

Chương trình 3-5 :
// InOut_5
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

int main()
{     
    FILE *fp;     
    char ch,string[81];     
    int count = 0;     
    int white=1;

    if ((fp = fopen("InOut_5", "r")) == NULL)
    {
        printf("Khong mo duoc tap tin\n");
        getch();
        exit(1);
    }
    
    while ((ch = getc(fp)) != EOF)
    switch (ch)
    {
        case ' ': // Neu co dau trong, dong moi hay tab
        case '\t':
        case '\n':
            white++;
            break;
        default:
            if (white) {
                white = 0;
                count++;
             }
    }
    
    fclose(fp);
    printf("Tap tin InOut_5 co %d tu", count);
    getch();
    
    return 0;
}

- Vào ra chuỗi:
Đọc hay ghi chuỗi trên tập tin cũng tương tự như đọc hay ghi từng kí tự riêng lẻ. Ta xét một chương trình ghi chuỗi

Chương trình 3-6 :
// InOut_6
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <conio.h>

int main()   
{     
    FILE *fp;     
    char string[8];     
   
    if ((fp=fopen("a.txt","w"))==NULL)       
    {  
        printf("Khong mo duoc tap tin\n");  
        getch();  
        exit(1);       
    }     
    while (strlen(gets(string))>0)       
    {  
        fputs(string,fp);  
        fputs("\n",fp);       
    }     
    fclose(fp);   
    
    return 0;
}

Trong chương trình mỗi chuỗi kết thúc bằng cách gõ enter và kết thúc chương trình bằng cách gõ enter ở đầu dòng mới. Do fputs() không tự động thêm vào mã kết thúc để chuyển dòng mới nên ta phải thêm vào tập tin mã này. Chương trình đọc một chuỗi từ tập tin:

Chương trình 3-7 :
// InOut_7
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <conio.h>

int main()
{
    FILE * fp;
    char string[81];

    if ((fp = fopen("b.txt", "r")) == NULL)
    {
        printf("Khong mo duoc tap tin\n");
        getch();
        exit(1);
    }
    while (fgets(string, 81, fp) != NULL)
        printf("%s", string);
    fclose(fp);
    getch();
    
    return 0;
}

Hàm fgets() nhận 3 đối số: địa chỉ nơi đặt chuỗi, chiều dài tối đa của chuỗi, và con trỏ chỉ tới tập tin.

- Vấn đề sang dòng mới:
Trong chương trình đếm kí tự ta thấy số kí tự đếm được bao giờ cũng nhỏ hơn số byte có trong tập tin này nhận được bằng lệnh dir của DOS.
Khi ta ghi một tập tin văn bản vào đĩa, C tự động ghi vào đĩa cả hai mã CR và LF khi gặp mã sang dòng mới “An”.
Ngược lại khi đọc tập tin từ đĩa, các mã CR và LF được tổ hợp thành mã sang dòng mới. Chương trình sau minh hoa thêm về Kĩ thuật vào ra chuỗi , nội dung tương tự lệnh type của DOS

Chương trình 3-8:
// InOut_8
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <conio.h>

int main()
{
    FILE *fp;     
    char string[81];     


    if ((fp=fopen("InOut_8","r"))==NULL)       
    {  
        printf("Khong mo duoc tap tin\n");  
        getch();  
        exit(1);       
    }     
    while (fgets(string,81,fp)!=NULL)       
        printf("%s",string);     
        
    fclose(fp);     
    getch();
    
    return 0;
}

- Các tập tin chuẩn và máy in:
Trên đây ta đã nói đến cách thức tiếp nhận một con trỏ tham chiếu đến một tập tin trên đĩa của hàm fopen(), C định nghĩa lại tê chuẩn của 5 tập tin chuẩn như sau:
    + in: Thiết bị vào chuẩn (bàn phím)
    + out: Thiết bị ra chuẩn (màn hình)
    + err: Thiết bị lỗi chuẩn (màn hình)
    + aux: Thiết bị phụ trợ chuẩn(cổng nối tiếp)
    + prn: Thiết bị in chuẩn (máy in)


Ta có thể dùng các tên này để truy cập đến các thiết bị. Chương trình sau dùng hàm fgets() và fputs() để in nội dung một tập tin ra máy in

- Nhập xuất định dạng:
Trước đây ta đã đề cập đến nhập xuất kí tự. Những số có định dạng cũng có thể ghi lên đĩa như các kí tự. Ta xét chương trình sau

Chương trình 3-9:
// InOut_9
#include <stdio.h>
#include <conio.h>

int main()   
{     
    FILE *p;     
    int i,n;     
    float x[4],y[4];     
  
    p=fopen("test.txt","w");    
    printf("Cho so cap so can nhap n = ");     
    scanf("%d",&n);     
    fprintf(p,"%d\n",n);     
    printf("Cho cac gia tri x va y\n");     
    for (i=0;i<n;i++)       
    {  
        scanf("%f%f",&x[i],&y[i]);  
        fprintf(p,"%f     %f\n",x[i],y[i]);       
    }     
    fclose(p);   
    
    return 0;
}

Chương trình 3-10:
// InOut_10
#include <stdio.h>
#include<conio.h>
#include <string.h>

int main()   
{     
    FILE *fp;     
    char name[2];     
    int code,n,i;     
    float height;     
    fp=fopen("input.txt","r");     
    fscanf(fp,"%d",&n);     
    for (i=0;i<n;i++)       
    {  
        fscanf(fp,"%s%d%f\n",name,&code,&height);  
        printf("%s%3d%8.3f\n",name,code,height);    
    }     
    fclose(fp);     
    getch();   
    
    return 0;
}

0 bình luận:

Đăng nhận xét