15 thg 10, 2010

Elementary Programming With C


Quản lý tập tin


Giới thiệu

    - Hầu hết các chương trình đều yêu cầu đọc và ghi dữ liệu vào các hệ thống lưu trữ trên đĩa. Các chương tŕnh xử lý văn bản cần lưu các tập tin văn bản, chương trình xử lý bảng tính cần luu nội dung của các ô, chương trình cơ sở dữ liệu cần lưu các mẩu tin. Bài này sẽ khám phá các tiện ích trong C dành cho các thao tác nhập/xuất (I/O) đĩa hệ thống. - Ngôn ngữ C không chứa bất kỳ câu lệnh nhập/xuất nào một cách tường minh. Tất cả các thao tác nhập/xuất đều thực hiện thông qua các hàm thư viện chuẩn của C. Tiếp cận này làm cho hệ thống quản lý tập tin của C rất mạnh và uyển chuyển. Nhập/xuất trong C là tuyệt vời với dữ liệu có thể truyền ở dạng nhị phân hay ở dạng văn bản mà con người có thể đọc được. Điều này làm cho việc tạo tập tin để đáp ứng mọi nhu cầu một cách dễ dàng. - Việc hiểu rõ sự khác biệt giữa stream và tập tin là rất quan trọng. Hệ thống nhập/xuất của C cung cấp cho người dùng một giao diện độc lập với thiết bị thật sự đang truy cập. Giao diện này không phải là một tập tin thật sự mà là một sự biểu diễn trừu tượng hóa của thiết bị. Giao diện trừu tượng hóa này được gọi là một stream và thiết bị thật sự được gọi là tập tin.

1. File Streams
    - Hệ thống tập tin của C làm việc được với rất nhiều thiết bị khác nhau bao gồm máy in, ổ đĩa, ổ băng từ và các thiết bị đầu cuối. Mặc dù tất cả các thiết bị đều khác nhau, nhưng hệ thống tập tin có vùng đệm sẽ chuyển mỗi thiết bị về một thiết bị logic gọi là một stream. Vì mọi stream hoạt động tương tự, nên việc quản lý các thiết bị là rất dễ dàng. Có hai loại stream - văn bản (text) và nhị phân (binary).

2. Streams văn bản
    - Một streams văn bản là một chuỗi các kư tự. Các streams văn bản có thể được tổ thành các dạng, mỗi dạng kết thúc bằng một ký tự sang dòng mới. Tuy nhiên, ký tự sang dòng mới là tùy chọn trong dòng cuối và được quyết định khi cài đặt. Hầu hết các trình biên dịch C không kết thúc stream văn bản với ký tự sang dòng mới. Trong một stream văn bản, có thể xảy ra một vài sự chuyển đổi ký tự khi môi trường yêu cầu. Chẳng hạn như, ký tự sang dòng mới có thể được chuyển thành một cặp ký tự về đầu dòng/nhảy đến dòng kế. Vì vậy, mối quan hệ giữa các ký tự được ghi (hay đọc) và những ký tự ở thiết bị ngoại vi có thể không phải là mối quan hệ một-một. Và cũng với sự chuyển đổi có thể xảy ra này, số lượng ký tự được ghi (hay đọc) có thể không giống như số lượng ký tự nhìn thấy ở thiết bị ngoại vi.

3. Stream nhị phân

- Một stream nhị phân là một chuỗi các byte với sự tương ứng một-một với thiết bị ngoại vi, nghĩa là, không có sự chuyển đổi kư tự. Cũng v́ vậy, số lượng byte đọc (hay ghi) cũng sẽ giống như số lượng byte ở thiết bị ngoại vi. Các stream nhị phân là các chuỗi byte thuần túy, mà không có bất kỳ kư hiệu nào được dùng để chỉ ra điểm kết thúc của tập tin hay kết thúc của record. Kết thúc của tập tin được xác định bằng độ lớn của tập tin.

4. Các hàm về tập tin và structure FILE
    - Một tập tin có thể tham chiếu đến bất cứ cái gì, từ một tập tin trên đĩa đến một thiết bị đầu cuối hay một máy in. Tuy nhiên, tất cả các tập tin đều không có cùng khả năng. Ví dụ như, một tập tin trên đĩa có thể hỗ trợ truy cập ngẫu nhiên trong khi một bàn phím thì không. Một tập tin sẽ kết hợp với một stream bằng cách thực hiện thao tác mở. Tương tự, nó sẽ thôi kết hợp với một stream bằng thao tác đóng. Khi một chương tŕnh kết thúc bình thường, tất cả các tập tin đều tự động đóng. Tuy nhiên, khi một chương trình bị treo hoặc kết thúc bất thường, các tập tin vẫn còn mở.

5. Các hàm cơ bản về tập tin.


    - Một hệ thống quản lý tập tin theo chuẩn ANSI bao gồm một số hàm liên quan với nhau. Các hàm thông dụng nhất được liệt kê trong bảng dưới đây. Hình ảnh - Các hàm trên chứa trong tập tin header stdio.h. Tập tin header này phải bao gồm vào chương tŕnh có sử dụng các hàm này. Hầu hết các hàm này tương tự như các hàm nhập/xuất từ thiết bị nhập xuất chuẩn. Tập tin header stdio.h còn định nghĩa một số macro sử dụng trong quá tŕnh xử lư tập tin. Ví dụ như macro EOF được định nghĩa là -1, chứa giá trị trả về khi một hàm cố đọc tiếp khi đã đến cuối tập tin.

6. Con trỏ tập tin.
    - Một con trỏ tập tin (file pointer) rất cần thiết cho việc đọc xuất và ghi các tập tin. Nó là một con trỏ đến một structure chứa thống tin vê tập tin. Thông tin bao gồm: tên tập tin, vị trí hiện tại của tập tin, tập tin đang được đọc hay ghi, có bất kỳ lỗi nào xuất hiện đã đến cuối tập tin. Người dùng không cần thiết phải biết chi tiết, v́ các định nghĩa lấy từ studio.h có bao gồm một khai báo structure tên là FILE. Câu lệnh khai báo duy nhất cần thiết cho một con trỏ tập tin là: Code:
    Trích dẫn:
    FILE *fp;
    - Khai báo này cho biết fp là một con trỏ đến một FILE.

7. Mở một tập tin văn bản

    - Hàm fopen() mở một stream để sử dụng và liên kết một tập tin với stream đó. Con trỏ kết hợp với tập tin được trả về từ hàm fopen(). Trong hầu hết các trường hợp, tập tin đang mở là một tập tin trên đĩa. Nguyên mẫu hàm fopen() là: Code:
    Trích dẫn:
    FILE * fopen(const char *filename, const char *mode);
    - Trong đó filename là một con trỏ đến chuỗi kư tự chứa một tên tập tin hợp lệ và cũng có thể chứa cả phần mô tả đường dẫn. Chuỗi được trỏ đến bởi con trỏ mode xác định cách thức tập tin được mở. Bảng sau liệt kê một số chế độ mở của tập tin. Hình ảnh - Macro NULL được định nghĩa trong stdio.h là "\0". Nếu sử dụng phương pháp trên để mở tập tin, thì hàm fopen() sẽ phát hiện ra lỗi nếu có, chẵng hạn như đĩa đang ở chế bộ cấm ghi (write-protected) hay đĩa đầy, trước khi bắt đầu ghi đĩa. - Nếu một tập tin được mở để ghi, bất kỳ một tập tin nào có cùng tên và đang mở sẽ bị viết chồng lên. Vì khi một tập tin được mở ở chế độ ghi, thì một tập tin mới được tạo ra. Nếu muốn nối thêm các mẫu tin vào tập tin đă có, thì nó phải được mở với chế độ "a". Nếu một tập tin được mở ở chế độ đọc và nó không tồn tại, hàm sẽ trả về lỗi. Nếu một tập tin được mở để đọc/ghi, nó sẽ không bị xóa nếu đă tồn tại, hàm sẽ trả về lỗi. Nếu một tập tin được mở ra để đọc/ghi, nó sẽ không bị xóa nếu đă tồn tại. Tuy nhiên, nếu nó không tồn tại, thì nó sẽ được tạo ra. Theo chuẩn ANSI, tám tập tin có thể được mở tại một thời điểm. Tuy vậy, hầu hết các trình biên dịch C và môi trường đều cho phép mở nhiều hơn tám tập tin.

8. Đóng một tập tin văn bản
    - Vì số lượng tập tin có thể mở tại một thời điểm bị giới hạn, việc đóng một tập tin khi không còn sử dụng là một điều quan trọng. Thao tác này sẽ giải phóng tài nguyên và làm giảm nguy cơ vượt quá giới hạn đă định. Đóng một stream cũng sẽ làm sạch và chép vùng đệm kết hợp của nó ra ngoài (một thao tác quan trọng để tránh mất dữ liệu) khi ghi ra đĩa. Hàm fclose() đóng một stream đă được mở bằng hàm fopen(). Nó ghi bất kỳ dữ liệu nào còn lại trong vùng đệm của đĩa vào tập tin. Nguyên mẫu của hàm fopen() là: Code:
    Trích dẫn:
    int fclose(FILE *fp);
    - Trong đó fp là một con trỏ tập tin. Hàm fclose() trả về 0 nếu đóng thành công. Bất kỳ giá trị trả về nào khác 0 đều cho thấy có lỗi xảy ra. Hàm fclose() sẽ thất bại nếu đĩa đă sớm được gỡ ra khỏi ổ đĩa hoặc đĩa bị đẩy. - Một hàm khác dùng để dóng stream là hàm fcloseall(). Hàm này hữu dụng khi phải đóng cùng một lúc nhiều stream đang mở. Nó sẽ đóng tất cả các stream và trả về số stream đă đóng hoặc EOF nếu có phát hiện lỗi. Nó có thể được sử dụng theo cách như sau: Code:
    Mã:
    flc = fcloseall();
    if(fcl==EOF)
    {
       printf("Error closing files");
    }
    else
       printf("%d file(s) closed", fcl);

9. Ghi một ký tự
    - Stream có thể được ghi vào tập tin theo từng ký tự một hoặc theo từng chuỗi. Trước hết chúng ta hăy thảo luận về cách ghi các ký tự vào tập tin. Hàm fputc() được sử dụng để ghi các ký tự vào tập tin đă được mở trước đó bằng hàm fopen().. Nguyên mẫu hàm của hàm này như sau: Code:
    Trích dẫn:
    int fputc(int ch, FILE *fp);
    - Trong đó fp là một con trỏ tập tin trả về bởi hàm fopen() và ch là ký tự cần ghi. Mặc dù ch được khai báo là kiểu int, nhưng nó được hàm fputc() chuyển đổi thành kiểu unsigned char. Hàm fputc() ghi một kư tự vào stream đă định vị trí hiện hành của con trỏ định vị trí bên trong tập tin và sau đó tăng con trỏ này lên. Nếu fputc() thành công, nó trả về kư tự đă ghi, ngược lại nó trả về EOF.

10. Đọc một ký tự
    - Hàm fgetc() được dùng để đọc các ký tự từ một tập tin đă được mở ở chế độ đọc, sử dụng hàm fopen(). Nguyễn mẫu của hàm là: Code:
    Trích dẫn:
    int fgetc(FILE *fp);
    - Trong đó fp là một con trỏ tập tin kiểu FILE trả về bởi hàm fopen(). Hàm fgetc() trả về ký tự kế tiếp của vị trí hiện hành trong stream input, và tăng con trỏ định vị trí bên trong tập tin lên. Ký tự đọc được là một ký tự kiểu unsigned char và được chuyển thành kiểu int. Nếu đă đên cuối tập tin, fgetc() trả về EOF. Để đọc một tập tin văn bản từ đầu cho đến cuối, câu lệnh sẽ là: Code:
    Mã:
    do
    {
       ch = fgetc(fp);
    } while (ch!=EOF);

11. Nhập xuất chuỗi
    - Ngoài fgetc() và fputc(), C còn hỗ trợ các hàm fputs() và fgets() để ghi vào và đọc ra các chuỗi ký tự từ tập tin trên đĩa. Nguyên mẫu hàm này như sau: Code:
    Trích dẫn:
    int fputs(const char *str, FILE *fp);
    - Hàm fputs() làm việc giống như hàm fputc(), ngoại trừ là nó viết toàn bộ chuỗi vào stream. Nó trả về EOF nếu xảy ra lỗi. Code:
    Trích dẫn:
    char *fgets(char *str, int length, FILE *fp);
    Hàm fgets() đọc một chuỗi từ stream đă cho cho đến khi đọc được một ký tự sang dòng mới hoặc sau khi đă đọc length-1 ký tự. Nếu đọc được một ký tự sang dòng mới, ký tự này được xem như là một phần của chuỗi (không giống như hàm gets()). Chuỗi kết quả sẽ kết thúc bằng ký tự null. Hàm trả về một con trỏ đến chuỗi nếu thành công và null nếu xảy ra lỗi.

12. Mở một tập tin nhị phân
    - Các hàm dùng để xử lý các tập tin nhị phân cũng giống như các hàm sử dụng để quản lý tập tin văn bản. Tuy nhiên, chế độ mở tập tin của hàm fopen() thì khác đi trong trường hợp các tập tin nhị phân. - Bảng sau đây liệt kê các chế độ khác nhau của hàm fopen() trong trường hợp mở tập tin nhị phân. Hình ảnh - Nếu một tập tin xyz được mở để ghi, câu lệnh sẽ là: Code:
    Mã:
    FILE *fp;
    fp = fopen("xyz", "wb");

13. Đóng một tập tin nhị phân
    - Ngoài tập tin văn bản, hàm fclose() cũng có thể được dùng để đóng một tập tin nhị phân. Nguyên mẫu hàm của fclose như sau: Code:
    Trích dẫn:
    int fclose(FILE *fp);
    - Trong đó fp là một con trỏ tập tin đến một tập tin đang mở.

14. Ghi một tập tin nhị phân:
    - Một số ứng dụng liên quan đến việc sử dụng các tập tin dữ liệu để lưu trữ các khối dữ liệu, trong đó mỗi khối bao gồm các byte liên tục. Mỗi khối nói chung sẽ biểu diễn một cấu trúc dữ liệu phức tạp hoặc một mảng. - Chẳng hạn như, một tập tin dữ liệu có thể bao gồm nhiều cấu trúc có cùng thành phần cấu tạo, hoặc nó có thể chứa nhiều mảng có cùng kiểu và kích thước. Và với những ứng dụng như vậy thường đòi hỏi đọc toàn bộ khối dữ liệu từ tập tin dữ liệu hoặc ghi toàn bộ khối vào tập tin dữ liệu hơn là đọc hay ghi các thành phần độc lập (nghĩa là các thành viên của cấu trúc hay các phần tử của mảng) trong mỗi khối riếng biệt. - Hàm fwrite() được dùng để ghi dữ liệu vào tập tin dữ liệu trong những tình huống như vậy. Hàm này có thể dùng để ghi bất kỳ kư kiểu dữ liệu nào. Nguyên mẫu của fwrite() là: Code:
    Trích dẫn:
    size_t_fwrite(const void *buffer, size_t num_bytes, size_t count, FILE *fp);
    - Kiểu dữ liệu size_t được thêm vào C chuẩn để tăng tính tương thích của chương tŕnh với nhiều hệ thống. Nó được định nghĩa trước như là một kiểu số nguyên đủ lớn để lưu trữ kết quả của hàm sizeof(). Đối với hầu hết các hệ thông, nó có thể được dùng như một số nguyên dương. - Buffer là một con trỏ trỏ đến thông tin sẽ được ghi vào tập tin. Số byte phải đọc hoặc ghi được cho hỏi num_bytes. Đối số count xác định có bao nhiêu mục (mỗi mục dài num_bytes) được đọc hoặc ghi. Cuối cùng, fp là một con trỏ tập tin trỏ đến một stream đă được mở trước đó. Các tập tin mở cho những thao tác này phải mở ở chế độ nhị phân. - Hàm này trả vê số lượng các đối tượng đă ghi vào tập tin nếu thao tác ghi thành công. Nếu giá trị này nhỏ hơn count thì đă xảy ra lỗi. Hàm ferror() (sẽ được thảo luận trong phần tới) có thể được dùng để xác định lỗi.

15. Đọc một tập tin nhị phân.
    - Hàm fread() có thể đươc dùng để đọc bất kỳ kiểu dữ liệu nào. Nguyên mẫu hàm là: Code:
    Trích dẫn:
    size_t fread(void *buffer, size_t num_bytes, size_t count FILE *fp);
    - Buffer là một con trỏ đến vùng nhớ sẽ nhận dữ liệu từ tập tin. Số byte phải đọc hoặc ghi được cho bởi num_bytes. Đối số count xác định có bao nhiêu mục (mỗi mục dài num_bytes) được đọc hoặc ghi. Cuối cùng, fp là một con trỏ tập tin trỏ đến một stream đă được mở trước đó. Các tập tin đă mở cho những thao tác này phải mở ở chế độ nhị phân. - Hàm này trả về số lượng các đối tượng đă đọc nếu thao tác đọc thành công. Nó trả về 0 nếu đọc đến cuối tập tin hoặc xảy ra lỗi. Hàm feof() và hàm ferror() (sẽ được thảo luận trong phần tới) có thể được dùng để xác định nguyên nhân. Các hàm fread() và fwrite() thường được gọi là các hàm đọc hoặc ghi không định dạng. Miễn là tập tin được mở cho các thao tác nhị phân, hàm fread() và fwrite() có thể độc và ghi bất kỳ kiểu thông tin nào. Ví dụ, chương trình sau đây ghi vào và sau đó đọc ngược ra một số kiểu double, một số kiểu int và một số kiểu long từ tập tin trên đĩa. Lưu ý rằng nó sử dụng hàm sizeof() để xác định độ dài của một kiểu dữ liệu.

16. 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á tŕ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à: Code:
    Trích dẫn:
    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 tập nhị phân cho đến cuối tập tin. Code:
    Mã:
    .
    .
    while (!feof())
       ch = fgetc(fp);
    .
    .

17. 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à:
Code:
Trích dẫn:
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().
Mã:
#include <stdio.h>

int main() {
  FILE *file;
  char *sentence;
 
  file = fopen("c:\\test1.txt", "a+");
  /* open or create a file for appending */

  if(file==NULL) {
    printf("Error: can't create file.\n");
    return 1;
  }
  else {
    printf("File created successfully.\n");

    printf("Enter a sentence: ");
    gets(sentence);

    fputs(sentence, file);
    fputs("\n", file);

    rewind(file);

    printf("Contents of the file: \n\n");

    /*while(fgets(sentence, 10, file)!=NULL) {
      printf("%s", sentence);
    }*/

    while(!feof(file)) {
      printf("%s", fgets(sentence, 10, file));
    }

    printf("\n");
    fclose(file);
    return 0;
  }
}


18. Hàm ferror()
    - Hàm ferror() xác định liệu một thao tác trên tập tin có sinh lỗi không. Nguyên mẫu của hàm là: Code:
    Trích dẫn:
    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ới 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.

19. 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à: Code:
    Trích dẫn:
    int remove (char *filename);
    - Nó trả về 0 nếu thành công ngược lại trả về một giá trị khác 0.

20. 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 nhập trong bộ nhớ và không thật sự hiển thị cho đến khi vùng đệm đầy. Nếu chương tŕ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ẻ 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 thi 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à: Code:
    Trích dẫn:
    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ê giá trị 0 nếu thành công, ngược lại, nó trả về EOF.

21. 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.
      + Nhập chuẩn (stdin) + Xuất chuẩn (stdout) + Lỗi chuẩn (stderr) + Máy in chuẩn (stdprn) + Thiêt bị hỗ trợ chuẩn (stdaux)
    - Trong đó, stdin, stdout 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.

22. 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 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ập 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à: Code:
    Trích dẫn:
    long int ftell(FILE *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 quy định khi gọi hàm fseek(). Nguyên mẫu của hàm fseek() là: Code:
    Trích dẫn:
    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 chi đị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 cho3 hằng kư tự hiệu (được định nghĩa trong stdio.h) như trong bảng 21,4: Hình ảnh - 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.

23. 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à scanf(). 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à: Code:
    Trích dẫn:
    int fprintf(FILE *fp, const char *control_string,..);
    int fscanf(FILE *fp, const char *control_string,..);
    - Trong đó fp là con trỏ tập tin 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.




 
Bài 22 : Quản lý tập tin

* Đọc, ghi và truy cập dữ liệu trong tập tin:


Code:
#include <stdio.h>

struct cust_st
{
    int acc_no;
    char cust_nm[30];
    float bal;
};

struct tran_st
{
   int acc_no;
   char trantype;
   float amt;
};

void main()
{
    int choice = 1;

    while(choice!=4)
    {
        clrscr();
        printf("\nSelect choice from menu\n\n1. Accept customer details\n2. Record Withdrawal/Deposit Transaction\n3. Printf Low Balance Report\n4. Exit\n\nEnter choice: ");
        scanf("%d", &choice);
        if(choice==1)
           addcust();
        else if(choice==2)
            rectran();
        else if(choice==3)
            prnlowbal();
    }
}

addcust()
{
   FILE *fp;
   char flag='y';
   struct cust_st custdata;

   clrscr();

   if((fp=open("customer","a+"))==NULL)
   {
      printf("\nERROR opening customer file");
      getch();
      return;
   }
   while(flag=='y')
   {
      printf("\n\nEnter Account Number: ");
      scanf("%d", &custdata.acc_no);
      printf("\n\nEnter Customer Name: ");
      scanf("%s", custdata.cust_nm);
      printf("\n\nEnter Account Balance: ");
      scanf("%f", &custdata.bal);
      fwrite(&custdata, sizeof(struct_cust_st), 1, fp);
      printf("\n\nAdd another? (y/n): ");
      scanf("%c", &flag);
   }
   fclose(fp);
}

rectran()
{
 FILE *fp1, *fp2;
 char flag = 'y', found, val_flag;
   struct cust_st custdata;
 struct tran_st trandata;
 int size = sizeof (struct cust_st);

 clrscr();
 if((fpl = fopen("customer","r+w"))==NULL)
 {
    printf("\nERROR opening customer file");
      getch();
      return;
   }

   if(fp2 = fopen("tran", "a+")==NULL)
   {
    printf("\nERROR opening transaction file");
      getch();
      return;
   }

   while(flag=='y')
   {
    printf("\n\nEnter Account number: ");
      scanf("%d", &trandata.acc_no);
      found='n';
      rewind(fp1);
      while((fread(&custdata, size, 1, fp)==1)&&(found=='n'))
      {
       if(custdata.acc_no == trandata.acc_no)
         {
          found = 'y';
            break;
         }
      }
      if(found=='y')
      {
       while(val_flag=='n')
         {
          printf("\nEnter Transaction type (D/W): ");
            scanf("%c", &trandata.trantype);
            if(trandata.trantype!='D' && trandata.trantype!='d' && trandata.trantype!='W' && trandata.trantype!='w')
               printf("\t\tInvalid transaction type, please reenter");
            else
             val_flag = 'y';
         }
         val_flag ='n';
         while(val_flag=='n')
         {
          printf("\nEnter amount: ");
            scanf("%f", &trandata.amt);
            if(trandata.trantype=='W' || trandata.trantype=='w')
            {
             if(trandata.amt > custdata.bal)
               {
                printf("\nAccount balance is %0.2f. Please reenter withdrawal amount.", custdata.bal);
               }
               else
               {
                custdata.bal -= trandata.amt;
                  val_flag = 'y';
               }
            }
            else
            {
             custdata.bal += trandata.amt;
               val_flag = 'y';
            }
         }
         fwrite(&trandata, sizeof(struct tran_st), 1, fp2);
         fseek(&custdata, size, 1, fp1);
      }
      else
       printf("\nThis account number does not exist");
      printf("\nRecord another transaction? (y/n): ");
      scanf("%c", &flag);
   }
   fclose(fp1);
   fclose(fp2);
}

prnlowbal()
{
 FILE *fp;
   struct cust_st custdata;
   char flag='n';

   clrscr();
   if(fp = fopen("customer", "r")==NULL)
   {
    printf("\nERROR opening customer file");
      getch();
      return;
   }

   printf("\nReport on account balances below 250\n\n");
   while(fread(&custdata, sizeof(struct cust_st), 1, fp))
   {
    if(custdata.bal < 250)
      {
       flag ='y';
         printf("\n%d\t%s\t%.2f", custdata.acc_no, custdata.cust_nm, custdata.bal);
   printf("\nNo Account balances found below 250");
      }
      getch();
      fclose(fp); 
   }
}
Bài tập bắt buộc

- Viết một chương trình C để hiển thị sự khác nhau giữa hai tập tin nhập vào như là đối số của dòng lệnh. Với mỗi sự khác nhau, hiển thị vị trí tìm thấy sự khác nhau và các ký tự của hai tập tin tại vị trí đó. Cũng cần phải bảo đảm rằng người sử dụng đã nhập vào số lượng đối số hợp lệ. Cuối cùng hiển thị tổng số sự khác nhau đã tìm thấy. Để làm điều này:

a. Khai báo các biến argv và argc để nhận vào đối số từ dòng lệnh.
b. Khai báo con trỏ đến hai tập tin.
c. Kiểm tra tính hợp lệ của argc để bảo đảm rằng đã nhập đúng số đối số.
d. Mở hai tập tin ở chế độ đọc.
e. Đặt một vòng lặp để đọc từng ký tự từ hai tập tin cho đến khi cuối cả hai tập tin.
f. Nếu các ký tự là khác nhau, hiển thị chúng cùng với vị trí của chúng. Tăng số điểm sự khác nhau lên 1.
g. Nếu đi đến cuối của một tập tin, in các ký tự còn lại trong tập tin kia như là sự khác biệt.
h. Kiểm tra số đếm sự khác nhau để hiển thị các thông báo thích hợp.
i. Đóng hai tập tin.

Hết bài 12

Bài tập tự làm


1. Việt một chương trình C để sao chép nội dung của một tập tin vào một tập tin khác loại trừ các từ a, an và the.
2. Viết một chương trình C để nhập vào hai chuỗi số. Lưu trữ mỗi chuối ở hai tập tin riêng biệt. Sắp xếp chuỗi trong mỗi tập tin. Trộn hai chuỗi vào một, sắp xếp và lưu lại chuỗi kết quả vào một tập tin mới. Hiển thị nội dung của tập tin mới.
__________________
Nói là một chuyện, làm là một chuyện. Chi bằng ta làm xong rồi hãy nói như vậy thì nói và làm sẽ là một chuyện.

Không có nhận xét nào:

Đăng nhận xét