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