欢迎光临散文网 会员登陆 & 注册

C语言文件输入输出

2023-01-30 21:56 作者:虚云幻仙  | 我要投稿

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h> // 标准高级I/O standard high-level I/O

#include <string.h>

#include <stdlib.h>

#include <ctype.h>


// 文件是存储设备上的一段已命名的存储区

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储存的是指向参数字符串的指针

{

     other();

     return 0;

}

void file_examine(void) {

     fputs("Enter filename:", stdout); //stdout标准输出文件,C将stdin(标准输入文件,是系统的普通输入设备,一般为输入设备键盘)和stdout(标准输出文件,是系统的普通输出设备,一般为输出设备屏幕)视为文件,所以函数将字符串输出到标准输出文件

     char filename[64] = "";

     char* res = fscanf(stdin,"%63s",filename); //fscanf比scanf多了第一个参数,文件指针,fscanf(stdin,...)和scanf等价

     if (res==NULL)

     {

         fputs("错误:没有收到内容\n", stderr); //stderr标准错误,程序每次运行会自动打开stdin/stdout/stderr三个标准文件,stderr通常输出到屏幕,即便stdout重定向到文件,stderr也不受影响

         exit(EXIT_FAILURE);

     }

     FILE* fp = fopen(filename, "r"); // FILE*文件指针,指向的数据对象是一个C结构,包含文件信息。fopen(文件名字符串,模式字符串)返回文件指针,打开失败返回NULL,文件名字符串为相对路径

     /*模式字符串有"w"(以文本模式、写模式打开文件,如果该文件不存在则创建,如果存在则清除内容)wb(和w区别是以二进制模式打开文件)w+ (和w区别是以更新模式打开文件,更新模式即可读写文件)wb+(对应wb)

     "r"(文本模式、读模式,如果文件不存在返回NULL)rb(二进制模式)r+(可读写)rb+(对应rb)

     "a"(文本模式,写模式,不存在则创建,存在则固定在文件末尾添加内容)ab(二进制)a+(更新模式,写入依然固定末尾添加)ab+(对应ab)

     wx/wbx/w+x/wb+x/w+bx(C11标准,类似非x模式,但是如果文件已存在或以独占模式打开文件则打开文件失败)

     */

     //由于各系统的文件格式有差异,使用文本模式会将输入和输出解释为C规定的统一格式,程序按照统一的格式处理内容(如将/r/n解释为/n一个字符),C实现将内容转换为系统所用的格式。而二进制模式将文件原始内容不做转换直接输入/输出

     if (fp==NULL)

     {

         fprintf(stderr, "错误:无法打开文件%s\n",filename); //fprintf(文件指针,格式化字符串,待打印项123...) 比printf多一个参数,实际上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'); //文件的最后一行末尾没有换行符

     int status = fclose(fp); //打开的文件用完必须要关闭,fclose(文件指针),关闭成功返回0,失败返回非0

     if (status!=0)

     {

         fputs("错误:无法关闭文件", stderr);

     }

     printf("File %s has %lu characters\n", filename, count);

}

FILE* open_file(char* filename, char* mode) {

     FILE* fp = fopen(filename, mode); // fopen()不仅打开一个文件,还创建了一个缓冲区(读写模式创建两个)以及一个包含文件信息和缓冲区数据的结构,fopen返回一个指向该结构的指针,把该指针赋给变量的过程称为fopen函数打开一个流stream,分为文本流和二进制流。

     // 该结构通常包含一个指定流中当前位置的文件位置指示器、错误指示器、文件结尾指示器、一个指向缓冲区开始处的指针、一个文件标识符、一个统计实际拷贝进缓冲区字节数的计数器

     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关闭失败", filename);

     }

     return status;

}

void copy_file(char* dest_file, char* src_file) {

     FILE* fp1, * fp2; //声明两个指针,第二个指针也要加*

     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规定char类型为1字节

     int len;

     long sum = 0L;

     while (len = fread(buffer, 1, 4096, fp1)) // fread(缓冲区,元素大小,元素数量,文件指针)从文件中读取二进制数据到指定的缓冲区,如果将buffer视为一个完整的数据对象,则元素大小为4096,元素数量为1,无论如何设定这两个参数,读取的字节数都是两个参数的乘积,函数返回读取到的元素数量,当读取到文件末尾时返回的数量会小于参数3

     {

         fwrite(buffer, 1, len, fp2); // fwrite(缓冲区,元素大小,元素数量,文件指针)将缓冲区的数据写入文件

         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数组,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) {    //将文件中所有字母转大写

     FILE* fp = open_file(filename, "r+"); //r+模式打开文件,可以读写,如果位置不是文件末尾,写操作会覆盖当前位置的字符

     if (fp)

     {

         fpos_t pos; // fpos_t类型的变量或数据对象可以在文件中指定一个位置

         fgetpos(fp, &pos); // 将文件当前位置存储到fpos_t变量的地址上(被调函数通过主调函数的变量的地址修改该变量的值),成功返回0,失败返回非0

         char ch;

         while ((ch=getc(fp))!=EOF)

         {

             fsetpos(fp, &pos); // 设定文件当前位置,成功返回0,失败返回非0

             putc(toupper(ch), fp); //转换字符为大写存储

             fflush(fp); //刷新输出流缓冲区,标准IO使用缓冲,fopen()打开文件时会生成缓冲区,一般当缓冲区被填满时才会将数据传给操作系统,而读写交替需要刷新缓冲才能正常执行,fflush(文件指针)用于刷新输出流,C标准未定义刷新输入流的情况,所以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()函数创建一个供标准IO函数替换使用的缓冲区,在打开文件后且未对流进行其他操作之前调用该函数有效。参数1为文件指针,参数2为供替换使用的缓冲区地址,传入NULL会使函数自动分配一个缓冲区,参数3为缓冲的模式,_IOFBF(完全缓冲IO full buffer,在缓冲区满或调用fflush函数时刷新)_IOLBF(行缓冲line,在写入\n或缓冲区满时刷新)_IONBF(无缓冲),参数4为缓冲区的大小,操作成功返回0,失败返回非0

     {

         fputs("Can't create output buffer\n", stderr);

         exit(EXIT_FAILURE);

     }

     append(fs, fa); //将fs的内容拼接到fa的后面

     if (ferror(fs)!=0) // ferror()函数用于判断io操作是否出现错误,当读或写出现错误返回非0,否则返回0。fs是读操作,出现错误(非0)说明fs的内容可能未完全读取,而输入出现错误时也会返回EOF

     {

         fprintf(stderr, "Error in reading file %s.\n", s1);

     }

     if (feof(fa) == 0) // feof()函数判断是否到达结尾,当上一次输入调用检测到文件结尾时,feof返回非0,否则返回0,所以如果返回0说明没到结尾、输入出现错误

     {

         fprintf(stderr, "Error in reading file %s.\n", s1);

     }

     rewind(fa); // rewind()函数将当前位置调回文件开头

     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返回成功写入项的数量,正常情况数量和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); //读取行号

     }

     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) //关闭文件成功返回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(文件指针,偏移量,起始点模式)函数将文件的位置移动到任意字节处,从起始点出发移动偏移量指定的字节数,参数3设定起始点的模式,SEEK_SET为以文件开头作为起始点,SEEK_CUR为以当前位置为起始点,SEEK_END为以文件结尾(eof)为起始点,偏移量为正数即往后/下移动,负数则往回/上移动。

             {

                 puts("pos out of range"); // fseek()函数移动成功返回0,移动超出文件范围返回-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模式设定起始点为文件末尾eof,这个位置getc会返回eof,往前一个字节/-1偏移量是文件中的最后一个字符

         printf("range from 0 to %ld\n", ftell(fp)-1); //ftell(文件指针)返回当前位置距离文件开始的字节数/偏移量

         fclose(fp);

         fp = fopen(filename, "r"); //在文本模式下测试fseek和ftell

         while (!feof(fp))

         {

             printf("%ld %c\n", ftell(fp), getc(fp)); //文本模式下将\r\n看作\n,但\n和\r前面一个字符的ftell()值依然相差2

         }

     }

}

void other(void)

{

     FILE* fp;

     char ch;

     if (fp=fopen("data02.txt","r"))

     {

         putchar(ch=getc(fp)); // 读取一个字符

         if (ch=='\n')

         {

             ungetc(ch, fp); // ungetc(字符,文件指针)将字符放回缓冲,不只可以放回上一个读取的字符,也可以放任意字符,放回缓冲区并不会改变文件内容,作用同scanf()读取到不符合的字符放回输入队列

         }

        

         fclose(fp);

     }

}


C语言文件输入输出的评论 (共 条)

分享到微博请遵守国家法律