标准IO之文件读写

  • 流支持不同的读写方式
    • 读写一个字符:fgetc()/fputc()
    • 读写一行:fgets()和fputs()
    • 读写若干个对象:fread()/fwrite(), 每次读/写若干个对象,而每个对象具有相同的长度

字符读写

字符输入

  • 用来输入一个字符
  • 头文件:
    • #include <stdio.h>

函数原型

  • int fgetc(FILE *stream);
    • 从流中读取下一个字符,并将其作为unsigned char强制转换为int类型返回,若到文件末尾或出错时返回EOF(-1)
  • int getc(FILE *stream); //宏
    • 等价于fgetc(),不同之处是它可以作为一个宏来实现,对stream进行多次求值
    • getc和fgetc区别是一个是宏一个是函数
  • int getchar(void);
    • 成功时返回读取的字符;若到文件末尾或出错时返回EOF(-1)
    • getchar 等同于fgetc(stdin)
    • stdin 也是FILE *的指针,是系统定义好的,指向的是标准输入(键盘输入)
    • 调用getchar会阻塞,等待你的键盘输入

函数返回值是int类型不是char类型,主要是为了扩展返回值的范围

打开文件后读取,是从文件开头开始读,a+a+读完一个后读写指针会后移

c 复制代码
#include <stdio.h>
int main(int argc, const char *argv[])
{
    int c;
    FILE * fp = NULL;
    if((fp = fopen("test.txt","r+")) == NULL ){
        perror("fopen\n");
        return -1;
    }
    printf("fopen success\n");
    while(1){
        #if 0
        if((c = fgetc(fp)) == EOF ){
            perror("fgetc");
            break;
        }
        #else
        if((c = getc(fp)) == EOF ){
            perror("getc");
            break;
        }
        #endif
        printf("%c", (char)c);
    }
    fclose(fp);
    return 0;
}

字符输出

  • 头文件:
    • #include <stdio.h>

函数原型

  • int fputc(int c, FILE *stream);
    • 将字符c(强制转换为无符号字符)写入流
  • int putc(int c, FILE *stream);//宏
    • 等价于fputc(),不同之处是它可以作为宏来实现,对stream进行多次求值
  • int putchar(int c);
    • 成功时返回写入的字符,出错时返回EOF
    • putchar©等同于fputc(c, stdout)
  • fputc()、putc()和putchar()返回作为无符号字符写入的字符,在错误时返回EOF

以上函数返回和输入参数都是int类型

遇到这种错误:Bad file descriptor, 很可能是文件打开的模式错误(只读模式去写,只写模式去读),或文件的读写权限不能满足文件打开设置的需求

c 复制代码
#include <stdio.h>
int main(int argc, const char *argv[])
{
    int c;
    FILE * fp = NULL;
    if ((fp = fopen("test.txt","a+")) == NULL ){
        perror("fopen\n");
        return -1;
    }
    printf("fopen success\n");
    #if 1
    if((c = fputc('a', fp)) == EOF ){
        perror("fputc");
        return -1;
    }
    #elif 0
    if((c = putc('a', fp)) == EOF ){
        perror("putc");
        return -1;
    }
    #else
    putchar('b');
    #endif
    printf("c = %c\n", c);
    fclose(fp);
    return 0;
}

例程:字符读写复制文件

c 复制代码
#include <stdio.h>
int main(int argc, const char *argv[])
{
    FILE *fp_a = NULL, *fp_b = NULL;
    int c = 0;
    if(argc < 3){
        printf("Usage: %s <be copied file> <copied file>\n", argv[0]);
        return -1;
    }
    if( (fp_a = fopen(argv[1], "r+")) == NULL ){
        perror("be copied file fopen");
        return -1;
    }
    if( (fp_b = fopen(argv[2] , "w+")) == NULL ){
        perror("copied file fopen");
        fclose(fp_a);
        return -1;
    }
    printf("fopen files ok\n");
    do{
        if((c = fgetc(fp_a)) == EOF){
            perror("fgetc");
            break;
        }
        if( fputc(c, fp_b) == EOF ){
            perror("fputc");
            break;
        }
    } while(1);
    fclose(fp_a);
    fclose(fp_b);
    return 0;
}

行输入输出

行输入

  • 头文件:
    • #inculde <stdio.h>

函数原型

  • char *gets(char *s);
    • 读取标准输入到缓冲区s

注意这个函数是危险的,不检查缓冲区溢出,永远不要使用gets()

gets()从stdin读取一行到s所指向的缓冲区,直到一个结束换行符或EOF,它用空字节('\0')替换。因为在事先不知道数据的情况下不可能知道gets()将读取多少字符,并且因为gets()将继续存储超过缓冲区末尾的字符,所以使用它是非常危险的。它可能被用来破坏计算机安全,使用fgets()代替。

  • char *fgets(char *s, int size, FILE *stream);
    • 输入的数据超出size,size-1个字符会保存到缓冲区,最后添加'\0',
    • 输入数据少于size-1,后面会添加'\n'和'\0'
  • 返回值:
    • 成功时返回s,到文件末尾或出错时返回NULL
  • 读取文件内容
c 复制代码
#include <stdio.h>
#define N 32
#define M 6
int main(int argc, const char *argv[])
{
    FILE * fp = NULL;
    char buf[M] = {0};
    if( (fp = fopen("test.txt", "r+")) == NULL ){
        perror("fopen");
        return -1;
    }
    while(1){
        if ( fgets(buf, M, fp) == NULL ){
            perror("fgets");
            break;
        }
        printf("test look:%s", buf);
    }
    printf("fgets from file ok\n");
    fclose(fp);
    return 0;
}

行输出

  • 头文件:
    • #include <stdio.h>
  • 函数原型:
    • int puts(const char *s);
    • int fputs(const char *s, FILE *stream);
  • 返回值:
    • 成功时返回非负整数(输出字符个数)
    • 出错时返回EOF(-1)
  • 参数:
    • puts将缓冲区s中的字符串输出到stdout,并追加'\n'
    • fputs将缓冲区s中的字符串输出到stream,不追加'\n'
c 复制代码
#include <stdio.h>
#define N 32
int main(int argc, const char *argv[])
{
    FILE * fp = NULL;
    char buf[N] = {0};
    if( (fp = fopen("test.txt", "a+")) == NULL ){
        perror("fopen");
        return -1;
    }
    if(fgets(buf, N, stdin) == NULL){
        perror("fgets");
        fclose(fp);
        return -1;
    }
    if(fputs(buf, fp) == EOF){
        perror("fputs");
        fclose(fp);
        return -1;
    }
    printf("fputs ok\n");
    fclose(fp);
    return 0;
}

例程:统计文件行数

  • 如何统计一个文本文件包含多少行?
c 复制代码
#include <stdio.h>
#include <string.h>
#define N 32
int main(int argc, const char *argv[])
{
    FILE * fp = NULL;
    int cout = 0;
    char buf[N] = {0};
    if( (fp = fopen("test.txt", "a+")) == NULL ){
        perror("fopen");
        return -1;
    }
    while(1){
        if(fgets(buf, N, fp) == NULL){
            perror("fgets");
            break;
        }
        if(buf[strlen(buf)-1] == '\n'){
            cout++;
        }
    }
    printf("cout=%d\n", cout);
    fclose(fp);
    return 0;
}

二进制、对象读写

  • 文本文件:每一个字符都是一个字节,外存存储对应ASCII码值。对于数值,以字符形式在外存上存储对应ASCII值。文本流中的数据可以显示和打印出来,都是用户可以读懂的信息
  • 二进制文件:流中的数据是按照二进制编码的方式(值的方式)来存放文件。二进制数据也可在屏幕上显示, 但其内容无法读懂
  • fread、fwrite既可以读写文本文件,也可以读写数据文件;

fread 读

  • 头文件:
    • #include <stdio.h>
  • 函数原型:
    • size_t fread(void *ptr, size_t size, size_t n, FILE *fp);
  • 参数:
    • void *ptr 读内容要放的位置的指针
    • size_t size 读取的"块"大小
    • size_t n 每次函数运行读取的"块"个数
    • FILE *fp 要读取文件的文件指针
  • 返回值:
    • 成功返回读写的对象个数
    • 出错时返回EOF
c 复制代码
#include <stdio.h>
#define N 10
int main(int argc, const char *argv[])
{
    FILE * fp = NULL;
    char buf[N] = {0};
    size_t ret = -1;
    if ((fp = fopen(argv[1], "r+")) < 0 ){
        perror("fread");
        return -1;
    }
    ret = fread(buf, sizeof(buf), 1, fp);
    if(0 > ret){
        perror("fread");
        fclose(fp);
        return -1;
    }
    printf("test buf=%s", buf);
    fclose(fp);
    return 0;
}

fwrite 写

  • 头文件:
    • #include <stdio.h>
  • 函数原型:
    • size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp);
  • 参数:
    • void *ptr 要写的内容的位置指针
    • size_t size 写的一个"块"大小
    • size_t n 每次函数运行写的"块"个数
    • FILE *fp 要写入文件的文件指针
  • 返回值:
    • 成功返回读写的对象个数
    • 出错时返回EOF
c 复制代码
#include <stdio.h>
#include <unistd.h>
typedef struct note{
    int id;
    char name[8];
    float score;
} STU;
int main(int argc, const char *argv[])
{
    FILE * fp = NULL;
    if((fp=fopen("test.txt", "a+")) < 0){
        perror("perror");
        return -1;
    }
    STU s[2] = {{1, "wang", 98},{2, "li", 95}};
    int len = sizeof(STU);
    if(fwrite(&s[0], len, 1, fp) < 0){
        perror("fwrite");
        fclose(fp);
        return -1;
    }
    fflush(fp);
    sleep(5);
    if(fwrite(&s[1], len, 1, fp) < 0){
        perror("fwrite");
        fclose(fp);
        return -1;
    }
    sleep(5);
    fclose(fp);
    if((fp=fopen("test.txt", "r+")) < 0){
        perror("perror");
        return -1;
    }
    STU ret;
    fread(&ret, len, 1, fp);
    printf("%d,%s,%f\n", ret.id, ret.name, ret.score);
    fclose(fp);
    return 0;
}

文件写完后,文件指针指向文件末尾,如果这时候去读,读不出来内容的

例程:读写复制文件

  • 如何利用 fread / fwrite 实现文件的复制?
c 复制代码
#include <stdio.h>
#include <string.h>
#define N 512
int main(int argc, const char *argv[])
{
    FILE *fp_r = NULL, *fp_w = NULL;
    if(argc<3){
        printf("Usage: %s <source file> <destination file>\n", argv[0]);
        return -1;
    }
    if((fp_r = fopen(argv[1], "r")) == NULL){
        printf("fopen source file failed\n");
        return -1;
    }
    if((fp_w = fopen(argv[2], "a+")) == NULL){
        printf("fopen destination file failed\n");
        return -1;
    }
    char buf[N] = {0};
    size_t bytes_read, bytes_write;
    while(1){
        // 判断文件流指针是不是已经移动到文件末尾
        if(feof(fp_r)){
            printf("read file in last line\n");
            break;
        }
        if((bytes_read = fread(buf, 1, sizeof(buf), fp_r)) < 0){
            printf("fread failed\n");
            goto END;
        }
        if((bytes_write = fwrite(buf, 1, bytes_read, fp_w)) < 0){
            printf("fwrite failed\n");
            goto END;
        }
        // 如果写入的数据量不等于读取的数据量,则表示写入失败
        if(bytes_read != bytes_write){
            printf("failed to write to destination file\n");
            goto END;
        }
        bzero(buf, sizeof(buf)); 
    }
    
    printf("file copied successfully form '%s' to '%s'\n", argv[1], argv[2]);
    END:
    fclose(fp_r);
    fclose(fp_w);
    return 0;
}
  • 字符数组使用后,空间不会自己清空,不影响下次使用需要主动清。bzero函数可以给指定的空间上都填充ASCII值0,表示清空
c 复制代码
#include <strings.h>
void bzero(void *s, size_t n);
  • feof函数可以判断文件是否读到末尾(文件读写指针),检查stream 指向的流的文件结束指示符,如果已达到,则返回非零值
c 复制代码
#include <stdio.h>
int feof(FILE *stream);

格式化输入输出

格式化输入

  • int fscanf(FILE *stream, const char *format, ...);
    • 从文件流读取格式化输入
  • int sscanf(const char *str, const char *format, ...);
    • 从字符串读取格式化输入
  • int scanf(const char *format, ...);
    • 标准输入版本(实际是fscanf的特殊形式)
    • 等价于 fscanf(stdin, format, ...)
  • 成功时返回输出的字符个数,出错时返回EOF

格式化输出

  • int fprintf(FILE *stream, const char *fmt, ...);
    • 格式化输出到文件流
  • int sprintf(char *s, const char *fmt, ...);
    • 格式化输出到字符串
  • int printf(const char *format, ...);
    • 标准输出版本
    • 等价于 fprintf(stdout, format, ...)
  • 成功时返回输出的字符个数,出错时返回EOF
c 复制代码
#include <stdio.h>
int main(int argc, const char *argv[])
{
    char buf[32] = {0};
#if 0
    FILE *fp = NULL;
    if((fp = fopen(argv[1], "a+")) == NULL){
        perror("fopen");
        return -1;
    }
    fprintf(fp, "%s", "hello");
    fscanf(stdin, "%s", buf);
    fprintf(fp, "%s", buf);
    fclose(fp);
#endif
    int a, b, c;
    sscanf("1 2", "%d %d %d", &a, &b, &c);
    sprintf(buf, "a=%d,b=%d,c=%d\n", a, b, c);
    printf("%s\n", buf);
    return 0;
}
相关推荐
SELSL6 天前
标准IO总结
linux·c语言·标准io·stdio·标准io与文件io的区别
2401_841495649 天前
【自然语言处理】处理 GBK 编码汉字的算法设计
人工智能·python·自然语言处理·校验·文件读写·gbk编码与解码·批量过滤
CQ_YM16 天前
Linux标准IO
linux·系统编程·标准io
emma羊羊3 个月前
【文件读写】绕过验证下
网络安全·php·upload·文件读写
m0_7156467610 个月前
无名管道、有名管道、信号、信号处理
java·linux·网络·数据结构·算法·嵌入式·标准io
KevinRay_1 年前
Numpy指南:解锁Python多维数组与矩阵运算(下)
python·矩阵·numpy·排序·文件读写
kim56591 年前
android studio 读写文件操作(方法二)
android·ide·android studio·文件读写
AL流云。1 年前
【C/C++进阶】——文件操作之文本文件与二进制文件指针读写
c语言·数据库·文件操作·文件读写
martian6651 年前
学懂C++ (十三):高级教程——C++ 文件和流详解
开发语言·c++·文件操作·文件读写·fstream