C语言文件输入输出

发布时间:2023-01-30 21:57:10
来源:哔哩哔哩

#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);

}

}

标签: 打开文件 当前位置 数据对象

AD
更多相关文章