深入浅出之文件操作
一、文件定義
所謂“文件”是記錄在外部介質(zhì)上的數(shù)據(jù)的集合。
二、緩沖文件系統(tǒng)(標(biāo)準(zhǔn)I/O)
目前C語言所使用的磁盤文件有兩大類,一類稱為緩沖文件系統(tǒng),又稱為標(biāo)準(zhǔn)文件系統(tǒng)或高層文件系統(tǒng);另一類稱為非緩沖文件系統(tǒng),又稱為底層文件系統(tǒng)。
緩沖文件系統(tǒng)的特點(diǎn)是對(duì)程序中的每一個(gè)文件都在內(nèi)存中開辟一個(gè)緩沖區(qū),從磁盤文件輸入的數(shù)據(jù)先輸入到緩沖區(qū)中,然后再通過緩沖區(qū)依次將數(shù)據(jù)送給接收變量。這樣做的目的是減少對(duì)磁盤文件讀寫次數(shù),因?yàn)槊恳淮螌?duì)磁盤的讀寫都要移動(dòng)磁頭并尋找磁道扇區(qū),這個(gè)過程是要花一些時(shí)間,如果每一次用讀寫函數(shù)時(shí)都對(duì)應(yīng)依次時(shí)間的磁盤訪問,那么就會(huì)花費(fèi)較多的讀寫時(shí)間。用緩沖區(qū)就可以依次讀入一批數(shù)據(jù),或輸出一批數(shù)據(jù),即不是執(zhí)行一次輸入或輸出函數(shù)就實(shí)際訪問磁盤一次,而是若干次讀寫函數(shù)語句對(duì)應(yīng)一次實(shí)際的磁盤訪問。緩沖文件系統(tǒng)自動(dòng)設(shè)置所需的緩沖區(qū),緩沖區(qū)的大小隨機(jī)器而異。
非緩沖區(qū)文件系統(tǒng)不由系統(tǒng)自動(dòng)設(shè)置緩沖區(qū),而是由用戶自己根據(jù)需要設(shè)置。
2.1 文件類型指針(FILE)
緩沖文件系統(tǒng)為每一個(gè)文件開辟了一個(gè)“文件信息區(qū)”,用來存放以上這些信息。
例如:FILE *fp;
注意:FILE不是結(jié)構(gòu)體變量名,它是用typedef定義的新類型名。
2.2 文件的打開與關(guān)閉
1)文件打開,格式:fp = fopen(文件指針變量,文件使用方式)
| r | 打開一個(gè)已有的文本文件,允許讀取文件。 |
| w | 打開一個(gè)文本文件,允許寫入文件。如果文件不存在,則會(huì)創(chuàng)建一個(gè)新文件。在這里,您的程序會(huì)從文件的開頭寫入內(nèi)容。如果文件存在,則該會(huì)被截?cái)酁榱汩L(zhǎng)度,重新寫入。 |
| a | 打開一個(gè)文本文件,以追加模式寫入文件。如果文件不存在,則會(huì)創(chuàng)建一個(gè)新文件。在這里,您的程序會(huì)在已有的文件內(nèi)容中追加內(nèi)容。 |
| r+ | 打開一個(gè)文本文件,允許讀寫文件。 |
| w+ | 打開一個(gè)文本文件,允許讀寫文件。如果文件已存在,則文件會(huì)被截?cái)酁榱汩L(zhǎng)度,如果文件不存在,則會(huì)創(chuàng)建一個(gè)新文件。 |
| a+ | 打開一個(gè)文本文件,允許讀寫文件。如果文件不存在,則會(huì)創(chuàng)建一個(gè)新文件。讀取會(huì)從文件的開頭開始,寫入則只能是追加模式。 |
返回值:如果順利打開,則將該文件信息區(qū)(結(jié)構(gòu)體變量)起始地址賦給指針變量fp,也就是fp所指向該文件信息區(qū)的結(jié)構(gòu)體;如果打開失敗,則fp的值為NULL,此時(shí)輸出信息“不能打開此文件”。
2)文件關(guān)閉,格式:fclose(文件指針變量)
它通知系統(tǒng),將此指針指向文件關(guān)閉,釋放相應(yīng)的文件信息區(qū)(結(jié)構(gòu)體變量)。如果不關(guān)閉文件,而直接使程序停止運(yùn)行,這時(shí)就會(huì)丟失緩沖區(qū)中還未寫入文件的信息。因此注意:文件使用完必須關(guān)閉。
2.3 文件的順序讀寫
2.3.1? 輸出一個(gè)字符
格式:fputc(ch,fp),把字符變量ch的值輸出到指針變量fp所指向的FILE結(jié)構(gòu)體的文件(簡(jiǎn)稱為指向該變文件)
#include <QCoreApplication>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);FILE *fp;const char str[]={"12345678"};if ((fp = fopen("file.txt","w")) == NULL){exit(0);}int i = 0;while ( str[i] != '\0'){fputc(str[i],fp);i++;}fclose(fp);return a.exec(); }2.3.2 從磁盤文件中接收一個(gè)字符
格式:ch =fgetc(fp),功能為從指針變量fp所指向的文件中讀入一個(gè)字符并賦給字符變量ch,fgetc函數(shù)的值就是該字符。如果執(zhí)行fgetc函數(shù)時(shí)遇到文件結(jié)束符,則函數(shù)返回文件結(jié)束符EOF(即-1)。注意這個(gè)-1并不是函數(shù)讀入的字符值,因?yàn)闆]有一個(gè)字符的ASCII碼為-1,當(dāng)操作系統(tǒng)判斷出文件中最后一個(gè)字符已被讀出時(shí),它使函數(shù)的返回值為-1.
#include <QCoreApplication>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);FILE *fp;char ch;if ((fp = fopen("file.txt","r")) == NULL){qDebug("can not open file");exit(0);}while ((ch = fgetc(fp)) != EOF){qDebug("ch:%c",ch);}fclose(fp);return a.exec(); } 輸出結(jié)果: ch:1 ch:2 ch:3 ch:4 ch:5 ch:6 ch:7 ch:82.3.3?輸入一個(gè)字符串
用fgets函數(shù)讀入一個(gè)字符串
格式:
char *fgets(char *str, int n, FILE *stream)功能為從fp指向的文件讀取n-1個(gè)字符,并把它放到字符數(shù)組str中。如果在讀入n-1個(gè)字符完成之前遇到換行符“\n”或文件結(jié)束符EOF,即結(jié)束讀入。但將遇到的換行符“\n”,也作為一個(gè)字符送入str數(shù)組,在讀入的字符串之后自動(dòng)加一個(gè)“\0”,因此送到str數(shù)組的字符串(包括“\0”)最多可占用n個(gè)字符。
fgets函數(shù)返回值為str數(shù)組的首地址,如果讀到文件尾或出錯(cuò)則返回NULL。
注意:fgets不能讀二進(jìn)制文件
#include <QCoreApplication>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);FILE *fp;char ch;char str[81];if ((fp = fopen("file.txt","r")) == NULL){qDebug("can not open file");exit(0);}while (fgets(str,81,fp) != NULL){qDebug("str:%s",str);}fclose(fp);return a.exec(); } 輸出結(jié)果: str:12345678char *gets(char *str)?從標(biāo)準(zhǔn)輸入 stdin 讀取一行,并把它存儲(chǔ)在 str 所指向的字符串中。當(dāng)讀取到換行符時(shí),或者到達(dá)文件末尾時(shí),它會(huì)停止,具體視情況而定。
如果成功,該函數(shù)返回 str。如果發(fā)生錯(cuò)誤或者到達(dá)文件末尾時(shí)還未讀取任何字符,則返回 NULL。
fgets會(huì)將換行符一并存入到指定的buf中,gets會(huì)丟掉換行符。
2.3.4 輸出一個(gè)字符串
用fputs函數(shù)輸出一個(gè)字符串
格式:
int fputs(const char *str, FILE *stream)把字符串寫入到指定的流 stream 中,但不包括空字符。把字符數(shù)組str中的字符串(或字符指針指向的字符串,或字符串常量)輸出到fp所指向的文件。但文件結(jié)束符“\0”不輸出。?
#include <QCoreApplication>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);FILE *fp;const char str[]={"12345678"};if ((fp = fopen("file.txt","w")) == NULL){exit(0);}fputs(str,fp);fclose(fp);return a.exec(); }int puts(const char *str)?把一個(gè)字符串寫入到標(biāo)準(zhǔn)輸出 stdout,直到空字符,但不包括空字符。換行符會(huì)被追加到輸出中。
如果成功,該函數(shù)返回一個(gè)非負(fù)值,如果發(fā)生錯(cuò)誤則返回 EOF。
fputs會(huì)將結(jié)尾的NULL丟掉,puts會(huì)自動(dòng)加上換行符。
2.3.5 格式化的輸出fprintf
格式化輸出:int fprintf(FILE *stream, const char *format, ...)fp 為文件指針,format 為格式控制字符串,… 表示參數(shù)列表。fprintf() 返回成功寫入的字符的個(gè)數(shù),失敗則返回負(fù)數(shù)。fscanf() 返回參數(shù)列表中被成功賦值的參數(shù)個(gè)數(shù)。?
| c | 字符 |
| d 或 i | 有符號(hào)十進(jìn)制整數(shù) |
| e | 使用 e 字符的科學(xué)科學(xué)記數(shù)法(尾數(shù)和指數(shù)) |
| E | 使用 E 字符的科學(xué)科學(xué)記數(shù)法(尾數(shù)和指數(shù)) |
| f | 十進(jìn)制浮點(diǎn)數(shù) |
| g | 自動(dòng)選擇 %e 或 %f 中合適的表示法 |
| G | 自動(dòng)選擇 %E 或 %f 中合適的表示法 |
| o | 有符號(hào)八進(jìn)制 |
| s | 字符的字符串 |
| u | 無符號(hào)十進(jìn)制整數(shù) |
| x | 無符號(hào)十六進(jìn)制整數(shù) |
| X | 無符號(hào)十六進(jìn)制整數(shù)(大寫字母) |
| p | 指針地址 |
| n | 無輸出 |
| % | 字符 |
返回值:如果成功,則返回寫入的字符總數(shù),否則返回一個(gè)負(fù)數(shù)。
#include <QCoreApplication>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);FILE *fp;if ((fp = fopen("file.txt","w")) == NULL){exit(0);}int len = fprintf(fp,"%d%d%d",123,456,78);qDebug("%d",len);fclose(fp);return a.exec(); } 輸出結(jié)果是: 8?2.3.6 格式化輸入fscanf
int fscanf(FILE *stream, const char *format, ...)| c | 單個(gè)字符:讀取下一個(gè)字符。如果指定了一個(gè)不為 1 的寬度 width,函數(shù)會(huì)讀取 width 個(gè)字符,并通過參數(shù)傳遞,把它們存儲(chǔ)在數(shù)組中連續(xù)位置。在末尾不會(huì)追加空字符。 | char * |
| d | 十進(jìn)制整數(shù):數(shù)字前面的 + 或 - 號(hào)是可選的。 | int * |
| e,E,f,g,G | 浮點(diǎn)數(shù):包含了一個(gè)小數(shù)點(diǎn)、一個(gè)可選的前置符號(hào) + 或 -、一個(gè)可選的后置字符 e 或 E,以及一個(gè)十進(jìn)制數(shù)字。兩個(gè)有效的實(shí)例 -732.103 和 7.12e4 | float * |
| o | 八進(jìn)制整數(shù)。 | int * |
| s | 字符串。這將讀取連續(xù)字符,直到遇到一個(gè)空格字符(空格字符可以是空白、換行和制表符)。 | char * |
| u | 無符號(hào)的十進(jìn)制整數(shù)。 | unsigned int * |
| x,X | 十六進(jìn)制整數(shù)。 | int * |
返回值:如果成功,該函數(shù)返回成功匹配和賦值的個(gè)數(shù)。如果到達(dá)文件末尾或發(fā)生讀錯(cuò)誤,則返回 EOF。?
#include <QCoreApplication>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);FILE *fp;char str[81];char str1[81];if ((fp = fopen("file.txt","r")) == NULL){exit(0);}int len = fscanf(fp,"%s",str);qDebug("%s,%d",str,len);int len1 = fscanf(fp,"%s",str1);qDebug("%s,%d",str1,len1);fclose(fp);return a.exec(); } 結(jié)果輸出: 12345678,1 ?98?,-1如果將 fp 設(shè)置為 stdin,那么 fscanf() 函數(shù)將會(huì)從鍵盤讀取數(shù)據(jù),與 scanf 的作用相同;設(shè)置為 stdout,那么 fprintf() 函數(shù)將會(huì)向顯示器輸出內(nèi)容,與 printf 的作用相同。例如:
#include<stdio.h> int main(){int a, b, sum;fprintf(stdout, "Input two numbers: ");fscanf(stdin, "%d %d", &a, &b);sum = a + b;fprintf(stdout, "sum=%d\n", sum);return 0; }??2.3.7 按數(shù)據(jù)塊的方式寫入fwrite
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)把?ptr?所指向的數(shù)組中的數(shù)據(jù)寫入到給定流?stream?中參數(shù)
- ptr?-- 這是指向要被寫入的元素?cái)?shù)組的指針。
- size?-- 這是要被寫入的每個(gè)元素的大小,以字節(jié)為單位。
- nmemb?-- 這是元素的個(gè)數(shù),每個(gè)元素的大小為 size 字節(jié)。
- stream?-- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象指定了一個(gè)輸出流。
返回值
如果成功,該函數(shù)返回nmemb。如果該數(shù)字與 nmemb 參數(shù)不同,則會(huì)顯示一個(gè)錯(cuò)誤。
#include <QCoreApplication>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);FILE *fp;char str[] = {"12345678"};if ((fp = fopen("file.txt","w")) == NULL){exit(0);}size_t len = fwrite(str,sizeof(str),2,fp);qDebug("%d",len);fclose(fp);return a.exec(); } 輸出結(jié)果是22.3.8 按數(shù)據(jù)塊的方式讀出fread
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)從給定流?stream?讀取數(shù)據(jù)到?ptr?所指向的數(shù)組中。參數(shù)
- ptr?-- 這是指向帶有最小尺寸?size*nmemb?字節(jié)的內(nèi)存塊的指針。
- size?-- 這是要讀取的每個(gè)元素的大小,以字節(jié)為單位。
- nmemb?-- 這是元素的個(gè)數(shù),每個(gè)元素的大小為 size 字節(jié)。
- stream?-- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象指定了一個(gè)輸入流。
返回值
成功返回nmemb。如果總數(shù)與 nmemb 參數(shù)不同,則可能發(fā)生了一個(gè)錯(cuò)誤或者到達(dá)了文件末尾。
#include <QCoreApplication>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);FILE *fp;char str[9];if ((fp = fopen("file.txt","r")) == NULL){exit(0);}size_t len = fread(str,sizeof(str),2,fp);qDebug("%s,%d",str,len);fclose(fp);return a.exec(); } 輸出結(jié)果:12345678,22.4 文件的定位與隨機(jī)讀寫
2.4.1 文件的定位fseek
int fseek(FILE *stream, long int offset, int whence)?設(shè)置流?stream?的文件位置為給定的偏移?offset,參數(shù) offset 意味著從給定的?whence?位置查找的字節(jié)數(shù)。參數(shù)
- stream?-- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象標(biāo)識(shí)了流。
- offset?-- 這是相對(duì) whence 的偏移量,以字節(jié)為單位。
- whence?-- 這是表示開始添加偏移 offset 的位置。它一般指定為下列常量之一:
| SEEK_SET(0) | 文件的開頭 |
| SEEK_CUR(1) | 文件指針的當(dāng)前位置 |
| SEEK_END(2) | 文件的末尾 |
“offset(位移量)”指以“起始點(diǎn)”向前移動(dòng)的字節(jié)數(shù)。如果它是負(fù)數(shù),表示向后移動(dòng),所謂向前就是指從文件開頭項(xiàng)文件末尾移動(dòng)的方向。位移量應(yīng)為long型。
fseek(fp,10L,SEEK_SET) 將位置移動(dòng)到離文件開始處10個(gè)字節(jié)
fseek(fp,-10L,SEEK_CUR)指位置指針從當(dāng)前位置向后移動(dòng)10個(gè)字節(jié)?
返回值
如果成功,則該函數(shù)返回零,否則返回非零值。
#include <QCoreApplication>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);FILE *fp;char str[9];if ((fp = fopen("file.txt","r")) == NULL){exit(0);}fseek(fp,3L,SEEK_SET);size_t len = fread(str,sizeof(str),2,fp);qDebug("%s,%d",str,len);qDebug("%d",ftell(fp));rewind(fp);len = fread(str,sizeof(str),1,fp);qDebug("%s,%d",str,len);qDebug("%d",ftell(fp));fclose(fp);return a.exec(); } 輸出結(jié)果: 45678,1 18 12345678,1 92.4.1 文件的定位ftell
long int ftell(FILE *stream)返回給定流 stream 的當(dāng)前文件位置。參數(shù)
- stream?-- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象標(biāo)識(shí)了流。
返回值
該函數(shù)返回位置標(biāo)識(shí)符的當(dāng)前值。如果發(fā)生錯(cuò)誤,則返回 -1L,全局變量 errno 被設(shè)置為一個(gè)正值。
#include <stdio.h>int main () {FILE *fp;int len;fp = fopen("file.txt", "r");if( fp == NULL ) {perror ("打開文件錯(cuò)誤");return(-1);}fseek(fp, 0, SEEK_END);len = ftell(fp);fclose(fp);printf("file.txt 的總大小 = %d 字節(jié)\n", len);return(0); }2.4.2 文件定位rewind?
void rewind(FILE *stream)設(shè)置文件位置為給定流?stream?的文件的開頭。 #include <stdio.h>int main() {char str[] = "This is runoob.com";FILE *fp;int ch;/* 首先讓我們?cè)谖募袑懭胍恍﹥?nèi)容 */fp = fopen( "file.txt" , "w" );fwrite(str , 1 , sizeof(str) , fp );fclose(fp);fp = fopen( "file.txt" , "r" );while(1){ch = fgetc(fp);if( feof(fp) ){break ;}printf("%c", ch);}rewind(fp);printf("\n");while(1){ch = fgetc(fp);if( feof(fp) ){break ;}printf("%c", ch);}fclose(fp);return(0); }2.5 文件操作的出錯(cuò)檢測(cè)
1) int ferror(FILE *stream)參數(shù)
- stream?-- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象標(biāo)識(shí)了流。
返回值
如果設(shè)置了與流關(guān)聯(lián)的錯(cuò)誤標(biāo)識(shí)符,該函數(shù)返回一個(gè)非零值(出錯(cuò)),否則返回一個(gè)零值(未出錯(cuò))。
#include <stdio.h>int main() {FILE *fp;char c;fp = fopen("file.txt", "w");c = fgetc(fp);if( ferror(fp) ){printf("讀取文件:file.txt 時(shí)發(fā)生錯(cuò)誤\n");}clearerr(fp);if( ferror(fp) ){printf("讀取文件:file.txt 時(shí)發(fā)生錯(cuò)誤\n");}fclose(fp);return(0); } 2) void clearerr(FILE *stream)清除給定流 stream 的文件結(jié)束和錯(cuò)誤標(biāo)識(shí)符。參數(shù)
- stream?-- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象標(biāo)識(shí)了流。
返回值
這不會(huì)失敗,且不會(huì)設(shè)置外部變量 errno,但是如果它檢測(cè)到它的參數(shù)不是一個(gè)有效的流,則返回 -1,并設(shè)置 errno 為 EBADF。
3) int feof(FILE *stream) 測(cè)試給定流 stream 的文件結(jié)束標(biāo)識(shí)符。參數(shù)
- stream?-- 這是指向 FILE 對(duì)象的指針,該 FILE 對(duì)象標(biāo)識(shí)了流。
返回值
當(dāng)設(shè)置了與流關(guān)聯(lián)的文件結(jié)束標(biāo)識(shí)符時(shí),該函數(shù)返回一個(gè)非零值(結(jié)尾),否則返回零(未結(jié)尾)。
#include <QCoreApplication>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);FILE *fp;char ch;if ((fp = fopen("file.txt","r")) == NULL){qDebug("can not open file");exit(0);}while (1){ch = fgetc(fp);qDebug("ch:%c",ch);if (feof(fp)){break;}}fclose(fp);return a.exec(); } 輸出結(jié)果是: ch:1 ch:2 ch:3 ch:4 ch:5 ch:6 ch:7 ch:8 ch:?2.6?feof 函數(shù)?
feof 函數(shù)的函數(shù)原型為:
int feof (FILE * fp);
所在頭文件:<stdio.h>
函數(shù)功能:檢查 fp 所關(guān)聯(lián)文件流中的結(jié)束標(biāo)志是否被置位,如果該文件的結(jié)束標(biāo)志已被置位,返回非 0 值;否則,返回 0。
需要注意的是:
1) 在文本文件和二進(jìn)制文件中,均可使用該函數(shù)判斷是否到達(dá)文件結(jié)尾。
2) 文件流中的結(jié)束標(biāo)志,是最近一次調(diào)用輸入等相關(guān)函數(shù)(如 fgetc、fgets、fread 及 fseek 等)時(shí)設(shè)置的。只有最近一次操作輸入的是非有效數(shù)據(jù)時(shí),文件結(jié)束標(biāo)志才被置位;否則,均不置位。
【例 3】從鍵盤輸入若干名學(xué)生的姓名、學(xué)號(hào)、語數(shù)外三門課成績(jī)并計(jì)算平均成績(jī),將這些學(xué)生信息以二進(jìn)制方式保存到當(dāng)前目錄文件 Stia_Info.dat 中。采用 fwrite 函數(shù)寫入數(shù)據(jù)。存儲(chǔ)空間要求采用數(shù)組形式。采用靜態(tài)數(shù)組形式,僅為了復(fù)習(xí)數(shù)組作為函數(shù)參數(shù)的情況,且便于理解,實(shí)際編程中不建議采用這種方案。
由于采用二進(jìn)制形式存儲(chǔ),故打開生成的二進(jìn)制文件 Stu_Info.dat 可能是“亂碼”。通過判斷文件的生成以及文件中部分顯示正常的數(shù)據(jù),可判斷代碼是否運(yùn)行正確。
2.7 保存結(jié)構(gòu)體到文件
#include <stdio.h> #include <errno.h> #include <string.h> typedef struct _Account{char usrname[128];char passwd[128];int age;int level; }Account; void write_account2file(const char *path, Account *account) {FILE *fp = NULL;fp = fopen(path, "w+");if (NULL == fp){printf("open %s fail, errno: %d", path, errno);return;}fwrite(account, sizeof(Account), 1, fp);fclose(fp);return; }void read_accountFromFile(const char *path, Account *account) {FILE *fp = fopen(path, "r");if (NULL == fp){printf("open %s fail, errno: %d", path, errno);return;}fread(account, sizeof(Account), 1, fp);fclose(fp); }void main(void) {#if 0Account account;strncpy(account.usrname, "fellow", strlen("fellow"));account.usrname[strlen("fellow")] = '\0';strncpy(account.passwd, "1111", strlen("1111"));account.passwd[strlen("1111")] = '\0';account.age = 18;account.level = 0;write_account2file("/mnt/hgfs/share/test/account.txt", &account);#endifAccount read;read_accountFromFile("/mnt/hgfs/share/test/account.txt", &read);printf("read:%s,%s,%d,%d\n", read.usrname, read.passwd, read.age,read.level);}三、非緩沖文件系統(tǒng)(系統(tǒng)I/O)
非緩沖文件系統(tǒng)沒有文件結(jié)構(gòu)體,不設(shè)文件系統(tǒng)類型文件,不能讀寫單個(gè)字符、字符串和格式化數(shù)據(jù)、一般用于二進(jìn)制文件,它只有一種文件讀寫方法,即成塊(包含多個(gè)字節(jié))讀入二進(jìn)制的數(shù)據(jù)。
從使用角度來看,緩沖文件系統(tǒng)功能強(qiáng)大,使用方便,但效率較低。而非緩沖文件系統(tǒng)最大的優(yōu)點(diǎn)是執(zhí)行效率高。
3.1 打開文件
open(文件名,使用方式)
使用方式
| 使用方式 | 意義 | |
| 宏名 | 數(shù)字 | |
| O_RDONLY | 1 | 打開一個(gè)文件,只能讀 |
| O_RDONLY | 2 | 打開一個(gè)文件,只能寫 |
| O_RDWR | 3 | 打開一個(gè)文件,能讀能寫 |
3.2 讀文件
用read函數(shù)實(shí)現(xiàn)系統(tǒng)級(jí)別I/O
格式:read(文件號(hào),緩沖地址,讀入最大字節(jié)數(shù))
例如read(fd,buff,512)
表示依次從fd所代表的文件中讀入512個(gè)字節(jié)到起始地址為buff的緩沖區(qū)中。如果文件可供本次讀入字節(jié)數(shù)不足,則將這些字節(jié)全部讀入。read函數(shù)返回值為實(shí)際讀入的字節(jié)數(shù)。如果返回0,表示文件結(jié)束,無讀入。如果讀操作失敗,則函數(shù)返回值為-1,。
3.3 寫文件
格式:write(文件號(hào),緩沖區(qū)地址,一次輸出的字節(jié)數(shù))
如:write(fd,buff,100)
表示將內(nèi)存中起始地址為buff的緩沖區(qū)中的100字節(jié)輸出到fd所代表的文件中去,可以看出,write函數(shù)可以輸出整個(gè)緩沖區(qū)的內(nèi)容,也可以輸出部分緩沖區(qū)內(nèi)容,如果成功,返回值為實(shí)際寫入的字節(jié)數(shù),如果失敗,則返回值為-1?。
3.4 關(guān)閉文件
格式:close(文件名)
例如:close(fd)的作用是關(guān)閉fd所代表的文件
3.5 緩沖區(qū)設(shè)置
非緩沖系統(tǒng)不自設(shè)置緩沖區(qū),而由設(shè)計(jì)者自己設(shè)置緩沖區(qū),也就是說緩沖區(qū)是程序的一部分,它的位置在用戶緩沖區(qū)中。在讀入數(shù)據(jù)時(shí),由磁盤文件將數(shù)據(jù)讀到緩沖區(qū)中,然后由程序可以直接引用緩沖區(qū)中的數(shù)據(jù),在輸出時(shí),將緩沖區(qū)中的數(shù)據(jù)送到磁盤文件。實(shí)際上,常用一個(gè)數(shù)組來作為緩沖區(qū)。
設(shè)置緩沖區(qū)的方法,實(shí)際上就是講數(shù)據(jù)輸入到該數(shù)組中,由程序?qū)?shù)組內(nèi)容進(jìn)行處理。
四、C++文件流操作
在C++程序中使用的保存數(shù)據(jù)的文件按存儲(chǔ)格式分為兩種類型,一種是文本文件,一種是二進(jìn)制文件。文本文件又稱為ASCII碼文件或字符文件,二進(jìn)制文件又稱為字節(jié)文件。在文本文件中,每個(gè)字節(jié)的內(nèi)容為字符的ASCII碼,在二進(jìn)制文件中,文件內(nèi)容是數(shù)據(jù)內(nèi)存的表示形式,是從內(nèi)存中直接復(fù)制過來的。
在C++文件流類中,
ifstream為輸入文件流類,用于實(shí)現(xiàn)文件的輸入。
ofstream為輸出文件流類,用于實(shí)現(xiàn)文件的輸出。
fstream為輸入輸出流類,用于實(shí)現(xiàn)輸入輸出。
4.1 打開操作
1) 用文件流的成員函數(shù)open()打開文件
ifstream、ofstream、fstream各有一個(gè)成員函數(shù)open()成員函數(shù)。
void open (const char * filename, openmode mode);
filename 是一個(gè)字符串,代表要打開的文件名。
mode 是以下標(biāo)志符的一個(gè)組合:
| ios::in | 為輸入(讀)而打開文件 |
| ios::out | 為輸出(寫)而打開文件 |
| ios::ate | 初始位置:文件尾 |
| ios::app | 所有輸出附加在文件末尾 |
| ios::trunc | 如果文件已存在則先刪除該文件 |
| ios::binary | 二進(jìn)制方式 |
ofstream, ifstream 和 fstream所有這些類的成員函數(shù)open 都包含了一個(gè)默認(rèn)打開文件的方式,這三個(gè)類的默認(rèn)方式各不相同:
| 類 | 參數(shù)的默認(rèn)方式 |
| ofstream | ios::out | ios::trunc |
| ifstream | ios::in |
| fstream | ios::in | ios::out |
?2)用文件流類的構(gòu)造函數(shù)打開文件
ifstream infile(“file.txt”) //! 利用構(gòu)造函數(shù)定義時(shí)直接打開一個(gè)輸入文件 ofstream outfile("file.txt") //! 利用構(gòu)造函數(shù)定義時(shí)直接打開一個(gè)輸出文件 fstream infile("file.txt",ios::in) //! 利用構(gòu)造函數(shù)定義時(shí)直接打開一個(gè)輸入文件 fstream outfile("file.txt",ios::out) //! 利用構(gòu)造函數(shù)時(shí)直接打開一個(gè)輸出文件3)判斷文件打開是否成功
#include <QCoreApplication> #include <fstream> #include <iomanip> #include <iostream> using namespace std;int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);char data[100];fstream infile;infile.open("file.txt",ios::in);qDebug("%d",infile);//! infile 返回1 表示成功,否則表示不成功qDebug("%d",infile.is_open()); //! 返回infile>>data;qDebug("%s",data);infile.close();return a.exec(); }is_open()函數(shù)的原型為: bool is_open();作用:判定文件是否打開成功 ;返回值: 打開成功返回true;否則,返回false;使用位置:一般在放在創(chuàng)建輸入輸出流后,如果文件打開錯(cuò)誤,直接退出程序。?4.2 關(guān)閉文件
格式:void close()
?4.3 文件的讀寫?
文件讀寫有兩種方式:
1) 直接采用流插入運(yùn)算“<<”和提取運(yùn)算符“>>”,這些運(yùn)算符將完成文件的字符轉(zhuǎn)換工作
2) 使用流成員函數(shù),輸出流成員函數(shù)有put和write,輸入流成員函數(shù)有g(shù)et,getline和read
#include <QCoreApplication> #include <fstream> #include <iomanip> #include <iostream> using namespace std;int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);//! 寫操作char str[] = {"12345678"};char data[100];ofstream outfile;outfile.open("file.txt");if (!outfile.is_open()){qDebug("can not open file");exit(0);}//! 方式1for (int i= 0;i < 3; i++){outfile<<str;outfile<<"\n";}outfile.close();//! 讀操作ifstream infile;infile.open("file.txt");if (!infile.is_open()){qDebug("can not open file");exit(0);}//! 方式1while(infile.good()){infile>>data;qDebug("%s",data);}infile.close();return a.exec(); } 結(jié)果輸出: 12345678 12345678 123456784.3.1 狀態(tài)標(biāo)志符的驗(yàn)證(Verification of state flags)
驗(yàn)證流的狀態(tài)的成員函數(shù)(所有都返回bool型返回值):
- bad()
如果在讀寫過程中出錯(cuò),返回 true 。例如:當(dāng)我們要對(duì)一個(gè)不是打開為寫狀態(tài)的文件進(jìn)行寫入時(shí),或者我們要寫入的設(shè)備沒有剩余空間的時(shí)候。
- fail()
除了與bad() 同樣的情況下會(huì)返回 true 以外,加上格式錯(cuò)誤時(shí)也返回true ,例如當(dāng)想要讀入一個(gè)整數(shù),而獲得了一個(gè)字母的時(shí)候。
- eof()
如果讀文件到達(dá)文件末尾,返回true。
- good()
這是最通用的:如果調(diào)用以上任何一個(gè)函數(shù)返回true 的話,此函數(shù)返回 false 。
要想重置以上成員函數(shù)所檢查的狀態(tài)標(biāo)志,你可以使用成員函數(shù)clear(),沒有參數(shù)。
4.3.2?istream::getline
讀取一行到字符數(shù)組。
istream& getline (char* s, streamsize n );
//默認(rèn)delim是換行字符'\n',遇到后丟棄,第二次讀取從delim后開始讀。
istream& getline (char* s, streamsize n, char delim );
<string> 字符串頭文件也定義了從流中讀取一行的函數(shù) getline()?
因?yàn)樗皇橇鞯某蓡T函數(shù),所以不能通過點(diǎn)訪問。
std::getline (string)
(1) 用戶定義截止字符
istream& getline (istream& ?is, string& str, char delim);
istream& getline (istream&& is, string& str, char delim); //c++11 標(biāo)準(zhǔn)
(2) 截止字符默認(rèn)'\n'
istream& getline (istream& ?is, string& str);
istream& getline (istream&& is, string& str); // c++11 標(biāo)準(zhǔn)
用法:?
從流對(duì)象is中讀取一行存到字符串str 直到遇到截止字符,如果遇到截止字符,則把它從流中取出來,然后丟棄(它不被存儲(chǔ),下一個(gè)操作的起點(diǎn)在它之后)函數(shù)調(diào)用前str 中的內(nèi)容將被覆蓋。?
?4.4 文件的隨機(jī)讀寫
獲得和設(shè)置流指針(get and put stream pointers)
所有輸入/輸出流對(duì)象(i/o streams objects)都有至少一個(gè)流指針:
- ifstream, 類似istream, 有一個(gè)被稱為get pointer的指針,指向下一個(gè)將被讀取的元素。
- ofstream, 類似 ostream, 有一個(gè)指針 put pointer ,指向?qū)懭胂乱粋€(gè)元素的位置。
- fstream, 類似 iostream, 同時(shí)繼承了get 和 put
我們可以通過使用以下成員函數(shù)來讀出或配置這些指向流中讀寫位置的流指針:
- tellg()?和?tellp()
這兩個(gè)成員函數(shù)不用傳入?yún)?shù),返回pos_type 類型的值(根據(jù)ANSI-C++ 標(biāo)準(zhǔn)) ,就是一個(gè)整數(shù),代表當(dāng)前get 流指針的位置 (用tellg) 或 put 流指針的位置(用tellp).
- seekg()?和seekp()
這對(duì)函數(shù)分別用來改變流指針get 和put的位置。兩個(gè)函數(shù)都被重載為兩種不同的原型:
seekg ( pos_type position ); seekp ( pos_type position );
使用這個(gè)原型,流指針被改變?yōu)橹赶驈奈募_始計(jì)算的一個(gè)絕對(duì)位置。要求傳入的參數(shù)類型與函數(shù) tellg 和tellp 的返回值類型相同。
seekg ( off_type offset, seekdir direction ); seekp ( off_type offset, seekdir direction );
使用這個(gè)原型可以指定由參數(shù)direction決定的一個(gè)具體的指針開始計(jì)算的一個(gè)位移(offset)。它可以是:
| ios::beg | 從流開始位置計(jì)算的位移 |
| ios::cur | 從流指針當(dāng)前位置開始計(jì)算的位移 |
| ios::end | 從流末尾處開始計(jì)算的位移 |
流指針 get 和 put 的值對(duì)文本文件(text file)和二進(jìn)制文件(binary file)的計(jì)算方法都是不同的,因?yàn)槲谋灸J降奈募心承┨厥庾址赡鼙恍薷摹S捎谶@個(gè)原因,建議對(duì)以文本文件模式打開的文件總是使用seekg 和 seekp的第一種原型,而且不要對(duì)tellg 或 tellp 的返回值進(jìn)行修改。對(duì)二進(jìn)制文件,你可以任意使用這些函數(shù),應(yīng)該不會(huì)有任何意外的行為產(chǎn)生。
以下例子使用這些函數(shù)來獲得一個(gè)二進(jìn)制文件的大小:
| ???? // obtaining file size | size of example.txt is 40 ? bytes. |
4.5 二進(jìn)制文件(Binary files)
在二進(jìn)制文件中,使用<< 和>>,以及函數(shù)(如getline)來操作符輸入和輸出數(shù)據(jù),沒有什么實(shí)際意義,雖然它們是符合語法的。
文 件流包括兩個(gè)為順序讀寫數(shù)據(jù)特殊設(shè)計(jì)的成員函數(shù):write 和 read。第一個(gè)函數(shù) (write) 是ostream 的一個(gè)成員函數(shù),都是被ofstream所繼承。而read 是istream 的一個(gè)成員函數(shù),被ifstream 所繼承。類 fstream 的對(duì)象同時(shí)擁有這兩個(gè)函數(shù)。它們的原型是:
write ( char * buffer, streamsize size );
read ( char * buffer, streamsize size );
這里 buffer 是一塊內(nèi)存的地址,用來存儲(chǔ)或讀出數(shù)據(jù)。參數(shù)size 是一個(gè)整數(shù)值,表示要從緩存(buffer)中讀出或?qū)懭氲淖址麛?shù)。
4.6 緩存和同步(Buffers and Synchronization)
當(dāng)我們對(duì)文件流進(jìn)行操作的時(shí)候,它們與一個(gè)streambuf 類型的緩存(buffer)聯(lián)系在一起。這個(gè)緩存(buffer)實(shí)際是一塊內(nèi)存空間,作為流(stream)和物理文件的媒介。例如,對(duì)于一個(gè)輸出流, 每次成員函數(shù)put (寫一個(gè)單個(gè)字符)被調(diào)用,這個(gè)字符不是直接被寫入該輸出流所對(duì)應(yīng)的物理文件中的,而是首先被插入到該流的緩存(buffer)中。
當(dāng)緩存被排放出來(flush)時(shí),它里面的所有數(shù)據(jù)或者被寫入物理媒質(zhì)中(如果是一個(gè)輸出流的話),或者簡(jiǎn)單的被抹掉(如果是一個(gè)輸入流的話)。這個(gè)過程稱為同步(synchronization),它會(huì)在以下任一情況下發(fā)生:
當(dāng)文件被關(guān)閉時(shí): 在文件被關(guān)閉之前,所有還沒有被完全寫出或讀取的緩存都將被同步。
當(dāng)緩存buffer 滿時(shí):緩存Buffers 有一定的空間限制。當(dāng)緩存滿時(shí),它會(huì)被自動(dòng)同步。
控制符明確指明:當(dāng)遇到流中某些特定的控制符時(shí),同步會(huì)發(fā)生。這些控制符包括:flush 和endl。
明確調(diào)用函數(shù)sync(): 調(diào)用成員函數(shù)sync() (無參數(shù))可以引發(fā)立即同步。這個(gè)函數(shù)返回一個(gè)int 值,等于-1 表示流沒有聯(lián)系的緩存或操作失敗。
在C++中,有一個(gè)stream這個(gè)類,所有的I/O都以這個(gè)“流”類為基礎(chǔ)的,包括我們要認(rèn)識(shí)的文件I/O,stream這個(gè)類有兩個(gè)重要的運(yùn)算符:
1、插入器(<<)
向流輸出數(shù)據(jù)。比如說系統(tǒng)有一個(gè)默認(rèn)的標(biāo)準(zhǔn)輸出流(cout),一般情況下就是指的顯示器,所以,cout<<"Write Stdout"<<'n';就表示把字符串"Write Stdout"和換行字符('n')輸出到標(biāo)準(zhǔn)輸出流。
2、析取器(>>)
從流中輸入數(shù)據(jù)。比如說系統(tǒng)有一個(gè)默認(rèn)的標(biāo)準(zhǔn)輸入流(cin),一般情況下就是指的鍵盤,所以,cin>>x;就表示從標(biāo)準(zhǔn)輸入流中讀取一個(gè)指定類型(即變量x的類型)的數(shù)據(jù)。
五、Qt之文件操作 QFile
5.1 QFile
QFile類是一個(gè)操作文件的輸入/輸出設(shè)備。
QFile是用來讀寫二進(jìn)制文件和文本文件的輸入/輸出設(shè)備。QFile可以自己?jiǎn)为?dú)被使用,但是如果和QDataStream或QTextStream一起使用將更加方便。
文件名通常可以通過構(gòu)造函數(shù)來傳遞,但也可以使用setName()來設(shè)置。目錄分隔符在任何操作系統(tǒng)下都使用“/",“/"不被支持。你可以通過exists()來檢查一個(gè)文件是否存在并且可以通過remove()來移去一個(gè)文件。更多操作系統(tǒng)相關(guān)的高級(jí)文件系統(tǒng)操作QT提供了QFileInfo和QDir類.
文件可以用open()來打開、用close()來關(guān)閉、用flush()來刷新。數(shù)據(jù)通常可以使用QDataStream或者QTextStream進(jìn)行讀寫,但你也可以使用read(),readLine(),readAll(),write()讀寫。QFile也支持getChar(),putChar(),和ungetChar()
size()可以返回文件的大小。你可以通過使用pos()函數(shù)得到當(dāng)前文件位置或者使用seek()移到一個(gè)新的文件位置。如果你到了文件的末尾,atEnd()返回真。
5.2. 文件打開方式
bool QFile::open(OpenMode mode);
/*
*OpenMode mode 打開方式,是一個(gè)枚舉類型
*QIODevice::NotOpen 不打開
*QIODevice::ReadOnly 只讀方式
*QIODevice::WriteOnly 讀寫方式
*QIODevice::ReadWrite 讀寫方式
*QIODevice::Append ? 追加方式
*QIODevice::Truncate 階段方式
*QIODevice::Text ? ? 轉(zhuǎn)換不同平臺(tái)的換行,讀的時(shí)候把所有換行轉(zhuǎn)成'\n',寫的時(shí)候再把'\n'轉(zhuǎn)換對(duì)應(yīng)平臺(tái)的換行
*QIODevice::Unbuffered 不使用緩沖區(qū)
*/
QIODevice類是輸入/輸出設(shè)備的基類。
QIODevice為設(shè)備提供了公共實(shí)現(xiàn)和抽象接口用于讀寫塊數(shù)據(jù)。
QIODevice是一個(gè)抽象類,不能被實(shí)例化。
5.3?QFile類關(guān)閉文件
void QFileDevice::close(); //刷新緩沖區(qū),并關(guān)閉文件?
5.4?讀文件
5.4.1 read()函數(shù)
qint64 QIODevice::read ( char * data, qint64 maxSize )//從設(shè)備讀取最多maxSize字節(jié)為數(shù)據(jù),并返回讀取的字節(jié)數(shù)。?如果發(fā)生錯(cuò)誤,例如嘗試從以WriteOnly模式打開的設(shè)備讀取時(shí),此函數(shù)返回-1。當(dāng)沒有更多數(shù)據(jù)可供讀取時(shí),返回0。在關(guān)閉的套接字上讀取或在進(jìn)程死亡后讀取也會(huì)返回-1
QByteArray QIODevice::read ( qint64 maxSize ) //從設(shè)備讀取最多maxSize字節(jié),并返回讀取為QByteArray的數(shù)據(jù)。 此功能無法報(bào)告錯(cuò)誤; 返回一個(gè)空的QByteArray()可能意味著當(dāng)前沒有數(shù)據(jù)可用于讀取,或者發(fā)生了錯(cuò)誤。 QFile file("file.txt");if (file.open(QFile::ReadOnly)){//QByteArray arr = file.read(1024);//qDebug() << arr;char buf[1024];qint64 lineLength = file.read(buf, sizeof(buf));if (lineLength != -1){QString str(buf);//將字符數(shù)組轉(zhuǎn)換為QStringqDebug() << str;}}打印結(jié)果:
"hello qfile!
hello qfile!
hello"
5.4.2 readAll()函數(shù)
QByteArray QIODevice::readAll ()從設(shè)備讀取所有可用數(shù)據(jù),并將其作為QByteArray返回。
此功能無法報(bào)告錯(cuò)誤;?返回一個(gè)空的QByteArray()可能意味著當(dāng)前沒有數(shù)據(jù)可用于讀取,或者發(fā)生了錯(cuò)誤。
打印結(jié)果:
"hello qfile!
hello qfile!
hello"
5.4.3 readLine()函數(shù)
qint64 QIODevice::readLine ( char * data, qint64 maxSize )此函數(shù)從設(shè)備讀取一行ASCII字符,最大為(maxSize - 1)個(gè)字節(jié),將字符存儲(chǔ)在數(shù)據(jù)中,并返回讀取的字節(jié)數(shù)。?如果無法讀取行但沒有出現(xiàn)錯(cuò)誤,則此函數(shù)返回0.如果發(fā)生錯(cuò)誤,則此函數(shù)返回可讀取的長(zhǎng)度,如果未讀取任何內(nèi)容,則返回-1。
終止'\ 0'字節(jié)始終附加到數(shù)據(jù),因此maxSize必須大于1。
讀取數(shù)據(jù),直到滿足以下任一條件:
讀取第一個(gè)'\ n'字符。
maxSize - 讀取1個(gè)字節(jié)。
檢測(cè)到設(shè)備數(shù)據(jù)的結(jié)束。
此函數(shù)調(diào)用readLineData(),readLineData()是使用對(duì)getChar()的重復(fù)調(diào)用實(shí)現(xiàn)的。?您可以通過在自己的子類中重新實(shí)現(xiàn)readLineData()來提供更高效的實(shí)現(xiàn)。
QFile file("file.txt");if (file.open(QFile::ReadOnly)){char buf[1024];qint64 lineLength = file.readLine(buf, sizeof(buf));if (lineLength != -1){QString str(buf);qDebug() << str;}}打印結(jié)果:
"hello qfile!
"
可以看出"\n"也被附加到了數(shù)據(jù)里。
5.4.4 getChar()函數(shù)
bool QIODevice::getChar ( char * c )從設(shè)備中讀取一個(gè)字符并將其存儲(chǔ)在c中。?如果c為0,則丟棄該字符。?成功時(shí)返回true;?否則返回false。
5.5 QFile類文件寫操作
QIODevice::write函數(shù)
qint64 QIODevice::write(const QByteArray &byteArray); //將byteArray寫入文件,寫完內(nèi)部位置指針后移 QFile file("d:/123.txt"); file.open(QIODevice::ReadWrite | QIODevice::Text); //打開模式可以使用‘|'組合 QByteArray byte("hellworld"); file.write(byte); file.write(byte); file.close();5.6. QDataStream
流控文件輸入輸出可以使用QDataStream。QDataStream 重載了運(yùn)算符了"<<"寫數(shù)據(jù),>>讀數(shù)據(jù)
#include <QDataStream> #include <QDebug> #include <QFile> int main(int argc, char**argv) {QFile file("file.txt");file.open(QIODevice::ReadWrite);QDataStream stream(&file);int a = 10;QString str = "helloworld";stream << a << str;file.close();return 0; } #include <QDataStream> #include <QDebug> #include <QFile> int main(int argc, char**argv) {QFile file("file.txt");file.open(QIODevice::ReadWrite); ?QDataStream stream(&file);int a;QString str;stream >> a >> str;qDebug() << "a:" << a << "str:" << str << endl;file.close();return 0; } QFile file("file.txt"); if(!file.open(QIODevice::ReadWrite | QIODevice::Text)) { qDebug()<<"Can't open the file!"<<endl; } QTextStream stream(&file); QString line_in; // while( !stream.atEnd()){ // line_in = stream.readLine(); // qDebug() << line_in; // } // stream.seek(stream.pos()); stream.seek(file.size());//將當(dāng)前讀取文件指針移動(dòng)到文件末尾 int count = 0; while(count < 10){ stream << QObject::trUtf8("新建行:") <<++count<<"/n"; } stream.seek(0);//將當(dāng)前讀取文件指針移動(dòng)到文件開始 while( !stream.atEnd()){ line_in = stream.readLine(); qDebug() << line_in; }一行一行直接讀取文件
QFile file("file.txt"); if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qDebug()<<"Can't open the file!"<<endl; } while(!file.atEnd()) { QByteArray line = file.readLine(); QString str(line); qDebug()<< str; }使用QTextStream讀取文件?
QFile file("file.txt"); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return; QTextStream in(&file); QString line = in.readLine(); while (!line.isNull()) { process_line(line); line = in.readLine(); }?
?
?
?
?
?
?
總結(jié)