C文件概述
所謂“文件”是指一組相關數據的有序集合。這個數據集有一個名稱,叫做文件名。實際上在前面的各章中我們已經多次使用了文件,例如源程序文件、目標文件、可執行文件、庫文件 (頭文件)等。
文件通常是駐留在外部介質(如磁盤等)上的,在使用時才調入內存中來。從不同的角度可對文件作不同的分類。從用戶的角度看,文件可分爲普通文件和設備文件兩種。
操作系統是以文件爲單位對數據進行管理的。
文件的分類
從用戶觀點:
特殊文件(標準輸入輸出文件或標準設備文件)
普通文件(磁盤文件)
從操作系統的角度看,每一個與主機相連的輸入、輸出設備看作是一個文件。
例:輸入文件:終端鍵盤
輸出文件:顯示屏和打印機
按數據的組織形式:
ASCII文件(文本文件):每一個字節放一個ASCII代碼
二進制文件:把內存中的數據按其在內存中的存儲形式原樣輸出到磁盤上存放。
例如整數10000D在內存中的存儲形式以及分別按ASCII碼形式和二進制形式輸出如下圖所示:
ASCII文件和二進制文件的比較:
ASCII文件便於對字符進行逐個處理,也便於輸出字符。但一般佔存儲空間較多,而且要花費轉換時間。
二進制文件可以節省外存空間和轉換時間,但一個字節並不對應一個字符,不能直接輸出字符形式。
一般中間結果數據需要暫時保存在外存上,以後又需要輸入內存的,常用二進制文件保存。
C語言對文件的處理方法
緩衝文件系統:系統自動地在內存區爲每一個正在使用的文件開闢一個緩衝區。用緩衝文件系統進行的輸入輸出又稱爲高級磁盤輸入輸出。
非緩衝文件系統:系統不自動開闢確定大小的緩衝區,而由程序爲每個文件設定緩衝區。用非緩衝文件系統進行的輸入輸出又稱爲低級輸入輸出系統。
課外知識
在UNIX系統下,用緩衝文件系統來處理文本文件,用非緩衝文件系統來處理二進制文件。
ANSI C 標準只採用緩衝文件系統來處理文本文件和二進制文件。
C語言中對文件的讀寫都是用庫函數來實現。
文件的打開與關閉
文件型指針變量:
FILE *fp;
fp是一個指向FILE類型結構體的指針變量。
我們使fp指向某一個文件的結構體變量,從而通過該結構體變量中的文件信息能夠訪問該文件。
如果有n個文件,一般應設n個指針變量,使它們分別指向n個文件,以實現對文件的訪問。
FILE類型的數組:
FILE f[5]; 定義了一個結構體數組f,它有5個元素,可以用來存放5個文件的信息。
一頭霧水?不明白?接着看具體的使用就明白啦~
一.文件的打開(fopen函數)
函數調用:
FILE *fp;
fp = fopen(文件名,使用文件方式);
注意:
需要打開的文件名,也就是準備訪問的文件的名字
使用文件的方式(“讀”還是“寫”等);
讓哪一個指針變量指向被打開的文件。
文件使用方式
文件使用方式 | 含 義 |
“r” |
(只讀)爲輸入打開一個文本文件 |
“w” | (只寫)爲輸出打開一個文本文件 |
“a” | (追加)向文本文件尾增加數據 |
“rb” | (只讀)爲輸入打開一個二進制文件 |
“wb” | (只寫)爲輸出打開一個二進制文件 |
"ab“ | (追加)向二進制文件尾增加數據 |
"r+“ | (讀寫)爲讀/寫打開一個文本文件 |
"w+” | (讀寫)爲讀/寫建立一個新的文本文件 |
"a+” | (讀寫)爲讀/寫打開一個文本文件 |
"rb+“ | (讀寫)爲讀/寫打開一個二進制文件 |
“wb+“ | (讀寫)爲讀/寫建立一個新的二進制文件 |
“ab+” | (讀寫)爲讀/寫打開一個二進制文件 |
對於文件使用方式有以下幾點說明
凡用“r”打開一個文件時,該文件必須已經存在,且只能從該文件讀出。
用“w”打開的文件只能向該文件寫入。若打開的文件不存在,則以指定的文件名建立該文件,若打開的文件已經存在,則將該文件刪去,重建一個新文件。
若要向一個已存在的文件追加新的信息,只能用“a”方式打開文件。但此時該文件必須是存在的,否則將會出錯。
在打開一個文件時,如果出錯,fopen將返回一個空指針值NULL。
在程序中可以用這一信息來判別是否完成打開文件的工作,並作相應的處理。
把一個文本文件讀入內存時,要將ASCII碼轉換成二進制碼,而把文件以文本方式寫入磁盤時,也要把二進制碼轉換成ASCII碼,因此文本文件的讀寫要花費較多的轉換時間。對二進制文件的讀寫不存在這種轉換。
舉例:
//文件打開
#include <stdio.h>
#include <stdlib.h>
void main()
{
FILE *fp;
if (!(fp = fopen("H:\\csdn.txt", "rb"))) // 注意要雙“ \”
{
printf("Can not open H:\\csdn file!\n");
//system("pause");//暫停在黑色窗口
}
else
{
printf("Open success!\n");
}
}
最好把每種打開方式都試一下,看看有什麼區別。
二、文件的關閉(fclose函數)
函數調用:
fclose(文件指針);
函數功能:
使文件指針變量不指向該文件,也就是文件指針變量與文件“脫鉤”,此後不能再通過該指針對原來與其相聯繫的文件進行讀寫操作。
返回值:
關閉成功返回值爲0;否則返回EOF(-1)
文件的讀寫
對文件的讀和寫是最常用的文件操作。在C語言中提供了多種文件讀寫的函數:
字符讀寫函數 :fgetc 和 fputc
字符串讀寫函數:fgets 和 fputs
數據塊讀寫函數:fread 和 fwrite
格式化讀寫函數:fscanf 和 fprinf
下面分別予以介紹。使用以上函數都要求包含頭文件stdio.h。
字符讀寫函數:fgetc和fputc
一、字符輸入輸出函數(fputc()和fgetc())
fputc()函數調用:
fputc ( ch,fp ) ;
函數功能:
將字符(ch的值)輸出到 fp 所指向的文件中去。
對於fputc函數的使用要說明幾點
用寫或讀寫方式打開一個已存在的文件時將清除原有的文件內容,寫入字符從文件首開始。如需保留原有文件內容,希望寫入的字符以文件末開始存放,必須以追加方式打開文件。被寫入的文件若不存在,則創建該文件。
每寫入一個字符,文件內部位置指針向後移動一個字節。
fputc函數有一個返回值,如寫入成功則返回寫入的字符,否則返回一個EOF。可用此來判斷寫入是否成功。
//文件寫入
#include <stdio.h>
#include <stdlib.h>
void main()
{
FILE *fp;
char ch, filename[20];
printf("Please input the filename you want to write: ");
scanf("%s", filename);
if (!(fp = fopen(filename, "wt+")))
{
printf("Cannot open the file!\n");
exit(0); // 終止程序
}
printf("Please input the sentences you want to write: ");
// ch = getchar(); // 請思考……
ch = getchar();
while (ch != EOF) // ctrl + z
{
fputc(ch, fp);
ch = getchar();
}
fclose(fp);
}
第一個 ch = getchar(); 是爲了消除scanf 產生的換行,如果沒有這一行,產生的文件的開頭就會多一個空行。
字符讀寫函數:fgetc和fputc
fgetc()函數調用:
ch = fgetc(fp);
函數功能:
其意義是從打開的文件 fp 中讀取一個字符並送入 ch 中。
對於fgetc函數的使用要說明幾點
在fgetc函數調用中,讀取的文件必須是以讀或讀寫方式打開的。
在文件內部有一個位置指針。用來指向文件的當前讀寫字節。
在文件打開時,該指針總是指向文件的第一個字節。使用fgetc 函數後,該位置指針將向後移動一個字節。因此可連續多次使用fgetc函數,讀取多個字符。
應注意文件指針和文件內部的位置指針不是一回事。
文件指針是指向整個文件的,須在程序中定義說明,只要不重新賦值,文件指針的值是不變的。
文件內部的位置指針用以指示文件內部的當前讀寫位置,每讀寫一次,該指針均向後移動,它不需在程序中定義說明,而是由系統自動設置的。
#include <stdio.h>
#include <stdlib.h>
void main()
{
FILE *fp;
char ch= 0, filename[20];
printf("Please input the filename you want to read: ");
scanf("%s", filename);
if (!(fp = fopen(filename, "r")))
{
printf("Cannot open the file!\n");
exit(0); // 終止程序
}
while (ch != EOF) // ctrl + z
{
ch = fgetc(fp);
putchar(ch);
}
fclose(fp);
}
補充 一下
從一個文本文件順序讀入字符並在屏幕上顯示出來:
ch = fgetc(fp);
while(ch!= EOF)
{
putchar(ch);
ch = fgetc(fp);
}
注意:EOF不是可輸出字符,因此不能在屏幕上顯示。由於字符的ASCII碼不可能出現-1,因此EOF定義爲-1是合適的。當讀入的字符值等於-1時,表示讀入的已不是正常的字符而是文件結束符。
補充 兩下
從一個二進制文件順序讀入字符:
while(!feof(fp))
{
ch = fgetc(fp);
}
注意:ANSI C提供一個feof()函數來判斷文件是否真的結束。如果是文件結束,函數feof(fp)的值爲1(真);否則爲0(假)。以上也適用於文本文件的讀取。
作業:圖片、文件合成器!
什麼是圖片、文件合成?
就是有一張圖片A(名爲a.jpg)和一個其他的文件類型(例如壓縮包B文件b.rar),我們將他們合成,得到一個文件C,當我們把文件C的後綴名改爲.jpg時,是一個可以打開顯示爲A圖片的圖片文件,當我們將C的後綴名改爲.rar時,它是一個可以打開的爲B壓縮包的文件。
原理就是把B文件的源碼放到A文件的源碼的後面,合成的文件就是文件C。
#include <stdio.h>
#include <stdlib.h>
void main()
{
FILE *f_file, *f_pic, *f_finish;
char ch, pic_name[20], file_name[20], finish_name[20];
printf("請輸入要合成的圖片、文件名稱: \n");
printf("圖片: ");
scanf("%s", pic_name);
printf("文件: ");
scanf("%s", file_name);
printf("結果: ");
scanf("%s", finish_name);
if( !(f_pic = fopen(pic_name, "rb")))
{
printf("Cannot open the ficture %s!\n", pic_name);
exit(0); // 終止程序
}
if( !(f_file = fopen(file_name, "rb")))
{
printf("Cannot open the file %s!\n", file_name);
exit(0); // 終止程序
}
if( !(f_finish = fopen(finish_name, "wb")))
{
printf("Cannot open the file %s!\n", finish_name);
exit(0); // 終止程序
}
while( !feof(f_pic) ) //feof()測試是否爲文件結尾,不是文件的結尾就返回0
{
ch = fgetc(f_pic);
fputc(ch, f_finish);
}
fclose(f_pic);
while( !feof(f_file) )
{
ch = fgetc(f_file);
fputc(ch, f_finish);
}
fclose(f_file);
fclose(f_finish);
}
字符串讀寫函數:fgets和fputs
二、字符串輸入輸出函數(fputs()和fgets())
fgets函數
函數調用形式如:
fgets(str,n,fp);
函數作用:
從fp所指的文件中讀出n-1個字符送入字符數
組str中,因爲在最後加一個’\0’。
返回值:
str的首地址
//字符串讀出
#include <stdio.h>
#include <stdlib.h>
#define LEN 50
void main()
{
FILE *fp;
char buffer[LEN];
if (!(fp = fopen("csdn.txt", "rt")))
{
printf("\nCannot open file strike any key exit!");
exit(1);
}
fgets(buffer, LEN, fp);
printf("%s\n", buffer);
fclose(fp);
}
fputs函數
函數調用方式:
fputs(“csdn.net”,fp);
函數作用:
其意義是把字符串“csdn.net”寫入fp所指的文件之中。
返回值:
輸入成功,返回值爲0;
輸入失敗,返回EOF.
//字符串寫入
#include <stdio.h>
#include <stdlib.h>
#define LEN 20
void main()
{
FILE *fp;
char ch, buffer[LEN];
if (!(fp = fopen("csdn.txt", "at+")))
{
printf("Cannot open file strike any key exit!");
exit(1);
}
printf("Please input a string:\n");
fgets(buffer, LEN, stdin); // 爲什麼不用scanf()? 因爲scanf() 不能接收有空格的字符串。
//stdin又是啥? stdin 文件是鍵盤輸入緩衝文件。
fputs(buffer, fp);
rewind(fp); // 重新定義文件內部指針去到開頭處
ch = fgetc(fp);
while (ch != EOF)
{
putchar(ch);
ch = fgetc(fp);
}
printf("\n");
fclose(fp);
}
數據塊讀寫函數(fread()和fwrite())
函數調用:
fread (buffer, size, count, fp);
fwrite(buffer, size, count, fp);
參數說明:
buffer:是一個指針。
對fread 來說,它是讀入數據的存放地址。
對fwrite來說,是要輸出數據的地址(均指起始地址)。
size: 要讀寫的字節數。
count: 要進行讀寫多少個size字節的數據項。
fp: 文件型指針。
使用舉例
若有如下結構類型:
struct student_type
{char name[10];
int num;
int age;
char addr[30];} stud[40];
可以用fread和fwrite來進行數據的操作:
for(i=0;i<40;i++)
fread(&stud[i],sizeof(struct student-type),1,fp);
for(i=0;i<40,i++)
fwrite(&stud[i],sizeof(struct student-type),1,fp);
要求:
從鍵盤輸入4個學生的有關數據,然後把它們以二進制的格式存儲到磁盤文件中。
#include <stdio.h>
#define SIZE 4
struct student
{
char name[10];
int num;
int age;
char addr[15];
}stu[SIZE];
void save()
{
FILE *fp;
int i;
if (!(fp = fopen("student-list", "wb")))
{
printf("Cannot open the file!\n");
return;
}
for (i = 0; i < SIZE; i++)
{
if (fwrite(&stu[i], sizeof(struct student), 1, fp) != 1)
{
printf("File write error!\n");
fclose(fp);
}
}
}
void main()
{
int i;
printf("Please input the student's name, num, age and address: \n");
for (i = 0; i < SIZE; i++)
{
scanf("%s %d %d %s", stu[i].name, &stu[i].num, &stu[i].age, &stu[i].addr);
}
save();
}
// 作業: 寫一個load()函數將該文件讀取並顯示出來! 聰明如你,認真思考一定能做到的!
答案請看下一節 -->> 零基礎入門學習C語言012講:文件操作(2)