C語(yǔ)言文件輸入輸出
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h> // 標(biāo)準(zhǔn)高級(jí)I/O standard high-level I/O
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
// 文件是存儲(chǔ)設(shè)備上的一段已命名的存儲(chǔ)區(qū)
void file_examine(void);
double average_data(char* filename);
void display1(int ch, char* filename);
void copy_file(char* dest_file, char* src_file);
FILE* open_file(char* filename,char* mode);
void close_file(FILE* fp, char* filename);
void to_upper(char* filename);
void concatenate(char* s1, char* s2);
void append(FILE* src, FILE* dest);
void addaword(char* filename);
void random_access(void);
void other(void);
int main(int argc, char* argv[]) //argv儲(chǔ)存的是指向參數(shù)字符串的指針
{
???? other();
???? return 0;
}
void file_examine(void) {
???? fputs("Enter filename:", stdout); //stdout標(biāo)準(zhǔn)輸出文件,C將stdin(標(biāo)準(zhǔn)輸入文件,是系統(tǒng)的普通輸入設(shè)備,一般為輸入設(shè)備鍵盤)和stdout(標(biāo)準(zhǔn)輸出文件,是系統(tǒng)的普通輸出設(shè)備,一般為輸出設(shè)備屏幕)視為文件,所以函數(shù)將字符串輸出到標(biāo)準(zhǔn)輸出文件
???? char filename[64] = "";
???? char* res = fscanf(stdin,"%63s",filename); //fscanf比scanf多了第一個(gè)參數(shù),文件指針,fscanf(stdin,...)和scanf等價(jià)
???? if (res==NULL)
???? {
???????? fputs("錯(cuò)誤:沒(méi)有收到內(nèi)容\n", stderr); //stderr標(biāo)準(zhǔn)錯(cuò)誤,程序每次運(yùn)行會(huì)自動(dòng)打開stdin/stdout/stderr三個(gè)標(biāo)準(zhǔn)文件,stderr通常輸出到屏幕,即便stdout重定向到文件,stderr也不受影響
???????? exit(EXIT_FAILURE);
???? }
???? FILE* fp = fopen(filename, "r"); // FILE*文件指針,指向的數(shù)據(jù)對(duì)象是一個(gè)C結(jié)構(gòu),包含文件信息。fopen(文件名字符串,模式字符串)返回文件指針,打開失敗返回NULL,文件名字符串為相對(duì)路徑
???? /*模式字符串有"w"(以文本模式、寫模式打開文件,如果該文件不存在則創(chuàng)建,如果存在則清除內(nèi)容)wb(和w區(qū)別是以二進(jìn)制模式打開文件)w+ (和w區(qū)別是以更新模式打開文件,更新模式即可讀寫文件)wb+(對(duì)應(yīng)wb)
?????"r"(文本模式、讀模式,如果文件不存在返回NULL)rb(二進(jìn)制模式)r+(可讀寫)rb+(對(duì)應(yīng)rb)
???? "a"(文本模式,寫模式,不存在則創(chuàng)建,存在則固定在文件末尾添加內(nèi)容)ab(二進(jìn)制)a+(更新模式,寫入依然固定末尾添加)ab+(對(duì)應(yīng)ab)
???? wx/wbx/w+x/wb+x/w+bx(C11標(biāo)準(zhǔn),類似非x模式,但是如果文件已存在或以獨(dú)占模式打開文件則打開文件失?。?/span>
???? */
???? //由于各系統(tǒng)的文件格式有差異,使用文本模式會(huì)將輸入和輸出解釋為C規(guī)定的統(tǒng)一格式,程序按照統(tǒng)一的格式處理內(nèi)容(如將/r/n解釋為/n一個(gè)字符),C實(shí)現(xiàn)將內(nèi)容轉(zhuǎn)換為系統(tǒng)所用的格式。而二進(jìn)制模式將文件原始內(nèi)容不做轉(zhuǎn)換直接輸入/輸出
???? if (fp==NULL)
???? {
???????? fprintf(stderr, "錯(cuò)誤:無(wú)法打開文件%s\n",filename); //fprintf(文件指針,格式化字符串,待打印項(xiàng)123...) 比printf多一個(gè)參數(shù),實(shí)際上printf就是使用的fprintf(stdout,...)
???????? exit(EXIT_FAILURE);
???? }
???? char ch;
???? unsigned long count;
???? for (count = 0; (ch=getc(fp))!=EOF; count++) //getc(文件指針),從指定的文件中讀取字符,getc(stdin)==getchar()
???? {
???? ????putc(ch, stdout); //putc(字符,文件指針),putc(...,stdout)==putchar(...)
???? }
???? putchar('\n'); //文件的最后一行末尾沒(méi)有換行符
???? int status = fclose(fp); //打開的文件用完必須要關(guān)閉,fclose(文件指針),關(guān)閉成功返回0,失敗返回非0
???? if (status!=0)
???? {
???? ????fputs("錯(cuò)誤:無(wú)法關(guān)閉文件", stderr);
???? }
???? printf("File %s has %lu characters\n", filename, count);
}
FILE* open_file(char* filename, char* mode) {
???? FILE* fp = fopen(filename, mode); // fopen()不僅打開一個(gè)文件,還創(chuàng)建了一個(gè)緩沖區(qū)(讀寫模式創(chuàng)建兩個(gè))以及一個(gè)包含文件信息和緩沖區(qū)數(shù)據(jù)的結(jié)構(gòu),fopen返回一個(gè)指向該結(jié)構(gòu)的指針,把該指針賦給變量的過(guò)程稱為fopen函數(shù)打開一個(gè)流stream,分為文本流和二進(jìn)制流。
???? // 該結(jié)構(gòu)通常包含一個(gè)指定流中當(dāng)前位置的文件位置指示器、錯(cuò)誤指示器、文件結(jié)尾指示器、一個(gè)指向緩沖區(qū)開始處的指針、一個(gè)文件標(biāo)識(shí)符、一個(gè)統(tǒng)計(jì)實(shí)際拷貝進(jìn)緩沖區(qū)字節(jié)數(shù)的計(jì)數(shù)器
???? if (fp == NULL)
???? {
???? ????fprintf(stderr, "文件%s打開失敗", filename);
???? }
???? return fp;
}
void close_file(FILE* fp, char* filename) {
???? int status = fclose(fp);
???? if (status != 0)
???? {
???? ????fprintf(stderr, "文件%s關(guān)閉失敗", filename);
???? }
???? return status;
}
void copy_file(char* dest_file, char* src_file) {
???? FILE* fp1, * fp2; //聲明兩個(gè)指針,第二個(gè)指針也要加*
???? if ((fp1 = open_file(src_file, "rb")) == NULL)
???? {
???? ????exit(EXIT_FAILURE);
???? }
???? if ((fp2 = open_file(dest_file, "wb")) == NULL)
???? {
???????? close_file(fp1, src_file);
???????? exit(EXIT_FAILURE);
???? }
???? char buffer[4096]; //C規(guī)定char類型為1字節(jié)
???? int len;
???? long sum = 0L;
???? while (len = fread(buffer, 1, 4096, fp1)) // fread(緩沖區(qū),元素大小,元素?cái)?shù)量,文件指針)從文件中讀取二進(jìn)制數(shù)據(jù)到指定的緩沖區(qū),如果將buffer視為一個(gè)完整的數(shù)據(jù)對(duì)象,則元素大小為4096,元素?cái)?shù)量為1,無(wú)論如何設(shè)定這兩個(gè)參數(shù),讀取的字節(jié)數(shù)都是兩個(gè)參數(shù)的乘積,函數(shù)返回讀取到的元素?cái)?shù)量,當(dāng)讀取到文件末尾時(shí)返回的數(shù)量會(huì)小于參數(shù)3
???? {
???????? fwrite(buffer, 1, len, fp2); // fwrite(緩沖區(qū),元素大小,元素?cái)?shù)量,文件指針)將緩沖區(qū)的數(shù)據(jù)寫入文件
???????? sum += len;
???? }
???? close_file(fp1, src_file);
???? close_file(fp2, dest_file);
???? printf("%ldbytes\n", sum);
}
double average_data(char* filename) {
???? FILE* fp = fopen(filename, "rb");
???? if (fp)
???? {
???????? double sum = 0;
???????? int count = 0;
???????? double buffer;
???????? putchar('(');
???????? while (fread(&buffer, sizeof(double), 1, fp))????//文件中原樣保存著double數(shù)組,fread將其中的元素逐一讀取
???????? {
???????????? printf("%.2f ", buffer);
???????????? sum += buffer;
???????????? count++;
???????? }
???????? putchar(')');
???????? fclose(fp);
???????? return sum / count;
???? }
???? return 0;
}
void display1(int ch, const char* filename) {
???? FILE* fp = fopen(filename, "r");
???? if (fp)
???? {
???????? char line[256];
???????? char* ptr;
???????? while (ptr = fgets(line, 256, fp))
???????? if (strchr(line, ch))
???????? fputs(line, stdout);
???????? fclose(fp);
???? }
}
void to_upper(char* filename) {????//將文件中所有字母轉(zhuǎn)大寫
???? FILE* fp = open_file(filename, "r+"); //r+模式打開文件,可以讀寫,如果位置不是文件末尾,寫操作會(huì)覆蓋當(dāng)前位置的字符
???? if (fp)
???? {
???????? fpos_t pos; // fpos_t類型的變量或數(shù)據(jù)對(duì)象可以在文件中指定一個(gè)位置
???????? fgetpos(fp, &pos); // 將文件當(dāng)前位置存儲(chǔ)到fpos_t變量的地址上(被調(diào)函數(shù)通過(guò)主調(diào)函數(shù)的變量的地址修改該變量的值),成功返回0,失敗返回非0
???????? char ch;
???????? while ((ch=getc(fp))!=EOF)
???????? {
???????????? fsetpos(fp, &pos); // 設(shè)定文件當(dāng)前位置,成功返回0,失敗返回非0
???????????? putc(toupper(ch), fp); //轉(zhuǎn)換字符為大寫存儲(chǔ)
???????????? fflush(fp); //刷新輸出流緩沖區(qū),標(biāo)準(zhǔn)IO使用緩沖,fopen()打開文件時(shí)會(huì)生成緩沖區(qū),一般當(dāng)緩沖區(qū)被填滿時(shí)才會(huì)將數(shù)據(jù)傳給操作系統(tǒng),而讀寫交替需要刷新緩沖才能正常執(zhí)行,fflush(文件指針)用于刷新輸出流,C標(biāo)準(zhǔn)未定義刷新輸入流的情況,所以fflush用于寫模式,如果是更新模式/+模式則上一次io操作必須為寫操作
???????????? fgetpos(fp, &pos); //更新位置
???????? }
???????? close_file(fp, filename);
???? }
}
void concatenate(char* s1, char *s2) {
???? if (strcmp(s1,s2)==0)
???? {
???????? puts("文件名不能沖突");
???????? exit(EXIT_FAILURE);
???? }
???? FILE* fa, * fs;
???? int files = 0;
???? int ch;
???? if ((fs = open_file(s1, "r")) == NULL)
???? {
???? ????exit(EXIT_FAILURE);
???? }
???? if ((fa = open_file(s2, "a+")) == NULL)
???? {
???????? close_file(fs, s1);
???????? exit(EXIT_FAILURE);
???? }
???? if (setvbuf(fa, NULL, _IOFBF, 4096) != 0) // setvbuf()函數(shù)創(chuàng)建一個(gè)供標(biāo)準(zhǔn)IO函數(shù)替換使用的緩沖區(qū),在打開文件后且未對(duì)流進(jìn)行其他操作之前調(diào)用該函數(shù)有效。參數(shù)1為文件指針,參數(shù)2為供替換使用的緩沖區(qū)地址,傳入NULL會(huì)使函數(shù)自動(dòng)分配一個(gè)緩沖區(qū),參數(shù)3為緩沖的模式,_IOFBF(完全緩沖IO full buffer,在緩沖區(qū)滿或調(diào)用fflush函數(shù)時(shí)刷新)_IOLBF(行緩沖line,在寫入\n或緩沖區(qū)滿時(shí)刷新)_IONBF(無(wú)緩沖),參數(shù)4為緩沖區(qū)的大小,操作成功返回0,失敗返回非0
???? {
???????? fputs("Can't create output buffer\n", stderr);
???????? exit(EXIT_FAILURE);
???? }
???? append(fs, fa); //將fs的內(nèi)容拼接到fa的后面
???? if (ferror(fs)!=0) // ferror()函數(shù)用于判斷io操作是否出現(xiàn)錯(cuò)誤,當(dāng)讀或?qū)懗霈F(xiàn)錯(cuò)誤返回非0,否則返回0。fs是讀操作,出現(xiàn)錯(cuò)誤(非0)說(shuō)明fs的內(nèi)容可能未完全讀取,而輸入出現(xiàn)錯(cuò)誤時(shí)也會(huì)返回EOF
???? {
???? ????fprintf(stderr, "Error in reading file %s.\n", s1);
???? }
???? if (feof(fa) == 0) // feof()函數(shù)判斷是否到達(dá)結(jié)尾,當(dāng)上一次輸入調(diào)用檢測(cè)到文件結(jié)尾時(shí),feof返回非0,否則返回0,所以如果返回0說(shuō)明沒(méi)到結(jié)尾、輸入出現(xiàn)錯(cuò)誤
???? {
???? ????fprintf(stderr, "Error in reading file %s.\n", s1);
???? }
???? rewind(fa); // rewind()函數(shù)將當(dāng)前位置調(diào)回文件開頭
???? printf("%s contents:\n", s2);
???? while ((ch=getc(fa))!=EOF)
???? {
???? ????putchar(ch);
???? }
???? close_file(fs, s1);
???? close_file(fa, s2);
}
void append(FILE* src, FILE* dest) {
???? size_t bytes;
???? char temp[4096];
???? while ((bytes=fread(temp,sizeof(char),4096,src))>0) // fread返回size_t類型
???? {
???? ????fwrite(temp, sizeof(char), bytes, dest); // fwrite返回成功寫入項(xiàng)的數(shù)量,正常情況數(shù)量和bytes相等
???? }
}
void addaword(char* filename)
{
???? FILE* fp;
???? char words[41];
???? if ((fp=fopen(filename,"a+"))==NULL) //打開失敗返回NULL
???? {
???????? fprintf(stdout, "Can't open \"%s\"file.\n", filename);
???????? exit(EXIT_FAILURE);
???? }
???? int num = 0;
???? while (fscanf(fp,"%40s",words)==1) //a+模式在寫入之前位置位于文件開頭
???? {
???????? if (isdigit(words[0]))
???????? ???? num = atoi(words); //讀取行號(hào)
???? }
???? puts("Enter words to add to the file; press the # key at the beginning of a line to terminate.");
???? while ((fscanf(stdin, "%40s", words) == 1) && (words[0] != '#'))
???? ????fprintf(fp, "%d.%s\n", ++num, words);
???? puts("File contents: ");
???? rewind(fp); //返回文件開頭位置
???? while (fscanf(fp, "%40s", words) == 1)
???? ????puts(words);
???? puts("Done!");
???? if (fclose(fp)!=0) //關(guān)閉文件成功返回0
???? {
???? ????fprintf(stderr, "Error closing file\n");
???? }
}
void random_access(void)
{
???? puts("Enter filename:");
???? char filename[64] = "";
???? FILE* fp;
???? if ((scanf("%63s", filename) == 1)&&(fp=fopen(filename,"rb")))
???? {
???????? long pos;
???????? char ch;
???????? while (puts("Enter a position:"),scanf("%ld",&pos)==1&&pos>=0)
???????? {
???????????? if (fseek(fp,pos,SEEK_SET)==-1) //fseek(文件指針,偏移量,起始點(diǎn)模式)函數(shù)將文件的位置移動(dòng)到任意字節(jié)處,從起始點(diǎn)出發(fā)移動(dòng)偏移量指定的字節(jié)數(shù),參數(shù)3設(shè)定起始點(diǎn)的模式,SEEK_SET為以文件開頭作為起始點(diǎn),SEEK_CUR為以當(dāng)前位置為起始點(diǎn),SEEK_END為以文件結(jié)尾(eof)為起始點(diǎn),偏移量為正數(shù)即往后/下移動(dòng),負(fù)數(shù)則往回/上移動(dòng)。
???????????? {
???????????????? puts("pos out of range"); // fseek()函數(shù)移動(dòng)成功返回0,移動(dòng)超出文件范圍返回-1(在visual studio中即使超出范圍也返回0)
???????????????? continue;
???????????? }
???????????? putchar('\"');
???????????? while ((ch=getc(fp))!=EOF&&ch!='\r'&&ch!='\n')
???????????? {
???????????? ????putchar(ch);
???????????? }
???????????? puts("\"\n");
???????? }
???????? fseek(fp, 0L, SEEK_END); //SEEK_END模式設(shè)定起始點(diǎn)為文件末尾eof,這個(gè)位置getc會(huì)返回eof,往前一個(gè)字節(jié)/-1偏移量是文件中的最后一個(gè)字符
???????? printf("range from 0 to %ld\n", ftell(fp)-1); //ftell(文件指針)返回當(dāng)前位置距離文件開始的字節(jié)數(shù)/偏移量
???????? fclose(fp);
???????? fp = fopen(filename, "r"); //在文本模式下測(cè)試fseek和ftell
???????? while (!feof(fp))
???????? {
???????? ????printf("%ld %c\n", ftell(fp), getc(fp)); //文本模式下將\r\n看作\n,但\n和\r前面一個(gè)字符的ftell()值依然相差2
???????? }
???? }
}
void other(void)
{
???? FILE* fp;
???? char ch;
???? if (fp=fopen("data02.txt","r"))
???? {
???????? putchar(ch=getc(fp)); // 讀取一個(gè)字符
???????? if (ch=='\n')
???????? {
???????? ????ungetc(ch, fp); // ungetc(字符,文件指針)將字符放回緩沖,不只可以放回上一個(gè)讀取的字符,也可以放任意字符,放回緩沖區(qū)并不會(huì)改變文件內(nèi)容,作用同scanf()讀取到不符合的字符放回輸入隊(duì)列
???????? }
????????
???????? fclose(fp);
???? }
}