C语言文件输入/输出,你真的都会了吗

282 阅读4分钟

待续

概览

  1. 文件内容、模式、格式;fopen的第二个参数表示的读写模式、fclose;文件指针;
  2. getc与putc;fprintf与fscanf;fgets与fputs;rewind、ungetc;fseek与ftell;fgetpos与fsetpos;fread与fwrite;feof与ferror;随机访问实例,自写s_gets

示例

  1. 文件内容、模式、格式;fopen的第二个参数表示的读写模式、fclose;文件指针;
  • 文件内容、模式、格式都有两种,二进制和文本,二进制文件存的就是内存中的二进制数据,文本则是以字符(一个字节)的形式储存文件。二进制模式是逐字节读取和写入的,使用 freadfwrite 函数;文本模式是以字符形式读取和写入的,有很多函数,诸如:scanfprintfgetcputcfscanffprintffgetsfputs等。UNIX、Linux只有一种文件格式,不区分二进制和文本,都以\n为换行符,文件结尾都是统计信息,而其他操作系统可能不是这样的,所以C用文本模式读取文件时会进行一个映射(比如mac中换行\r映射为\n),而用二进制模式读取时存的是什么就是什么,没有这样的映射。
  • 关于 fopen 函数第一个参数是目标文件指针,第二个参数表示读写模式,如下图所示: r表示读,w表示清空写,a表示追加写,它们三个都加上+表示的是更新模式,既能读又能写,注意区分写的方式。然后对于二进制文件,就在这三个字母后面加b即可(与+谁先谁后没影响),对于只有一种文件格式的UNIX系统加不加b都一样。 打开一个文件的过程,你知道吗?FILE其实是个结构体,包含了文件信息,创建的文件的缓冲区的信息和填充程度的信息等。
#include<stdio.h>

int main(int argc, char * argv[]){
    FILE * f = fopen("test.dat","wb+");
    double d[3] = {0.1,4.5,6.8};
    printf("成功写入%zd个\n",fwrite(d,sizeof(double),3,f));
    double e[3];
    // 这里要注意,此时文件指针的位置!
    rewind(f);
    printf("成功读取%zd个\n",fread(e,sizeof(double),3,f));
    for(int i=0; i<3; i++){
        printf("%f ",e[i]);
    }
    fclose(f);
}   

// 结果示例
// 成功写入3个
// 成功读取3个
// 0.100000 4.500000 6.800000
  1. getc与putc;fprintf与fscanf;fgets与fputs;rewind、ungetc;fseek与ftell;fgetpos与fsetpos;fread与fwrite;feof与ferror;随机访问实例,自写s_gets
int	 getc(FILE *);  // 成功,则返回该字符的ASCII值,失败返回-1(EOF)
int	 putc(int, FILE *);  // 成功,则返回该字符的ASCII值,失败返回-1(EOF)
int	 fprintf(FILE * __restrict, const char * __restrict, ...);
int	 fscanf(FILE * __restrict, const char * __restrict, ...);
char	*fgets(char * __restrict, int, FILE *);  // 成功返回字符串首地址,失败返回0(NULL)
int	 fputs(const char * __restrict, FILE * __restrict);  // 失败返回-1(EOF)
void	 rewind(FILE *);   // 回到文件首
int	 ungetc(int, FILE *);  // 将一个字符放回文件流
int	 fseek(FILE *, long, int);  // 指定文件指针的位置,后跟字节数(相对偏移量,对照SEEK_CUR、SEEK_END、SEEK_SET)
long	 ftell(FILE *);  // 获取当前文件指针所在位置
int	 fgetpos(FILE * __restrict, fpos_t *);  // 弥补 ftell 字节数为20亿(long)的缺陷,可以更多
int	 fsetpos(FILE *, const fpos_t *);
size_t	 fread(void * __restrict __ptr, size_t __size, size_t __nitems, FILE * __restrict __stream);  // 例如数组的地址、元素的大小、元素的个数;成功返回读取到的个数,失败返回-1(EOF)
size_t	 fwrite(const void * __restrict __ptr, size_t __size, size_t __nitems, FILE * __restrict __stream);
int	 feof(FILE *);  // 它们俩的检测是在返回EOF之后再判断是文件末尾还是其他错误
int	 ferror(FILE *);
#include<stdio.h>

int main(int argc, char * argv[]){
    FILE * f = fopen("test.dat","wb+");
    double d[3] = {0.1,4.5,6.8};
    printf("成功写入%zd个\n",fwrite(d,sizeof(double),3,f));
    double e[3];
    // 这里要注意,此时文件指针的位置!
    rewind(f);
    printf("成功读取%zd个\n",fread(e,sizeof(double),3,f));
    for(int i=0; i<3; i++){
        printf("%f ",e[i]);
    }
    printf("%d ",getc(f));  // 此时返回了EOF
    if(feof(f)){  // 判断是不是文件末尾
        printf("eof");
    }
    if(ferror(f)){  // 判断是不是错误
        printf("error");
    }
    fclose(f);
}   

// 结果示例
// 成功写入3个
// 成功读取3个
// 0.100000 4.500000 6.800000 -1 eof                                               
#include<stdio.h>
#include<string.h>
#define MAXSIZE 41
char * s_gets(char * str, int n);
int main(int argc, char * argv[]){
    char c[MAXSIZE];
    s_gets(c,MAXSIZE);
    fputs(c,stdout);
    char d = getchar();
    putchar(d);
}   
char * s_gets(char * str, int n){
    char * res;
    char * pos;
    res = fgets(str,n,stdin);
    pos = strchr(str,'\n');  // 关键部分!
    if(res){
        if(pos){
            *pos = '\0';
        }
        else{
            while(getchar() != '\n'){
                continue;
            }
        }
    }
    return res;
}

// 结果示例
// ./test
// ahgahgkjahdgjkahjgkahlkajhlkafjgklr    agjakldg
// ahgahgkjahdgjkahjgkahlkajhlkafjgklr    ah
// h                               

随机访问实例,用到了自定义缓冲区的函数,参数中涉及0、1、2(完全缓冲、行缓冲、无缓冲,_IOFBF、_IOLBF、_IONBF)。 int setvbuf(FILE * __restrict, char * __restrict, int, size_t);

C 库函数 int fflush(FILE *stream)刷新流 stream 的输出缓冲区,只要最近一次流的操作不是输入就行。