檔案讀寫
上一章我們講解了 C 語言處理的標準輸入和輸出設備,本章我們將介紹 C 程式員如何創建、打開、關閉文本檔案或二進制檔案,
一個檔案,無論它是文本檔案還是二進制檔案,都是代表了一系列的位元組,C 語言不僅提供了訪問頂層的函式,也提供了底層(OS)呼叫來處理存盤設備上的檔案,本章將講解檔案管理的重要呼叫,
打開檔案
您可以使用 fopen( ) 函式來創建一個新的檔案或者打開一個已有的檔案,這個呼叫會初始化型別 FILE 的一個物件,型別 FILE 包含了所有用來控制流的必要的資訊,下面是這個函式呼叫的原型:
FILE *fopen( const char * filename, const char * mode );
在這里,filename 是字串,用來命名檔案,訪問模式 mode 的值可以是下列值中的一個:
| 模式 | 描述 |
|---|---|
| r | 打開一個已有的文本檔案,允許讀取檔案, |
| w | 打開一個文本檔案,允許寫入檔案,如果檔案不存在,則會創建一個新檔案,在這里,您的程式會從檔案的開頭寫入內容,如果檔案存在,則該會被截斷為零長度,重新寫入, |
| a | 打開一個文本檔案,以追加模式寫入檔案,如果檔案不存在,則會創建一個新檔案,在這里,您的程式會在已有的檔案內容中追加內容, |
| r+ | 打開一個文本檔案,允許讀寫檔案, |
| w+ | 打開一個文本檔案,允許讀寫檔案,如果檔案已存在,則檔案會被截斷為零長度,如果檔案不存在,則會創建一個新檔案, |
| a+ | 打開一個文本檔案,允許讀寫檔案,如果檔案不存在,則會創建一個新檔案,讀取會從檔案的開頭開始,寫入則只能是追加模式, |
如果處理的是二進制檔案,則需使用下面的訪問模式來取代上面的訪問模式:
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
關閉檔案
為了關閉檔案,請使用 fclose( ) 函式,函式的原型如下:
int fclose( FILE *fp );
如果成功關閉檔案,fclose( ) 函式回傳零,如果關閉檔案時發生錯誤,函式回傳 EOF,這個函式實際上,會清慷訓沖區中的資料,關閉檔案,并釋放用于該檔案的所有記憶體,EOF 是一個定義在頭檔案 stdio.h 中的常量,
C 標準庫提供了各種函式來按字符或者以固定長度字串的形式讀寫檔案,
寫入檔案
下面是把字符寫入到流中的最簡單的函式:
int fputc( int c, FILE *fp );
函式 fputc() 把引數 c 的字符值寫入到 fp 所指向的輸出流中,如果寫入成功,它會回傳寫入的字符,如果發生錯誤,則會回傳 EOF,您可以使用下面的函式來把一個以 null 結尾的字串寫入到流中:
int fputs( const char *s, FILE *fp );
函式 fputs() 把字串 s 寫入到 fp 所指向的輸出流中,如果寫入成功,它會回傳一個非負值,如果發生錯誤,則會回傳 EOF,您也可以使用
int fprintf(FILE *fp,const char *format, …)
函式來把一個字串寫入到檔案中,嘗試下面的實體:
注意:請確保您有可用的 tmp 目錄,如果不存在該目錄,則需要在您的計算機上先創建該目錄,
/tmp 一般是 Linux 系統上的臨時目錄,如果你在 Windows 系統上運行,則需要修改為本地環境中已存在的目錄,例如: C:\tmp、D:\tmp等,
#include <stdio.h>
int main()
{
FILE *fp = NULL;
fp = fopen("/tmp/test.txt", "w+");
fprintf(fp, "This is testing for fprintf...\n");
fputs("This is testing for fputs...\n", fp);
fclose(fp);
}
當上面的代碼被編譯和執行時,它會在 /tmp 目錄中創建一個新的檔案 test.txt,并使用兩個不同的函式寫入兩行,接下來讓我們來讀取這個檔案,
讀取檔案
下面是從檔案讀取單個字符的最簡單的函式:
int fgetc( FILE * fp );
fgetc() 函式從 fp 所指向的輸入檔案中讀取一個字符,回傳值是讀取的字符,如果發生錯誤則回傳 EOF,
下面的函式允許您從流中讀取一個字串:
char *fgets( char *buf, int n, FILE *fp );
函式 fgets() 從 fp 所指向的輸入流中讀取 n - 1 個字符,它會把讀取的字串復制到緩沖區 buf,并在最后追加一個 null 字符來終止字串,如果這個函式在讀取最后一個字符之前就遇到一個換行符 '\n' 或檔案的末尾 EOF,則只會回傳讀取到的字符,包括換行符,
您也可以使用:
int fscanf(FILE *fp, const char *format, ...)
fscanf函式從檔案中讀取字串,但是在遇到第一個空格和換行符時,它會停止讀取,
#include <stdio.h>
int main()
{
FILE *fp = NULL;
char buff[255];
fp = fopen("/tmp/test.txt", "r");
fscanf(fp, "%s", buff);
printf("1: %s\n", buff );
fgets(buff, 255, (FILE*)fp);
printf("2: %s\n", buff );
fgets(buff, 255, (FILE*)fp);
printf("3: %s\n", buff );
fclose(fp);
}
當上面的代碼被編譯和執行時,它會讀取上一部分創建的檔案,產生下列結果:
1: This
2: is testing for fprintf...3: This is testing for fputs...
首先,fscanf() 方法只讀取了 This,因為它在后邊遇到了一個空格,其次,呼叫 fgets() 讀取剩余的部分,直到行尾,最后,呼叫 fgets() 完整地讀取第二行,
之所以會從上一次讀取的位置繼續讀,是因為它會有一個指標指向上一次讀取到那個位置,下一次再讀取的時候直接從這個指標的位置開始,但前提是你沒有在中途呼叫fclose函式來關閉檔案,
二進制 I/O 函式
下面兩個函式用于二進制輸入和輸出:
size_t fread(void *ptr, size_t size_of_elements,
size_t number_of_elements, FILE *a_file);
size_t fwrite(const void *ptr, size_t size_of_elements,
size_t number_of_elements, FILE *a_file);
這兩個函式都是用于存盤塊的讀寫 - 通常是陣列或結構體,
前處理器
C 前處理器不是編譯器的組成部分,但是它是編譯程序中一個單獨的步驟,簡言之,C 前處理器只不過是一個文本替換工具而已,它們會指示編譯器在實際編譯之前完成所需的預處理,我們將把 C 前處理器(C Preprocessor)簡寫為 CPP,
所有的前處理器命令都是以井號(#)開頭,它必須是第一個非空字符,為了增強可讀性,前處理器指令應從第一列開始,下面列出了所有重要的前處理器指令:
| 指令 | 描述 |
|---|---|
| #define | 定義宏 |
| #include | 包含一個源代碼檔案 |
| #undef | 取消已定義的宏 |
| #ifdef | 如果宏已經定義,則回傳真 |
| #ifndef | 如果宏沒有定義,則回傳真 |
| #if | 如果給定條件為真,則編譯下面代碼 |
| #else | #if 的替代方案 |
| #elif | 如果前面的 #if 給定條件不為真,當前條件為真,則編譯下面代碼 |
| #endif | 結束一個 #if……#else 條件編譯塊 |
| #error | 當遇到標準錯誤時,輸出錯誤訊息 |
| #pragma | 使用標準化方法,向編譯器發布特殊的命令到編譯器中 |
前處理器實體
分析下面的實體來理解不同的指令,
#define MAX_ARRAY_LENGTH 20
這個指令告訴 CPP 把所有的 MAX_ARRAY_LENGTH 替換為 20,使用 #define 定義常量來增強可讀性,
#include <stdio.h>
#include "myheader.h"
第一行指令告訴 CPP 從系統庫中獲取 stdio.h,并添加文本到當前的源檔案中,
第二行告訴 CPP 從本地目錄中獲取 myheader.h,并添加內容到當前的源檔案中,
#undef FILE_SIZE
#define FILE_SIZE 42
這個指令告訴 CPP 取消已定義的 FILE_SIZE,并定義它為 42,
#ifndef MESSAGE
#define MESSAGE "You wish!"
#endif
這個指令告訴 CPP 只有當 MESSAGE 未定義時,才定義 MESSAGE,
#ifdef DEBUG
/* Your debugging statements here */
#endif
這個指令告訴 CPP 如果定義了 DEBUG,則執行處理陳述句,在編譯時,如果您向 gcc 編譯器傳遞了 -DDEBUG 開關量,這個指令就非常有用,它定義了 DEBUG,您可以在編譯期間隨時開啟或關閉除錯,
預定義宏
ANSI C 定義了許多宏,在編程中您可以使用這些宏,但是不能直接修改這些預定義的宏,
| 宏 | 描述 |
|---|---|
| _DATE_ | 當前日期,一個以 "MMM DD YYYY" 格式表示的字符常量, |
| _TIME_ | 當前時間,一個以 "HH:MM:SS" 格式表示的字符常量, |
| _FILE_ | 這會包含當前檔案名,一個字串常量, |
| _LINE_ | 這會包含當前行號,一個十進制常量, |
| _STDC_ | 當編譯器以 ANSI 標準編譯時,則定義為 1, |
讓我們來嘗試下面的實體:
#include <stdio.h>
main()
{
printf("File :%s\n", __FILE__ );
printf("Date :%s\n", __DATE__ );
printf("Time :%s\n", __TIME__ );
printf("Line :%d\n", __LINE__ );
printf("ANSI :%d\n", __STDC__ );
}
當上面的代碼(在檔案 test.c 中)被編譯和執行時,它會產生下列結果:
File :test.c
Date :Dec 12 2019
Time :16:34:13
Line :8
ANSI :1
前處理器運算子
C 前處理器提供了下列的運算子來幫助您創建宏:
宏延續運算子(\)
一個宏通常寫在一個單行上,但是如果宏太長,一個單行容納不下,則使用宏延續運算子(\),例如:
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
字串常量化運算子(#)
在宏定義中,當需要把一個宏的引數轉換為字串常量時,則使用字串常量化運算子(#),在宏中使用的該運算子有一個特定的引數或引數串列,例如:
#include <stdio.h>
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
int main(void)
{
message_for(Carole, Debra);
return 0;
}
當上面的代碼被編譯和執行時,它會產生下列結果:
Carole and Debra: We love you!
標記粘貼運算子(##)
宏定義內的標記粘貼運算子(##)會合并兩個引數,它允許在宏定義中兩個獨立的標記被合并為一個標記,例如:
#include <stdio.h>
#define tokenpaster(n) printf ("token" #n " = %d", token##n)
int main(void)
{
int token34 = 40;
tokenpaster(34);
return 0;
}
當上面的代碼被編譯和執行時,它會產生下列結果:
token34 = 40
這是怎么發生的,因為這個實體會從編譯器產生下列的實際輸出:
printf ("token34 = %d", token34);
這個實體演示了 token##n 會連接到 token34 中,在這里,我們使用了字串常量化運算子(#)和標記粘貼運算子(##),
defined() 運算子
前處理器 defined 運算子是用在常量運算式中的,用來確定一個識別符號是否已經使用 #define 定義過,如果指定的識別符號已定義,則值為真(非零),如果指定的識別符號未定義,則值為假(零),下面的實體演示了 defined() 運算子的用法:
#include <stdio.h>
#if !defined (MESSAGE)
#define MESSAGE "You wish!"
#endif
int main(void)
{
printf("Here is the message: %s\n", MESSAGE);
return 0;
}
當上面的代碼被編譯和執行時,它會產生下列結果:
Here is the message: You wish!
引數化的宏
CPP 一個強大的功能是可以使用引數化的宏來模擬函式,例如,下面的代碼是計算一個數的平方:
int square(int x) {
return x * x;
}
我們可以使用宏重寫上面的代碼,如下:
#define square(x) ((x) * (x))
在使用帶有引數的宏之前,必須使用 #define 指令定義,引數串列是括在圓括號內,且必須緊跟在宏名稱的后邊,宏名稱和左圓括號之間不允許有空格,例如:
#include <stdio.h>
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void)
{
printf("Max between 20 and 10 is %d\n", MAX(10, 20));
return 0;
}
當上面的代碼被編譯和執行時,它會產生下列結果:
Max between 20 and 10 is 20
參考自:https://www.runoob.com/cprogramming/c-tutorial.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/56863.html
標籤:C
