【文件I/O】(一)标准I/O

2023-11-20

标准I/O:库函数

一、I/O相关知识

1.1最早接触的I/O

#include<stdio.h> 标准的输入输出头文件
它是printf、scanf函数的头文件,它们都是标准IO

1.2I/O的种类

​标准IO:库函数
文件IO:系统调用

1.3库函数和系统调用

系统调用:系统调用就是从用户空间进入内核空间的一次过程,系统调用没有缓冲区,系统调用的效率低,系统调用可移植性比较差。

库函数:库函数=缓冲区+系统调用,库函数的效率比系统调用的高,库函数可移植比较强。

1.4什么是FILE?

FILE:又被称为流,是一个结构体类型,标准I/O用FILE来存放打开的文件的相关信息(缓冲区,系统调用相关的内容),标准I/O的所有操作都是围绕FILEl来进行的。在一个正在执行的程序中默认已经有三个FILE的指针stdin,stdout,stderr。

标准I/O预定义3个流,程序运行时自动打开

标准输入流 0 STDIN_FILENO stdin
标准输出流 1 STDOUT_FILENO stdout
标准错误流 2 STDERR_FILENO stderr

FILE结构体

typedef struct _IO_FILE FILE;

struct _IO_FILE {
	char* _IO_buf_base;	//缓冲区的起始地址
  	char* _IO_buf_end;	//缓冲区的结束地址
    int _fileno;        //系统调用的文件描述符
    ...
};

二进制流/文本流换行符

Liniux
换行符 <---> '\n'

windows
二进制流:换行符 <---> '\n'
文本流:换行符 <---> '\r''\n'

二、标准I/O函数

1.fopen、fclose、strerror、perror(打开、关闭文件,输出错误码信息)

fopen、fclose函数API

#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
功能:打开文件(标准IO)
参数:
    @pathname:想要打开文件的路径及名字 "./hello.txt" "/home/linux/1.c"
 	@mode:打开文件的方式 "a" "a+" "w" "w+" "r" "r+"
   r      以只读的方式打开文件,将光标定位到文件的开头
   r+     以读写的方式打开文件,将光标定位到文件的开头
   w      以只写方式打开文件,如果文件存在就清空,如果文件不存在就创建,将光标定位到开头
   w+     以读写方式打开文件,如果文件存在就清空,如果文件不存在就创建,将光标定位到开头
   a      以追加的方式打开文件,如果文件不存在就创建,如果文件存在不会清空,将光标定位到结尾
   a+     以读和追加的方式打开文件,如果文件不存在就创建,读光标在开头,写光标在结尾(光标就一个)
返回值:成功返回文件指针,失败返回NULL,置位错误码errno
int fclose(FILE *stream);
功能:关闭文件
参数:
    @stream:文件指针
 返回值:成功返回0,失败返回EOF(-1),置位错误码  //#define EOF (-1)

errno(内核中错误码问题)

#include <errno.h>
extern int errno;
errno 存放错误码

当用户通过fopen调用系统接口的时候,会打开文件,如果文件打开失败会给fp设置为NULL,同时将errno设置为对应的错误码,在内核中一个有4K个错误码,通过错误码的数值表示错误的类型。(errno是在#include <errno.h>的头文件中声明的)
在这里插入图片描述

strerror、perror函数API

#include <string.h>
char *strerror(int errnum);
功能:将错误码转换为错误信息
参数:
    @errnum:错误码
返回值:错误信息的字符串
#include <stdio.h>
void perror(const char *s);
功能:打印错误信息
参数:
    @s:用户的附加信息
返回值:无

1.1fopen、fclose函数实例1

#include <stdio.h>

int main(int argc, const char *argv[])
{
    // 1.定义文件流指针
    FILE *fp;

    // 2.以只写方式打开文件,若文件不存在,创建文件
    if (NULL == (fp = fopen("./1.txt", "w"))) {
        perror("fopen errror");
        return -1;
    }

    // 3.关闭文件流指针
    fclose(fp);

    puts("--------start");
    fclose(stdout);
    puts("--------end");  //关闭标准输出流stdout,终端不会打印这行
    return 0;
}

1.2strerror、perror函数实例2

#include <stdio.h>
#include <errno.h>
#include <string.h>

int main(int argc, const char *argv[])
{
    // 1.定义文件流指针
    FILE *fp;

    // 2.以只读方式打开文件,若文件不存在,打印错误信息
    if (NULL == (fp = fopen("./1.txt", "r"))) {
        printf("errno = %d, errmsg = %s\n", errno, strerror(errno));
        perror("fopen error");
        return -1;
    }

    // 3.关闭文件流指针
    fclose(fp);
    return 0;
}

2.fgetc、fputc、getchar、putchar(读、写一个字符)

fgetc、fputc函数API

int fgetc(FILE *stream);
功能:从文件中读取一个字符(光标自动向后移动)
参数:
    @stream:文件指针
返回值:成功返回读取到的字符的ASCII,读取到结尾或者遇到错误就返回EOF
int fputc(int c, FILE *stream);
功能:向文件中写入一个字符(光标自动向后移动)
参数:
    @c:字符的ascii的值 'c' 65
    @stream:文件指针
返回值:成功返回写入到的字符的ASCII,失败返回EOF

getchar、putchar函数API

int getchar(void);
getchar()等同于fgetc(stdin)
int putchar(int c);
putchar(c)等同于fputc(ch, stdout)

2.1fgetc函数实例1(从文件读)

#include <stdio.h>

int main(int argc, const char *argv[])
{   
    FILE *fp;
    if (NULL == (fp = fopen("./1.txt", "r"))) {
        perror("fopen error");
        return -1;
    }

    // 文件内容 abcd
    char ch;
    while (EOF != (ch = fgetc(fp))) {
        printf("%c", ch);
    }
    
    fclose(fp);
    return 0;
}

2.2fgetc函数实例2(从终端读)

#include <stdio.h>

int main(int argc, const char *argv[])
{
    char ch;
    ch = fgetc(stdin);
    printf("ch = %c\n", ch);

    return 0;
}

2.3fgetc函数实例3(统计文件行号)

#include <stdio.h>

int main(int argc, const char *argv[])
{   
    // 1.对输入参数的个数进行检查
    if (2 != argc) {
        fprintf(stderr, "Usage: %s <file>\n", argv[0]);
        return -1;
    }

    // 2.以只读的方式打开文件
    FILE *fp;
    if (NULL == (fp = fopen(argv[1], "r"))) {
        perror("fopen error");
        return -1;
    }

    // 3.读取文件中的字符
    int line = 0;
    char ch;
    // 如果没有读取到文件的结尾,循环就继续,如果读取到文件的结尾那就停止
    while (EOF != (ch = fgetc(fp))) {
        if ('\n' == ch) {
            line++;
        }
    }

    // 4.打印行号,关闭文件
    printf("%s line = %d\n", argv[1], line);
    fclose(fp);

    return 0;
}

2.4fputc函数实例4(向文件写)

#include <stdio.h>

int main(int argc, const char *argv[])
{
    if (2 != argc) {
        fprintf(stderr, "Usage: %s <file>\n", argv[0]);
        return -1;
    }

    FILE *fp;
    if (NULL == (fp = fopen(argv[1], "w"))) {
        perror("fopen error");
        return -1;
    }

    char ch = 'A';
    fputc(ch, fp);

    fputc('h', fp);
    fputc('e', fp);
    fputc('l', fp);
    fputc('l', fp);
    fputc('o', fp);
    fputc('!', fp);

    fclose(fp);
    return 0;
}

2.5fputc函数实例5(向终端写)

#include <stdio.h>

int main(int argc, const char* argv[])
{
    //向终端上写hello'\n'
    fputc('h', stdout);
    fputc('e', stdout);
    fputc('l', stdout);
    fputc('l', stdout);
    fputc('o', stdout);
    fputc('\n', stdout);

    return 0;
}

2.6fputc函数实例6(文件拷贝)

#include <stdio.h>

int main(int argc, const char *argv[])
{   
    // 1.对输入参数的个数进行检查
    if (3 != argc) {
        fprintf(stderr, "Usage: %s <src_file> <dest_file>\n", argv[0]);
        return -1;
    }

    // 2.以只读的方式打开文件,以只写方式打开目标文件
    FILE *sfp, *dfp;
    if (NULL == (sfp = fopen(argv[1], "r"))) {
        perror("open src_file error");
        return -1;
    }
    if (NULL == (dfp = fopen(argv[2], "w"))) {
        perror("open dest_file error");
        return -1;
    }

    // 3.读取文件中的字符
    char ch;
    while (EOF != (ch = fgetc(sfp))) {
        fputc(ch, dfp);
    }

    // 4.关闭文件
    fclose(sfp);
    fclose(dfp);
    return 0;
}

3.fgets、fputs、gets、puts(读、写一行字符)

fgets、fputs函数API

char *fgets(char *s, int size, FILE *stream);
功能:从文件中读取字符串到向s指向的内存中,遇到'\n'或size-1个字符返回(最后一个用于存'\0')。
	fgets遇到EOF或者换行符的时候就会停止,如果遇到换行符停止的,换行符也会被存储到s中。
参数:
    @s:指向存储字符的首地址
    @size:想要读取字符的个数
    @stream:文件指针
返回值:成功返回s,失败返回NULL
int fputs(const char *s, FILE *stream);
功能:将s指向的内存中的字符串写入到文件中
参数:
    @s:被写字符串的首地址
 	@steam:文件指针
返回值:成功返回大于0的值,失败返回EOF

gets、puts函数API

char *gets(char *s);
功能:gets从标准输入stdin读字符串到s指向的内存
返回值:成功返回时s,遇到'\n'或已输入size-1个字符时返回,总是包含'\0',到文件末尾或出错返回NULL
gets不推荐使用,容易造成缓冲区溢出
int puts(const char *s);
功能:puts将缓冲区s中的字符串输出到stdout,并追加'\n'
返回值:成功时返回输出的字符个数,失败返回EOF

3.1fgets函数实例1(从文件读)

#include <stdio.h>

int main(int argc, const char *argv[])
{
    FILE *fp;
    if (NULL == (fp = fopen("./1.txt", "r"))) {
        perror("fopen error");
        return -1;
    }
 
    char buf[10] = {0};
    //fgets读取到换行符'\n'或sizeof(buf)-1个字符返回,将读取字符串最后一个字符buf[strlen(buf)]填充为'\0'
    while (NULL != fgets(buf, sizeof(buf), fp)) {
        printf("%s", buf);  //注意:在linux中创建的文本中最后末尾一行也有换行符'\n'
    }

    fclose(fp);
    return 0;
}

3.2fgets函数实例2(从终端读)

#include <stdio.h>
#include <string.h>

int main(int argc, const char* argv[])
{
    char buf[10] = {0};

    //终端输入: hello\n
    //fgets读取到换行符'\n'或sizeof(buf)-1个字符返回,将读取字符串最后一个字符buf[strlen(buf)]填充为'\0'
    if (NULL == fgets(buf, sizeof(buf), stdin)) {
        printf("fgets error\n");
        return -1;
    }

    buf[strlen(buf) - 1] = '\0';  //将终端读取的'\n'设置为'\0'
    printf("buf = %s\n", buf);  //buf = hello,终端输出的换行符是printf里的
    return 0;
}

3.3fgets函数实例3(统计文件行号)

#include <stdio.h>
#include <string.h>

int main(int argc, const char *argv[])
{
     // 1.对输入参数的个数进行检查
    if (2 != argc) {
        fprintf(stderr, "Usage: %s <file>\n", argv[0]);
        return -1;
    }

    // 2.以只读的方式打开文件
    FILE *fp;
    if (NULL == (fp = fopen(argv[1], "r"))) {
        perror("fopen error");
        return -1;
    }

    // 3.读取文件中的字符
    char buf[10] = {0};
    int line = 0;
    //fgets读取到换行符'\n'或sizeof(buf)-1个字符返回,将读取字符串最后一个字符buf[strlen(buf)]填充为'\0'
    while (NULL != fgets(buf, sizeof(buf), fp)) {  
        if ('\n' == buf[strlen(buf) - 1]) {  //注意:在linux中创建的文本中最后末尾一行也有换行符'\n'
            line++;
        }
    }

    // 4.打印结果,关闭文件
    printf("%s line = %d\n", argv[1], line);
    fclose(fp);
    return 0;
}

3.4fputs函数实例4(向文件写)

#include <stdio.h>
#include <string.h>

int main(int argc, const char *argv[])
{
    FILE *fp;
    if (NULL == (fp = fopen("./1.txt", "w"))) {
        perror("fopen error");
        return -1;
    }

    char buf[20] = "hello world!";
    fputs(buf, fp);
    fputs("xiao ming", fp);

    fclose(fp);
    return 0;
}

3.5fputs函数实例(向终端写)

#include <stdio.h>

int main(int argc, const char *argv[])
{
    char buf[20] = "hello wrorld";
    fputs(buf, stdout);
    fputs("xiao ming\n", stdout);
    
    return 0;
}

4.fread、fwrite、feof、ferror(读、写若干个对象,判断文件是否结束或出错)

fread、fwrite函数API
即可读写文本文件,也可读写数据文件,效率高。

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从stream中读取nmemb项数据,每一项的大小是size,将它们存到ptr。
参数:
	@ptr:存储读取到数据的首地址
 	@size:每一项的大小
	@nmemb:项目的个数
    @stream:文件指针
返回值:成功返回读取到的项目的个数,如果小于项目的个数就是错误或者到文件的结尾了。
		fread读取到错误或文件结尾返回,不会给读取的字符串数据填充'\0'。
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
功能:将ptr中的数据写入到stream中,写nmemb向,每一项的大小是size
参数:
	@ptr:被写数据的首地址
	@size:每一项的大小
	@nmemb:项目的个数
	@stream:文件指针
返回值:成功返回项目的个数,失败返回小于项目的个数。

feof、ferror函数API

fread返回值无法区分是到达了文件的结尾还是错误发生了,调用者必须通过feof或者ferror函数来判断。
int feof(FILE *stream);
功能:判断是否读取到了文件的结尾,如果到了文件的结尾将返回真
参数:
    @stream:文件指针
返回值:如果到了文件的结尾返回真,如果没有到文件结尾返回假

int ferror(FILE *stream);
功能:如果在读文件的时候发生了错误,这个函数返回真
参数:
    @stream:文件指针
返回值:如果发生了错误返回真,如果没有发生错误返回假

4.1fread函数实例1(从文件读)

#include <stdio.h>

int main(int argc, const char *argv[])
{
    FILE *fp;
    if (NULL == (fp = fopen("./1.txt", "r"))) {
        perror("fopen error");
        return -1;
    }

    // fread读取到错误或文件结尾返回,不会给读取的字符串数据填充'\0'。
    char buf[10] = {0};
    int ret;
    ret = fread(buf, 1, sizeof(buf)-1, fp);  //最好sizeof(buf)-1, 防止当读满buf时,buf后连续内存不为'\0'

    printf("buf = %s", buf);
    printf("ret = %d\n", ret); //9
    printf("feof = %d, ferror = %d\n", feof(fp), ferror(fp));  //用vim创建的1.txt, 当输入字符为 <= 7 + ('/n''/0') 
                                                               //feof = 1, ferror = 0
                                                               //用vscode创建的1.txt, 当输入里字符为 <= 8 + ('/0')
                                                               //feof = 1, ferror = 0

    fclose(fp);
    return 0;
}

4.2fread函数实例2(从文件读)

#include <stdio.h>
#include <string.h>

int main(int argc, const char* argv[])
{
    // 1.对输入参数的个数进行检查
    if (argc != 2) {
        printf("input error,try again\n");
        printf("usage: ./a.out filename\n");
        return -1;
    }

    // 2.以只读的方式打开文件
    FILE* fp;
    if ((fp = fopen(argv[1], "r")) == NULL) {
        perror("fopen error");
        return -1;
    }

    // 3.读取数据
    char buf[10] = {0};
    int ret;
    // 如果在读的时候没有到结尾,并且也没有错误,循环继续
    while (!(feof(fp) || ferror(fp))) {
        // 在读取之前将数组中的内容清零,如果没有这句,最后一行打印的结果不对
        // 因为fread读取到错误或文件结尾返回,不会给读取的字符串数据填充'\0'。
        memset(buf, 0, sizeof(buf));
        fread(buf, 1, sizeof(buf)-1, fp);
        printf("%s", buf);
    }
    
    fclose(fp);
    return 0;
}

4.3fread函数实例3(读取整数、结构体)

配合4.4fwrite函数实例4使用

#include <stdio.h>

typedef struct {
    char name[20];
    int age;
    char sex;
}stu_t;

int main(int argc, const char* argv[])
{
    FILE* fp;
    if (NULL == (fp = fopen("./1.txt", "r"))) {
        perror("fopen error");
        return -1;
    }

    // 1.读取一个字符
    // char ch;
    // fread(&ch, 1, 1, fp);
    // printf("ch = %c\n", ch);

    // 2.读取数组
    // char buf[128] = {0};
    // fread(buf, 1, sizeof(buf)-1, fp);
    // printf("buf = %s\n", buf);

    // 3.读取整数
    // int num;
    // fread(&num, sizeof(num), 1, fp);
    // printf("num = %d\n", num);

    // 4.读取结构体
    stu_t stu;
    fread(&stu, sizeof(stu), 1, fp);
    printf("name=%s, age=%d, sex=%c\n",stu.name,stu.age,stu.sex);
    
    fclose(fp);
    return 0;
}

4.4fwrite函数实例4(写入整数、结构体)

配合4.3fread函数实例3使用

#include <stdio.h>
#include <string.h>

typedef struct{
    char name[20];
    int age;
    char sex;
}stu_t;

int main(int argc, const char* argv[])
{
    FILE* fp;
    if ((fp = fopen("./1.txt", "w")) == NULL) {
        perror("fopen error");
        return -1;
    }
    // 1.使用fwrite写一个字符
    // char ch='q';
    // fwrite(&ch, 1, 1, fp);

    // 2.使用fwrite写字符串
    // char buf[] = "hello everyone!";
    // fwrite(buf, 1, strlen(buf), fp);

    // 3.使用fwrite写整数
    // hello.txt是一个文本文件,文本文件中只能显示字符,并不能直接显示整数,
    // 但是数据确实是写进去了。写入的内容见下图
    // int num=12345;
    // fwrite(&num, sizeof(num), 1, fp);

    // 4.使用fwrite写结构体
    stu_t stu = {
        .name = "xiao ming",
        .age = 30,
        .sex = 'm'
    };
    fwrite(&stu, sizeof(stu), 1, fp);

    fclose(fp);
    return 0;
}

4.5fwrite写整数到文件,文本内容解析

在这里插入图片描述

4.6fread、fwrite函数实例5(文件拷贝)

#include <stdio.h>
#include <string.h>

int main(int argc, const char *argv[])
{
    // 1.对输入参数的个数进行检查
    if (3 != argc) {
        fprintf(stderr, "Usage: %s <src_file> <dest_file>\n", argv[0]);
        return -1;
    }

    // 2.以只读的方式打开源文件,以只写方式打开目标文件
    FILE *sfp, *dfp;
    if (NULL == (sfp = fopen(argv[1], "r"))) {
        perror("fopen src_file error");
        return -1;
    }
    if (NULL == (dfp = fopen(argv[2], "w"))) {
        perror("fopen dest_file error");
        return -1;
    }
 
    // 3.循环拷贝
    // 如果在读的时候没有到结尾,并且也没有错误,循环继续
    char buf[10] = {0};
    int ret;
    while (!(feof(sfp) || ferror(sfp))) {
        ret = fread(buf, 1, sizeof(buf), sfp);
        fwrite(buf, 1, ret, dfp);
    }

    // 4.判断返回数据项个数也行
    // while (0 < (ret = fread(buf, 1, sizeof(buf), sfp))) {
    //     fwrite(buf, 1, ret, dfp);
    // }

    fclose(sfp);
    fclose(dfp);
    return 0;
}

5.sprintf、snprintf、fprintf(格式化输出到内存、文件)

sprintf、snprintf、fprintf函数API

int sprintf(char *str, const char *format, ...);
功能:将format控制格式的内容写到str对应的内存中(不会向终端显示)
参数:
    @str:内存地址
    @format:和printf的参数完全相同
返回值:成功返回格式化的字符的个数,失败返回负数
int snprintf(char *str, size_t size, const char *format, ...);
功能:将format控制格式的内容写到str对应的内存中(不会向终端显示)
参数:
    @str:内存地址
    @size:最多格式化size个字符,其中还包括一个'\0'
	@format:和printf的参数完全相同
返回值:成功返回格式化的字符的个数,失败返回负数
int fprintf(FILE *stream, const char *format, ...);
功能:将控制格式格式化的字符串写入到文件中
参数:
    @stream:文件指针
 	@format:控制格式
返回值:成功返回格式化的字符的个数,失败返回负数

5.1sprintf函数实例1

#include <stdio.h>
#include <string.h>

typedef struct{
    char name[20];
    int age;
    char sex;
}stu_t;
int main(int argc, const char* argv[])
{
    FILE* fp;

    if ((fp = fopen("./hello.txt", "w+")) == NULL) {
        perror("fopen error");
        return -1;
    }
    //1.使用fwrite写整数
    //hello.txt是一个文本文件,文本文件中只能显示字符,并不能直接显示整数,
    //但是数据确实是写进去了。
    int num=123456;
    char buf[10] = {0};
    sprintf(buf,"%d\n",num); 
    //使用sprintf函数的时候一定要注意越界问题,如果越界程序错误
    fwrite(buf,1,strlen(buf),fp);

    //2.使用fwrite写结构体
    stu_t stu = {
        .name = "zhangsan",
        .age = 30,
        .sex = 'm'
    };
    char buf1[50] = {0};
    sprintf(buf1,"%s,%d,%c\n",stu.name,stu.age,stu.sex);
    fwrite(buf1,1,strlen(buf1),fp);

    fclose(fp);
    return 0;
}

5.2snprintf函数实例2

#include <stdio.h>
#include <string.h>

typedef struct{
    char name[20];
    int age;
    char sex;
}stu_t;
int main(int argc, const char* argv[])
{
    FILE* fp;

    if ((fp = fopen("./hello.txt", "w+")) == NULL) {
        perror("fopen error");
        return -1;
    }
    //1.使用fwrite写整数
    //hello.txt是一个文本文件,文本文件中只能显示字符,并不能直接显示整数,
    //但是数据确实是写进去了。
    int num=123456;
    char buf[10] = {0};
    snprintf(buf,sizeof(buf),"%d",num); 
    //使用sprintf函数的时候一定要注意越界问题,如果越界程序错误
    fwrite(buf,1,strlen(buf),fp);

    //2.使用fwrite写结构体
    stu_t stu = {
        .name = "zhangsan",
        .age = 30,
        .sex = 'm'
    };
    char buf1[50] = {0};
    snprintf(buf1,sizeof(buf1),"%s,%d,%c\n",stu.name,stu.age,stu.sex);
    fwrite(buf1,1,strlen(buf1),fp);

    fclose(fp);
    return 0;
}

5.4fprintf函数实例(获取系统时间到终端显示)

#include <stdio.h>
#include <time.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	time_t ts;
	struct tm *tm;

	while (1) {	
		if (-1 == (ts = time(NULL))) {
			perror("get time error");
			return -1;
		}
		if (NULL == (tm = localtime(&ts))) {
			perror("change time error");
			return -1;
		}	
		fprintf(stdout, "%d-%02d-%02d %02d:%02d:%02d\r", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
		fflush(stdout);
		sleep(1);
	}

	return 0;
}

6.time、localtime(获取系统时间,将系统时间转为本地时间)

time、localtime函数API

#include <time.h>
time_t time(time_t *tloc);
功能:获取自1970-01-01 00:00:00到当前的秒钟数
参数:
    @tloc:NULL
返回值:成功返回秒钟数,失败返回-1,并置位错误码
struct tm *localtime(const time_t *timep);
功能:将time_t的秒钟转换为tm的结构体(结构体中包含年、月、日...)
参数:
    @timep:秒钟变量的地址
返回值:成功返回tm结构体指针,失败返回NULL,并置位错误码
       struct tm {
           int tm_sec;    /* Seconds (0-60) */
           int tm_min;    /* Minutes (0-59) */
           int tm_hour;   /* Hours (0-23) */
           int tm_mday;   /* Day of the month (1-31) */
           int tm_mon;    /* Month (0-11) */   //+1
           int tm_year;   /* Year - 1900 */    //+1900
           int tm_wday;   /* Day of the week (0-6, Sunday = 0) */ //周几
           int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */ //这一年中的第几天
           int tm_isdst;  /* Daylight saving time */ //夏令时
       };

8.fflush(刷新缓冲区)

fflush函数API

int fflush(FILE *fp);
功能:将流缓冲区里的数据写入到实际的文件,Linux下只能刷新输出缓冲区
参数:
	@fp:文件指针
返回值:成功返回0,失败返回EOF

9.fseek、rewind、ftell(定位文件指针)

fseek、rewind、ftell函数API

int fseek(FILE *stream, long offset, int whence);
功能:设置光标的位置
参数:
    @stream:文件指针
	@offset:光标的偏移
        >0 向后偏移
        =0 不偏移
        <0 向前偏移
    @whence:从那个位置偏移
        SEEK_SET //从开头开始偏移
  SEEK_CUR //从当前位置偏移
        SEEK_END //从结尾的位置偏移
返回值:成功返回0,失败返回-1置位错误码  
        
eg:
 fseek(fp,0,SEEK_END); //将光标定位到文件的结尾
 fseek(fp,50,SEEK_SET);//将光标定位到第50个字节的位置
 fseek(fp,-5,SEEK_CUR);//将光标从当前位置向前偏移5个字节

 1. 文件a模式打开时,函数fseek无效
 2. rewind(fp)相当于fseek(fp, 0, SEEK_SET)
 3. 这3个函数只适用于2G以下的文件
void rewind(FILE *stream);
功能:将光标恢复到文件的开头(rewind = fseek(stream, 0, SEEK_SET))
参数:
    @stream:文件指针
返回值:无
long ftell(FILE *stream);
功能:返回光标到文件开头的字节数
参数:
    @stream:文件指针
返回值:成功返回字节数,失败返回-1置位错误码

9.1fseek函数实例1

#include <stdio.h>

#define PRINT_ERR(msg) \
    do {               \
        perror(msg);   \
        return -1;     \
    } while (0)

int main(int argc, const char* argv[])
{
    FILE* fp;

    if ((fp = fopen("./hello.txt", "r+")) == NULL)
        PRINT_ERR("fopen error");

    if (fseek(fp, 4, SEEK_SET))
        PRINT_ERR("fseek error");

    char ch;
    ch = fgetc(fp);
    printf("ch = %c\n",ch);
    fputc('Q',fp);

    fclose(fp);
    return 0;
}

9.2ftell函数实例2(输出文件大小)

#include <stdio.h>

#define PRINT_ERR(msg) \
    do {               \
        perror(msg);   \
        return -1;     \
    } while (0)

int main(int argc, const char* argv[])
{
    FILE* fp;

    if(argc !=2){
        fprintf(stderr,"input error,try again\n");
        fprintf(stderr,"usage:./a.out filename\n");
        return -1;
    }
    if ((fp = fopen(argv[1], "r")) == NULL)
        PRINT_ERR("fopen error");

    if (fseek(fp, 0, SEEK_END))
        PRINT_ERR("fseek error");

    printf("pos = %ld\n",ftell(fp));

    fclose(fp);
    return 0;
}

10.freopen(重定向输入输出流)

在这里插入图片描述

10.1freopen函数实例

#include <stdio.h>

int main(int argc, const char **argv)
{
	if (freopen("1.txt", "w", stdout) == NULL) {
		perror("freopen");
		return -1;
	}
	printf("stdout --> 1.txt\n");
	
	fclose(stdout);
	
	printf("end!\n");
	return 0;
}

三、缓冲区相关知识

1.缓冲区的分类

行缓存:和终端相关的缓冲区就是行缓存(stdin stdout)
全缓存:和文件相关的缓冲区就是全缓存(fp)
无缓存:没有缓冲区(stderr)

2.缓冲区的大小

行缓存:1024(1K)
全缓存:4096(4K)
无缓存:0

#include <stdio.h>

int main(int argc, const char *argv[])
{
    // 1.stdin缓冲区大小 1024(使用了才分配)
    //FILE *stdin; --->FILE是一个结构体类型,stdin结构体指针,结构体指针访问内部成员->
    int num;
    scanf("%d", &num);
    printf("stdio buffer size = %ld\n", stdin->_IO_buf_end - stdin->_IO_buf_base);  //1024

    // 2.stdout缓冲区大小 1024
    printf("stdout buffer size = %ld\n", stdout->_IO_buf_end - stdout->_IO_buf_base);  //1024

    // 3.文件相关的缓冲区的大小 4096
    FILE *fp;
    if (NULL == (fp = fopen("./1.txt", "w"))) {
        perror("fopen error");
        return -1;
    }
    fputc(num, fp);
    printf("fp buffer size = %ld\n", fp->_IO_buf_end - fp->_IO_buf_base);  //4096
    
    fclose(fp);
    return 0;
}

3.缓冲区的刷新时机

3.1行缓冲的刷新时机(6种)

1.行缓存遇到换行符的时候会刷新缓冲区
2.当程序结束的时候会刷新行缓冲区
3.当关闭文件的时候会刷新行缓冲区
4.当输入和输出发生切换的时候也会刷新行缓冲区
5.当缓冲区满的时候会刷新缓冲区
6.自己使用fflush函数主动刷新缓冲区

#include <stdio.h>

int main(int argc, const char* argv[])
{
    // 1.行缓存遇到换行符的时候会刷新缓冲区
    // printf("1111111111\n");
    // while(1);

    // 2.当程序结束的时候会刷新行缓冲区
    // printf("1111111111");

    // 3.当关闭文件的时候会刷新行缓冲区
    // printf("1111111111");
    // fclose(stdout);
    // while (1);

    // 4.当输入和输出发生切换的时候也会刷新行缓冲区
    // printf("1111111111");
    // fgetc(stdin);
    // while (1);

    // 5.当缓冲区满的时候会刷新缓冲区
    // for(int i = 0; i < 1025; i++){
    //     fputc('o',stdout);
    // }
    // while(1);

    // 6.自己使用fflush函数主动刷新缓冲区
    printf("1111111111");
    fflush(stdout); //刷新缓冲区的函数
    while(1);

    return 0;
}

3.2全缓冲的刷新时机(5种)

除了换行符不能刷新缓冲区, 其他和行缓冲区一样。

1.当程序结束的时候会刷新行缓冲区
2.当关闭文件的时候会刷新行缓冲区
3.当输入和输出发生切换的时候也会刷新行缓冲区
4.当缓冲区满的时候会刷新缓冲区
5.自己使用fflush函数主动刷新缓冲区

#include <stdio.h>

int main(int argc, const char* argv[])
{
    FILE* fp;
    if (NULL == (fp = fopen("./1.txt", "w"))) {
        printf("fopen error\n");
        return -1;
    }
    // 1.全缓存遇到换行符的时候不会刷新缓冲区
    // fputs("1111111111\n",fp);
    // while(1);

    // 2.当程序结束的时候会刷新全缓冲区
    // fputs("1111111111",fp);

    // 3.当关闭文件的时候会刷新全缓冲区
    // fputs("1111111111",fp);
    // fclose(fp);
    // while (1);

    // 4.当输入和输出发生切换的时候也会刷新全缓冲区
    //  fputs("1111111111",fp);
    //  fgetc(fp);
    //  while (1);

    // 5.当缓冲区满的时候会刷新全缓冲区
    //  for(int i = 0; i < 4097; i++){
    //      fputc('o',fp);
    //  }
    //  while(1);

    // 6.自己使用fflush函数主动刷新全缓冲区
    fputs("1111111111", fp);
    fflush(fp); // 刷新缓冲区的函数
    while (1);

    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【文件I/O】(一)标准I/O 的相关文章

  • 为什么我可以直接从 bash 执行 JAR?

    我是一个长期从事 Java 工作的人 并且知道运行带有主类的 JAR 的方法MANIFEST MFJar 中的文件很简单 java jar theJar jar 我用它来启动 Fabric3 服务器 包含在bin server jar在其标
  • docker容器大小远大于实际大小

    我正在尝试从中构建图像debian latest 构建后 报告的图像虚拟大小来自docker images命令为 1 917 GB 我登录查看尺寸 du sh 大小为 573 MB 我很确定这么大的尺寸通常是不可能的 这里发生了什么 如何获
  • 在脚本内使用不带密码的 sudo

    由于某种原因 我需要作为用户在没有 sudo 的情况下运行脚本 script sh 该脚本需要 root 权限才能工作 我认为将 sudo 放入 script sh 中是唯一的解决方案 让我们举个例子 script sh bin sh su
  • 如何使用 GOPATH 的 Samba 服务器位置?

    我正在尝试将 GOPATH 设置为共享网络文件夹 当我进入 export GOPATH smb path to shared folder I get go GOPATH entry is relative must be absolute
  • CoAP数据包的大小是多少?

    我是这项技术的新手 有人可以帮助我了解一些疑问吗 Q 1 CoAP数据包的大小是多少 我知道有 4 字节固定标头 但是包括标头 选项和负载在内的最大大小限制是多少 Q 2 有像MQTT那样的Keep Alive的概念吗 它在UDP上工作 它
  • 我不明白 execlp() 在 Linux 中如何工作

    过去两天我一直在试图理解execlp 系统调用 但我还在这里 让我直奔主题 The man pageexeclp 将系统调用声明为int execlp const char file const char arg 与描述 execl exe
  • 需要一些建议来开始在 ARM(使用 Linux)平台上编程

    我 也许 很快就会在托管 Linux 发行版的 ARM 平台上工作 我不知道哪个发行版 我知道该项目涉及视频流 但我无法告诉你更多信息 其实我只收到通知 还没见到任何人 我从来没有在这样的平台上工作过 所以我的想法是在项目开始之前进行测试
  • 我如何知道 C 程序的可执行文件是在前台还是后台运行?

    在我的 C 程序中 我想知道我的可执行文件是否像这样在前台运行 a out 或者像这样 a out 如果你是前台工作 getpgrp tcgetpgrp STDOUT FILENO or STDIN FILENO or STDERR FIL
  • 监视目录的更改

    很像一个类似的问题 https stackoverflow com questions 112276 directory modification monitoring 我正在尝试监视 Linux 机器上的目录以添加新文件 并希望在这些新文
  • 确定我可以向文件句柄写入多少内容;将数据从一个 FH 复制到另一个 FH

    如何确定是否可以将给定数量的字节写入文件句柄 实际上是套接字 或者 如何 取消读取 我从其他文件句柄读取的数据 我想要类似的东西 n how much can I write w handle n read r handle buf n a
  • 快速像素绘图库

    我的应用程序以每像素的方式生成 动画 因此我需要有效地绘制它们 我尝试过不同的策略 库 但结果并不令人满意 尤其是在更高分辨率的情况下 这是我尝试过的 SDL 好的 但是慢 OpenGL 像素操作效率低下 xlib 更好 但仍然太慢 svg
  • 如何在c linux中收听特定接口上的广播?

    我目前可以通过执行以下操作来收听我编写的简单广播服务器 仅广播 hello int fd socket PF INET SOCK DGRAM 0 struct sockaddr in addr memset addr 0 sizeof ad
  • C++ Boost ASIO 简单的周期性定时器?

    我想要一个非常简单的周期性计时器每 50 毫秒调用我的代码 我可以创建一个始终休眠 50 毫秒的线程 但这很痛苦 我可以开始研究用于制作计时器的 Linux API 但它不可移植 I d like使用升压 我只是不确定这是否可能 boost
  • 高效的内存屏障

    我有一个多线程应用程序 其中每个线程都有一个整数类型的变量 这些变量在程序执行期间递增 在代码中的某些点 线程将其计数变量与其他线程的计数变量进行比较 现在 我们知道在多核上运行的线程可能会无序执行 一个线程可能无法读取其他线程的预期计数器
  • 在 Mac OSX 上交叉编译 x86_64-unknown-linux-gnu 失败

    我尝试将我的 Rust 项目之一编译到 x86 64 unknown linux gnu 目标 cargo build target x86 64 unknown linux gnu Compiling deployer v0 1 0 fi
  • 监控子进程的内存使用情况

    我有一个 Linux 守护进程 它分叉几个子进程并监视它们是否崩溃 根据需要重新启动 如果父进程可以监视子进程的内存使用情况 以检测内存泄漏并在超出一定大小时重新启动子进程 那就太好了 我怎样才能做到这一点 您应该能够从 proc PID
  • Capistrano 3 部署无法连接到 GitHub - 权限被拒绝(公钥)

    我使用 Capistrano v3 和 capistrano symfony gem 设置了以下部署脚本 我正在使用 Ubuntu 14 4 部署到 AWS EC2 实例 我正在连接从 AWS 下载的 pem 文件 我的deploy rb中
  • 限制 Imagemagick 使用的空间和内存

    我在 Rails 应用程序上使用 Imagemagick 使用 rmagick 但我的服务器 Ubuntu 不是很大 当我启动转换进程时 Imagemagick 占据了我的服务器 30GB HDD 的所有位置 内存 我想限制内存和 tmp
  • vagrant ssh -c 并在连接关闭后保持后台进程运行

    我正在编写一个脚本来启动和后台流浪机器内的进程 似乎每次脚本结束和 ssh 会话结束时 后台进程也会结束 这是我正在运行的命令 vagrant ssh c cd vagrant src nohup python hello py gt he
  • 嵌入式linux编写AT命令

    我在向 GSM 模块写入 AT 命令时遇到问题 当我使用 minicom b 115200 D dev ttySP0 term vt100 时它工作完美 但我不知道如何在 C 代码中做同样的事情 我没有收到任何错误 但模块对命令没有反应 有

随机推荐

  • HiveSQL原理和优化详解

    Hive SQL 编译成MapReduce过程 编译 SQL 的任务是在上节中介绍的 COMPILER 编译器组件 中完成的 Hive将SQL转化为MapReduce任务 整个编译过程分为六个阶段 词法 语法解析 Antlr 定义 SQL
  • javascript相关

    1 扁平数据结构转Tree 打平的数据内容如下 let arr id 1 name 部门1 pid 0 id 2 name 部门2 pid 1 id 3 name 部门3 pid 1 id 4 name 部门4 pid 3 id 5 nam
  • vscode编辑器插件总结

    之前一直用webstorm webstorm确实太重了 后来无意中发现了vscode 高颜值吸引了我哈哈哈 就一直用着 很喜欢VScode的插件功能 想要什么插件就搜索 比如搜索angular 只要点击一下某款插件 插件的介绍和用法都会在右
  • feign的Fallback机制

    对接口使用 FeignClient后声明feign客户端后 可以使用属性fallback指定异常处理类 这个类必须实现 FeignClient作用的接口 且被注入到容器中 FeignClient name service provider1
  • 浪潮

    这是一篇旧闻 是我2011年8月6日发在豆瓣上的 前几天重玩豆瓣 看到了 很多怀念 我感到了生命的浪潮 读西哲史有感 o 不会吧 浑浑噩噩的大学生活居然过去一半了啊 当年读 此间的少年 满以为大学就是乔峰 慕容复PK 令狐冲 杨康宿舍里面切
  • 测试分为什么,白盒,黑盒,单元,集成测试?

    一 为什么测试的概念这么多 一个软件项目就好比一部复杂的汽车 有很多零件 当每个零件生产完成后 就要测试零件是否存在质量问题 零件组成复杂的汽车后 我们还要测试汽车 比如著名的中保研 测试刹车 测试气囊 测试防撞 顾客从4s店购买汽车 要带
  • Vue学习(五)登陆页面之重置和发起登陆请求及弹窗提示

    Vue学习 五 登陆页面之重置和发起登陆请求及弹窗提示 表单重置 根据预验证结果决定是否发出登陆请求 编写代码 启动api服务器 弹窗提示 表单重置 直接调用element ui给我们写好的函数就可以了 获取当前表单的实例对象 通过这个实例
  • java中实现多态的机制是什么_java中实现多态的机制是什么? java什么是多态?

    学习java刚刚入门的小伙伴们 不知道大家在初次接触java中的多态一概念的时候 是否能清晰的讲出实现多态的机制是什么吗 什么才是java中的多态呢 多态性是指的面向对象程序设计代码重用的一个重要的机制 对于Java多态性 应该都不是第一次
  • Android Framework——进程间通讯学习,从Binder使用看起

    前言 Binder 是安卓中非常重要的进程间通讯工具 通过Binder 安卓在ServiceManager中对外提供了一系列的服务 学习Binder 将很好地为我们学习framework开个好头 Android 使用多进程 Android
  • 5分钟带你看懂Jeesite10大功能要点

    jeesite内容丰富 集成了大量优秀的组件 是一个值得研究的框架 它有 1 shiro安全权限控制 2 mybatis查询缓存接口扩展 3 ecache分布式缓存整合 4 页面资源缓存优化 5 多数据源灵活切换 6 mybatismapp
  • EasyExcel轻松读取Excel文件!

    EasyExcel是一个Java库 用于快速 简单地读写Excel文件 要使用EasyExcel 您首先需要将其添加为项目的依赖 如果使用Maven 可以添加以下依赖项
  • 算法:二分查找之第一个错误的版本

    方法一 class Solution public int firstBadVersion int n int left 1 int right n while left lt right int mid right left 2 left
  • 【React】 4课 react初识组件

    首先我们如1课创建一个文件夹在文件夹中安装react环境需要的配置文件 npm init y npm i babel standalone D npm i react react dom D 安装配置文件教程链接 https blog cs
  • 4个开源的Java代码静态分析工具

    1 PMD PMD是一款采用BSD协议发布的Java程序代码检查工具 该工具可以做到检查Java代码中是否含有未使用的变量 是否含有空的抓取块 是否含有不必要的对象等 该软件功能强大 扫描效率高 是Java程序员debug的好帮手 PMD支
  • Lex和Yacc应用教程(四).语法树的应用

    Lex和Yacc应用方法 四 语法树的应用 草木瓜 20070515 一 序 不论什么语言 语法结构总是那几种 可以想象任何程序体都可以解释成一棵语法树 语法树的本质是递归 很显然Yacc文法的核心思想也是递归 本文就通过具体实例 使用Ya
  • cesium加载影像的问题解决

    我用gdal把web墨卡托转为经纬度 再切分片时 发现对不上影像 经过两天排查 发现竟然是前端写错 viewer scene imageryLayers addImageryProvider new Cesium UrlTemplateIm
  • vscode乱码

    vscode中文乱码怎么解决 vscode是一款跨平台源代码编辑器 能够在桌面上运行 并且能够用途windows macOS以及Linux 但是有不少小伙伴们在使用vscode时 输入输出的却是中文代码 也不知道如何解决 那么今天小编就来告
  • Ribbon负载均衡策略DynamicServerListLoadBalancer的ServerListFilter解读

    一 DynamicServerListLoadBalancer在类图中的位置 二 DynamicServerListLoadBalancer源码解读 1 关键代码请见注释 2 源码位置 ribbon master ribbon loadba
  • JDK 8 List集合使用记录

    JDK8 的新特性给我们开发带来了很大的便利性 先声明 我没有系统的去学习 JDK8的这些所有新特性 本文只是记录一些我个人日常开发中常遇到的一些 JDK8 的新特性方法 1 提取对象集合中的某一属性集合 List lt 对象 gt gt
  • 【文件I/O】(一)标准I/O

    标准I O 库函数 一 I O相关知识 1 1最早接触的I O 1 2I O的种类 1 3库函数和系统调用 1 4什么是FILE 二 标准I O函数 1 fopen fclose strerror perror 打开 关闭文件 输出错误码信