Chủ Nhật, 2 tháng 1, 2022

3: Nhập Và Xuất Dữ Liệu (Phần 4) - Các File Ngẫu Nhiên, Lỗi Ra Vào, Vào Ra Ở Mức Hệ Thống

 

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


- Các File Ngẫu Nhiên

Các tập tin đề cập trước đây là các tập tin tuần tự, nghĩa là tập tin mà khi đọc hay ghi đề theo chế độ tuần tự từ đầu đến cuối tập tin. Đối với tập tin tuần tự ta không thể đọc hay ghi một cách trực tiếp tại một vị trí bất kì trên tập tin. Tập tin ngẫu nhiên cho phép ta truy cập ngẫu nhiên vào những vị trí cần thiết trên tập tin. Các hàm dùng khi truy cập tập tin ngẫu nhiên là:
- rewind(): di chuyển con trỏ tập tin về đầu tập tin
Cú pháp: void rewind(FILE *fp);
- fseek(): di chuyển con trỏ tập tin về vị trí mong muốn
Cú pháp: int fseek(FILE *fp, long sb, int xp)
fp - con trỏ tập tin
sb - số byte cần di chuyển
xp - vị trí xuất phát mà việc dịch chuyển đưcợ bắt đầu từ đó . xp có thể có các giá trỊ sau:
    + xp=SEEK_SET hay 0: xuất páht từ đầu tập tin
    + xp=SEEK_ CUR hay 1: xuất phát từ vị trí con trỏ hiện tại
    + xp=SEEK_END hay 2 : xuất páht từ cuối tập tin
-ftell(): cho biết vị trí hiện tại của con trỏ tập tin

Ta xét chương trình ví dụ sau:
Chương trùnh 3-18:
// InOut_18
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>

int main()   
{     
    struct nguoi {      
        char ten[30];      
        int so;      
        float cao;    
    }
    nv;     
    int recno;     
    FILE *fp;     
    long int offset;     
   
    if ((fp=fopen("nhanvien.rec","r"))==NULL)   
    {  
        printf("Khong mo duoc file\n");  
        getch();  
        exit(1);       
    }     
    printf("Ban muon doc ban ghi thu may : ");
    scanf("%d",&recno);     
    recno--;    
    offset=recno*sizeof(nv);   
    
    if (fseek(fp,offset,0)!=0)       
    {  
        printf("Khong di chuyen duoc con tro file toi do\n");  
        getch();  
        exit(1);       
    }     
    fread(&nv,sizeof(nv),1,fp);     
    printf("Ten :%s\n",nv.ten);     
    printf("Ma nhan vien : %3d\n",nv.so);    
    printf("Chieu cao :%4.2f\n",nv.cao);     
    getch();   
    
    return 0;
}

- Lỗi Ra Vào

Nói chung, khi mở tập tin thành công ta có thể ghi lên nó . Tuy nhiên , nhiều trường hợp không mở được tập tin nhưng ta không biết lỗi do đâu . Để xác định llõi ta dùng hàm
- ferror(). Hàm này có đối số là con trỏ tập tin . Hàm sẽ có giá trị không nếu không có lỗi gì.
Ngược lại hàm cho giá trị khác không . Ta cũng có thể dùng hàm perrorQ để chỉ nội dung lỗi.

Chương trùnh 3-19:
// InOut_19
#include <stdio.h>
#include<conio.h>
#include <string.h>
#include <stdlib.h>

int main()   
{     
    FILE *fp;     
    char name[40],numstr[10];     
    int code;     
    float height;     
    int n,i;     
   
    fp=fopen("a:\newfile.txt","w");     
    printf("Cho so nguoi can nhap : ");     
    gets(numstr);     
    n=atoi(numstr);     
    for (i=0;i<n;i++)       
    {  
        printf("Nhap ten : ");  
        gets(name);  
        printf("Nhap ma so : ");  
        gets(numstr);  
        code=atoi(numstr);  
        printf("Nhap chieu cao : ");  
        gets(numstr);  
        height=atof(numstr);  
        fprintf(fp,"%s  %d  %f",name,code,height);  
        if (ferror(fp))    
        {      
            perror("Loi ghi file ");      
            getch();      
            exit(1);    
        }       
    }     
    fclose(fp);   
    
    return 0;
}

Sau lỗi do ta ghi, trình biên dịch sẽ thông báo lỗi cụ thể trong câu “ Loi ghi file : no such file on directory”

- Vào Ra Ở Mức Hệ Thống
1. Các tập tin tiêu đề và biến chuẩn : 
Trong cách vào ra ở mức hệ thống, ta phải khởi tạo bộ đệm rồi đặt dữ liệu vào đó trước ghi hay đọc. Vào ra ở mức hệ thống có lợi ở chỗ lượng mã ít hơn vào ra chuẩn và tốc độ sẽ nhanh hơn . Để dùng các hàm cấp 1 ta phải cần các tập tin tiêu đề sau:
+ io.h - chứa các prototype của các hầm cấp 1
+ fcntl.h - chứa các định nghĩa quyền truy cập
+ sys/stat.h - chá các định nghĩa thuộc tính
+ dos.h - chứa các thuộc tính theo DOS

2. Tóm tắt các hàm:
+ creat - tạo tập tin mới
+ _creat - tạo tập tin mới theo kiểu nhị phân
+ open - mở tập tin
+ _open - mở tập tin đã tồn tại
+ close và _ close - đóng tập tin
+ chmod - thay đổi thuộc tính của tập tin
+ _chmode - thay đổi thuộc tính của tập tin theo kiểu DOS
+ perror - thông báo lỗi (stdlib.h)
+ wrIfe - chi một dãy các byte
+ read - đọc một dãy các byte
+ Lseek - dùng di chuyển con trỏ vị trí

3. Đọc tập tin theo cách vào ra hệ thống:
Ta có chương trình đọc tập tin từ đĩa và hiển thị lên màn hình theo cách vào ra hệ thống.
Chương trùnh 3-20:
// InOut_20
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#define BUFFSIZE 512
char buff[BUFFSIZE];

void main(int argc,char *argv[])   
{     
    int inhandle,bytes,i;     
  
    if (argc!=2)       
    {  
        printf("Dang <ten chuong trinh>   <ten tap tin>");  
        getch();  
        exit(1);       
    }     
    if ((inhandle=open(argv[1],O_RDONLY|O_BINARY))<0)       
    {  
        printf("Khong mo duoc file\n");  
        getch();  
        exit(1);       
    }     
    while ((bytes=read(inhandle,buff,BUFFSIZE))>0)   
        for (i=0;i<bytes;i++)  
            putch(buff[i]);     
    close(inhandle);   
    
}

4. Khởi tạo bộ đệm:
Trong chương trình ta phải định nghĩa bộ đệm bằng phát biểu
#define BUFFSIZE 512
char buff[BUFFSIZE]

Nhờ đó ta đọc được đữ liệu từ đía vào bộ đệm buff. Với DOS, kích thước bộ đệm nên chọn là bội số của 512.

5. Mở một tập tin:
Cũng giống như vào ra bằng hàm cấp 2, ta phải mở tập tin trước khi đọc hay ghi bằng phát biểu:
inhandle=open(argv[1],O _RDONLY lO_BINARY);

Biểu thức này thiết lập sự liên lạc giữa tập tin và hệ điều hành . Trong biểu thức ta cần một hằng lag oflag là dấu hiệu cho biết mức độ dùng tập tin.
+ O_APPEND_: Đặt con trỏ ở cuối tập tin
+ O CREAT Tạo tập tin mới để ghi(không có hiệu quả nếu tập tin đã có )
+ O_RDONLY:  Mở một tập tin để chỉ đọc
+ O_RDWR: Mở một tập tin để chỉ đọc hay ghi
+ O_TRUNC:  Mở và cắt bỏ bớt tập tin
+ O_WRONLY:  Mở tập tin để ghi
+ O_BINARY:  Mở tập tin kiểu nhị phân
+ O_TEXT: Mở tập tin kiểu văn bản

6. Danh số của tập tin:
Trong vào ra chuẩn, con trỏ tập tin sẽ nhận được ngay khi gọi hàm fopen() còn trong nhập xuất bằng hàm cấp 1 ta nhậ được giá trị nguyên gọi là danh số của tập tin.
Đây là số gán cho một tập tin cụ thể để tham chiếu đến tập tin này.
Nếu hàm open() cho ta giá trị -l nghĩa là danh số không đúng và phát sinh lỗi.

7. Đọc tập tin vào bộ đệm:
Để đọc tập tin vào bộ đệm ta dùng lệnh:
byte = read(inhandle , buff, BUFSIZE);

Hàm này có 3 đối số: danh số của tập tin, địa chỉ của bộ đệm và số byte cực đại cần đọc. Giá trị của hàm readQ chỉ ra số byte đã đọc được.

8. Đóng tập tin :
Để đóng tập tin ta dùng lệnh
close(inhandle);

9. Thông báo lỗi:
Khi hàm open() cho giá trị -1, nghĩa là có lỗi. Dạng lỗi sẽ được đọc
bằng perror(). Ta có chương trình ví dụ
Chương trùnh 3-21:
// InOut_21
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#define BUFFSIZE 512
char buff[BUFFSIZE];


void main(int argc,char *argv[])   
{     
    int inhandle,bytes,i;     
   
    if (argc!=2)       
    {  
        printf("Dang <ten chuong trinh>   <ten tap tin>");  
        getch();  
        exit(1);       
    }     
    if ((inhandle=open(argv[1],O_RDONLY|O_BINARY))<0)
    {
        perror("Khong mo duoc file\n");
        getch();
        exit(1);
    }
    while ((bytes = read(inhandle, buff, BUFFSIZE)) > 0)
        for (i = 0; i < bytes; i++)
            putch(buff[i]);
    close(inhandle);
}

10. Thao tác trên bộ đệm:
Việc đưa tập tin vào bộ đệm có lợi là cho phép truy cập trên bộ đệm thay vì trên tập tin . Làm như vậy nhanh hơn truy cập trên đĩa.
Chương trình sau cho phép tìm một từ trong một tập tin văn bản.
Chương trùnh 3-22:
// InOut_22
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <io.h>
#define BUFFSIZE 1024
char buff[BUFFSIZE];

void search(char * cau, int buflen) {
    char * p, * ptr;
    ptr = buff;
    while ((ptr = memchr(ptr, cau[0], buflen)) != NULL)
        if (memcmp(ptr, cau, strlen(cau)) == 0)
        {
            printf("Tu xuat hien lan dau trong cau tai vi tri %d:\n", ptr - buff + 1);
            for (p = ptr; p < ptr + strlen(cau); p++)
                putch( * p);
            exit(1);
        }
        else
            ptr++;
}

void main(int argc, char * argv[])
{
    int inhandle, bytes;
    void search(char * , int);


    if (argc != 3)
    {
        printf("Dang <ten chuong trinh> <ten tap tin> <tu can tim>");
        getch();
        exit(1);
    }
    if ((inhandle = open(argv[1], O_TEXT)) < 0)
    {
        printf("Khong mo duoc file %s\n", argv[1]);
        getch();
        exit(1);
    }
    while ((bytes = read(inhandle, buff, BUFFSIZE)) > 0)
        search(argv[2], bytes);
    close(inhandle);
    printf("Khong tim thay");
    getch();
}

11. Hàm dùng bộ đệm:
Hàm search() là chương trình con mình hoạ cách dùng bộ đệm. Ta có một hàm memchr() dạng:
ptr = memchr(ptr, cau[0], buflen);

Hàm này dùng để tìm vị trí của kí tự cau[O] trong chuỗi chỉ bởi ptr và độ dài của phần cần tìm trong bộ đệm là buflen . Chương trình sẽ truyền argv[2] cho cau.
Hàm này cho giá trị NULL khi không tìm thấy kí tự cần tìm . Ngược lại nó sẽ cho địa chỉ của kí tự đã tìm thấy trong bộ đệm.
Việc so sánh các chuỗi cau và chuỗi ptr được tiến hành nhờ hàm mememp
trong câu lệnh:
if ((memecmp(ptr,cau,strlen(cau))==O)

Hàm này cũng có 3 đối số là : chuỗi thu nhất ptr , chuỗi thu hai cau và đo dai can so sánh
strlen(cau)

12. Ghi lên tập tin:
Ghi thông tin lên tập tin phức tạp hơn đọc từ tập tin. Ta có chương trình ví dụ sau dùng để chép từ một tập tin này sang tập tin khác.
Chương trùnh 3-23:
// InOut_23
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <sys\stat.h>
#define BUFFSIZE 4096
char buff[BUFFSIZE];

void main(int argc, char * argv[])
{
    int inhandle, outhandle, bytes;

    if (argc != 3)
    {
        printf("Dang <ten chuong trinh> <ten tap tin 1> <ten tap tin 2>");
        getch();
        exit(1);
    }
    if ((inhandle = open(argv[1], O_RDWR | O_BINARY)) < 0)
    {
        printf("Khong mo duoc file %s\n", argv[1]);
        getch();
        exit(1);
    }
    if ((outhandle = open(argv[2], O_CREAT | O_WRONLY | O_BINARY, S_IWRITE)) < 0)
    {
        printf("Khong mo duoc file %s\n", argv[2]);
        getch();
        exit(1);
    }
    while ((bytes = read(inhandle, buff, BUFFSIZE)) > 0)
        write(outhandle, buff, bytes);

    close(inhandle);
    close(outhandle);
    printf("Da chep xong");
    getch();
}

Trong ví dụ trên ta mở một lúc 2 tập tin với danh số là inhandle và outhandle
Biểu thức mở tập tin nguồn không có gì đặc biệt còn biểu thức mở tập tin đích có dạng:
outhandle = open(argv[2] ,O_CREATIO_WRONLY IO_BINARY,S IWRITE)

với:
O_CREAT để tạo tập tin trên đĩa
O_WRONLY để chỉ ghi lên tập tin
O_BINARY để mở tập tin theo kiểu nhị phân

Khi mở tập tin với O_CREAT, đối thứ 3 của openQ là một trong 3 trị:
S_IWRITE : chỉ cho phép ghi lên tập tin
S_IREAD: chỉ cho phép đọc từ tập tin
S_IWRITE | S_IREAD : cho phép đọc và ghi lên tập tin

Để dùng các trị này phải khai báo #include <syswtath> sau khai báo #include<femtlLh>.
Hàm write() có đối tương tự như read(). Trong vòng lặp while hàm read() báo số byte đọc được qua biến bytes và hàm writeQ sẽ biết số bytes cần ghi vào tập tin đích. Trong chương trình ta dùng bộ đệm với kích thước khá lớn để chương trình chạy nhanh.

0 bình luận:

Đăng nhận xét