IO 学习

一丶IO基础

1> IO:(inout output) 所谓IO,就是程序与外部设备进行信息交换的过程

2> IO的分类:标准IO和文件IO

3> 标准IO:调用封装好的相关库函数,来实现数据的输入输出

4> 文件IO:调用系统(内核)提供的相关函数,来实现数据的输入输出

5> 标准IO和文件IO的区别:

1、标准IO属于库函数,文件IO属于系统调用

2、标准IO操作的是文件指针,文件IO操作的是文件描述符

3、标准IO有缓冲器,文件IO没有缓冲区

6> 阻塞IO和非阻塞IO

7> 目前所接触过的IO函数:

printf、scanf、putchar、getchar、puts、gets

8> 常用的IO接口函数

标准IO:fprint、fscanf、fputc、fgetc、fputs、fgets、fread、 fwrite、fopen、fclose、fseek、ftell、rewind。。。

文件IO:open、close、read、write、seek。。。

9> IO操作流程

二丶 标准IO

2.1 标准IO提供的内容

可以通过指令:man 7 stdio.h 可以查看该头文件提供的内容

1、标准的缓冲输入输出

2、提供的数据类型 FILE 文件结构体类型. off_t 偏移量类型 size_t 大小的类型,一般是一个long int类型

3、提供的偏移量的宏值 SEEK_SET:文件起始位置 SEEK_END:文件结束位置 SEEK_CUR:文件当前位置

4、提供文件结束标志:EOF (end of file)

5、默认提供的文件指针: stdin:标准输入指针 stdout:标准输出指针 stderr:标准出错指针

6、标准IO会提供三种缓冲区: 全缓存 行缓存 不缓存

2.2 FILE结构体

1> 如何追该结构体:可以使用 vi -t FILE

2> 结构体解析

245 struct _IO_FILE {

257 char* _IO_buf_base; /* 缓冲区开始地址 */

258 char* _IO_buf_end; /* 缓冲区结束地址 */

267 268 int _fileno; //用于系统调用的文件描述符

286 };

 3> 当一个程序启动后,系统会自动打开三个文件指针:

stdin:标准输入文件指针 ---> scanf、getchar、gets

stdout:标准输出文件指针 ----> printf、putchar、puts

stderr:标准出错文件指针 ---> perror

以上三个文件指针,都是针对于终端文件操作而言的

2.3 fopen 打开文件

#include <stdio.h>

        FILE *fopen(const char *pathname, const char *mode);

                功能:以指定的方式打开一个给定的文件,并返回该文件的文件地址

                参数1:要打开的文件文件路径,可以是相对路径,也可以是绝对路径

                参数2:文件打开的方式,需要以以下字符开头的字符串

                        r 以只读的形式打开文件,文件光标定位在开头.如果文件不存在,则报错

                        r+ 以读写的形式打开文件,文件光标定位在开头.如果文件不存在,则报错

                        w 以只写的形式打开文件,如果文件存在,则清空文件内容,如果文件不存在,则创建该文件,文件光标定位在开头.

                        w+ 以读写的形式打开文件,如果文件存在,则清空文件内容,如果文件不存在,则创建该文件,文件光标定位在开头.

                        a 以追加的形式打开文件,如果文件不存在则创建文件,文件光标定位在结尾                         a+ 以读或者追加的形式打开文件,如果文件不存在,则创建文件,如果第一次是读数据,则光标定位在开头,否则定位在结尾 返回值:成功调用返回打开的文件地址,失败返回NULL,并置位错误码

2.4 fclose:关闭文件

 #include <stdio.h>

int fclose(FILE *stream);

        功能:关闭给定的文件指针

        参数1:要关闭的文件指针

        返回值:成功返回0,失败返回EOF,并置位错误码

#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件类型的结构体以便于接受打开的文件地址
    FILE * fp = NULL;

    //打开一个文件
    //fp = fopen("./text.txt", "r");           //以只读的形式打开一个当前路径下的文件
    fp = fopen("./text.txt", "w");           //以只写的形式打开一个当前路径下的文件
    if(NULL == fp)
    {
        printf("fopen error\n");
        return -1;
    }

    printf("fopen success");   
    

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


    return 0;
}

2.5 fgetc\fputc:单字符的输入输出

#include <stdio.h>

int fputc(int c, FILE *stream);

        功能:将给定的字符,写入到文件指针stream指向的文件中去

        参数1:要写入的字符

        参数2:打开的文件指针

        返回值:成功返回写入字符的ascii值,失败返回EOF,并置位错误码

#include <stdio.h>

int fgetc(FILE *stream);

        功能:从指定文件中,读取一个字符

        参数:打开的文件指针

        返回值:从文件中读取的第一个字符的ascii值,失败返回EOF并置位错误码

#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件类型的结构体以便于接受打开的文件地址
    FILE * fp = NULL;

    //打开一个文件
    //fp = fopen("./text.txt", "r");           //以只读的形式打开一个当前路径下的文件
    fp = fopen("./text.txt", "w+");           //以只写的形式打开一个当前路径下的文件
    if(NULL == fp)
    {
        printf("fopen error\n");
        return -1;
    }

    printf("fopen success\n");

    //向文件中写入字符
    fputc('H', fp);
    fputc('e', fp);
    fputc('l', fp);
    fputc('l', fp);
    fputc('o', fp);
    fputc('\n', fp);
    //文件中存储的结果为 Hello  
    //说明每次写入数据时,光标会自动后移
    

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

    //再次以只读的形式打开文件
    fp = fopen("./text.txt", "r");           //以只写的形式打开一个当前路径下的文件
    if(NULL == fp)
    {
        printf("fopen error\n");
        return -1;
    }


    //定义变量用于读取数据
    char buf = 0;
    while(1)
    {
        buf = fgetc(fp);       //从fp中读取数据
        if(EOF == buf)          //表示文件读取结束
        {
            break;
        }

        printf("%c\t", buf);   //将读取的数据显示到终端
    }

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


    



    return 0;
}

//练习:
//1、使用fgetc完成,打开一个文件,统计该文件中的行数
#include<myhead.h>

int main(int argc, const char *argv[])
{
    //判断传入的参数是否为2个
    if(argc != 2)
    {
        printf("input file error!!!\n");
        printf("usage:./a.out fileName\n");
        return -1;
    }


    //表示传过来的有一个文件
    //定义文件指针
    FILE *fp = NULL;
    if((fp = fopen(argv[1], "r")) == NULL)
    {
        printf("fopen error\n");
        return -1;
    }

    //说明文件已经打开
    char buf = 0;        //接受字符
    int count = 0;       //统计行数
    while(1)
    {
        //从文件中读取一个字符
        buf = fgetc(fp);
        if(buf == EOF)
        {
            break;
        }

        //判断是否读取到回车
        if(buf == '\n')
        {
            count ++;
        }
    }

    //输出行号
    printf("该文件一共有%d行\n", count);

    //关闭文件
    fclose(fp);
    return 0;
}
//练习
//2、使用fgetc和fputc完成,cp指令的功能,实现两个文件的拷贝,将src文件中的内容,拷贝到dest文件中
#include<myhead.h>

int main(int argc, const char *argv[])
{
    //判断传入的是否为三个文件
    if(argc != 3)
    {
        printf("input file error\n");
        printf("usage:./a.out srcfile destfile\n");
        return -1;
    }

    //以只读的形式打开源文件
    FILE *sfp = NULL;
    if((sfp = fopen(argv[1], "r")) == NULL)
    {
        printf("open src file error\n");
        return -1;
    }

    //以只写的形式打开目标文件
    FILE *dfp = NULL;
    if((dfp = fopen(argv[2], "w")) == NULL)
    {
        printf("open destfile error\n");
        return -1;
    }

    //定义搬运工
    char buf = 0;
    //循环从源文件中读取数据放入到目标文件中
    while(1)
    {
        buf = fgetc(sfp);        //从源文件中读取
        if(buf == EOF)
        {
            break;
        }

        //将读取的数据写入目标文件中
        fputc(buf, dfp);
    }

    //关闭文件
    fclose(sfp);
    fclose(dfp);

    printf("拷贝成功\n");


    return 0;
}

2.6 有关错误码问题

1> 错误码是调用内核提供的函数时,如果调用出错,那么内核空间会向用户空间反馈一个错误信息

2> 错误信息千奇百怪,并且是字符串内容,为了方便起见,给这些错误信息,进行编号

3> 如果,内核空间函数调用出问题时,只需要反馈错误编号即可,这个错误编号就叫做错误码

4> 常见错误码:可以通过指令 vi -t EIO 查看

 5 #define EPERM        1  /* Operation not permitted */
  6 #define ENOENT       2  /* No such file or directory */
  7 #define ESRCH        3  /* No such process */
  8 #define EINTR        4  /* Interrupted system call */
  9 #define EIO      5  /* I/O error */
 10 #define ENXIO        6  /* No such device or address */
 11 #define E2BIG        7  /* Argument list too long */
 12 #define ENOEXEC      8  /* Exec format error */
 13 #define EBADF        9  /* Bad file number */
 14 #define ECHILD      10  /* No child processes */
 15 #define EAGAIN      11  /* Try again */
 16 #define ENOMEM      12  /* Out of memory */
 17 #define EACCES      13  /* Permission denied */
 18 #define EFAULT      14  /* Bad address */
 19 #define ENOTBLK     15  /* Block device required */
 20 #define EBUSY       16  /* Device or resource busy */
 21 #define EEXIST      17  /* File exists */
 22 #define EXDEV       18  /* Cross-device link */
 23 #define ENODEV      19  /* No such device */                                                            
 24 #define ENOTDIR     20  /* Not a directory */
 25 #define EISDIR      21  /* Is a directory */
 26 #define EINVAL      22  /* Invalid argument */
 27 #define ENFILE      23  /* File table overflow */
 28 #define EMFILE      24  /* Too many open files */
 29 #define ENOTTY      25  /* Not a typewriter */
 30 #define ETXTBSY     26  /* Text file busy */
 31 #define EFBIG       27  /* File too large */
 32 #define ENOSPC      28  /* No space left on device */
 33 #define ESPIPE      29  /* Illegal seek */
 34 #define EROFS       30  /* Read-only file system */
 35 #define EMLINK      31  /* Too many links */
 36 #define EPIPE       32  /* Broken pipe */
 37 #define EDOM        33  /* Math argument out of domain of func */
 38 #define ERANGE      34  /* Math result not representable */

5> 有关错误码的相关函数

1、strerror函数

         #include <string.h>

        char *strerror(int errnum);

                功能:将给定的错误码,转变成错误信息

                参数1:错误码 返回值:错误码对应的错误信息的字符串

错误码如何获得?

答:需要加一个头文件:#include<errno.h>

#include <errno.h>

const char * const sys_errlist[];

int sys_nerr;

int errno;         /* Not really declared this way; see errno(3) */

2、perror函数

        #include <stdio.h>

        void perror(const char *s);

                功能:向标准出错缓冲区中,写入最新的错误码对应的信息

                参数:提示字符串,会自动提供一个冒号,并且输出结束后,会自动加一个换行                 返回值:无

#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件指针
    FILE *fp = NULL;
    if((fp = fopen("./file.txt", "r")) == NULL)
    {
        //strerror:将错误码转变成错误信息
        //printf("errno:%s\n", strerror(errno));
        perror("fopen error");

        return -1;
    }

    printf("open success\n");

    return 0;
}

2.7 fputs\fgets:字符串输入输出

#include <stdio.h>

int fputs(const char *s, FILE *stream);

        功能:将给定的字符串,写入到文件中

        参数1:要写入的字符串起始地址

        参数2:打开的文件指针

        返回值:成功返回写入的字符个数(字符串长度),失败返回EOF

#include <stdio.h>

char *fgets(char *s, int size, FILE *stream);

        功能:从stream所指向的文件中,最多读取size-1的字符到s中,在读取过程中,如果遇到回车或者文件结束,会结束本次读取,并且会把回车也放入容器中。在后面自动加上'\0'

        参数1:存放数据的容器,一般是一个字符数组

        参数2:读取的大小

        参数3:文件指针

        返回值:成功返回容器的起始地址,失败返回NULL

#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件指针
    FILE *fp = NULL;
    if((fp = fopen("./file.txt", "w")) == NULL)
    {
        //strerror:将错误码转变成错误信息
        //printf("errno:%s\n", strerror(errno));
        perror("fopen error");

        return -1;
    }

    printf("open success\n");

    //向文件中写入字符串
    fputs("Hello world\n", fp);
    fputs("I love China\n", fp);
    fputs("Hua qing yuan jian\n", fp);
    fputs("good good study day day up\n", fp);


    //关闭文件
    fclose(fp);



    return 0;
}

#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件指针
    FILE *fp = NULL;
    if((fp = fopen("./file.txt", "w")) == NULL)
    {
        //strerror:将错误码转变成错误信息
        //printf("errno:%s\n", strerror(errno));
        perror("fopen error");

        return -1;
    }

    printf("open success\n");

    //向文件中写入字符串
    fputs("Hello world\n", fp);
    fputs("I love China\n", fp);
    fputs("Hua qing yuan jian\n", fp);
    fputs("good good study day day up\n", fp);

    //关闭文件
    fclose(fp);

    //以只读的形式重新打开文件
    if((fp = fopen("./file.txt", "r")) == NULL)
    {
        perror("fopen error");
        return -1;
    }

    //循环读取数据
    char buf[128] = "";
    while(1)
    {
        //清空容器
        bzero(buf,  sizeof(buf));
        if( fgets(buf, sizeof(buf), fp)  == NULL)
        {
            break;    //说明文件读取结束
        }

        //输出读取的数据
        printf("%s", buf);   
    }

    printf("\n");

    //关闭文件
    fclose(fp);



    return 0;
}

2.8 关于缓冲区

1> 标准IO提供了三种缓冲区:行缓存、全缓存、不缓存

2> 行缓存:有关标准输入、标准输出指针对应的缓冲区,其大小位1024字节

3> 全缓存:有关普通文件指针对应的缓冲区,其大小位4096字节

4> 不缓存:有关标准出错文件指针对应的缓冲区,其大小位 0

#include<myhead.h>

int main(int argc, const char *argv[])
{
    printf("行缓存的大小为:%ld\n", stdout->_IO_buf_end - stdout->_IO_buf_base);//0
    printf("行缓存的大小为:%ld\n", stdout->_IO_buf_end - stdout->_IO_buf_base);//1024
    getchar();        //使用一次标准输入
    printf("行缓存的大小为:%ld\n", stdin->_IO_buf_end - stdin->_IO_buf_base);//1024
    perror("usage");      //使用一次标准出错
    printf("不缓存的大小为:%ld\n", stderr->_IO_buf_end - stderr->_IO_buf_base);//0

    //验证全缓冲
    FILE *fp = NULL;
    if((fp = fopen("./tt.c", "w+")) == NULL)
    {
        perror("fopen error");
        return -1;
    }

    fgetc(fp);        //使用一次全缓冲
    printf("全缓存的大小为:%ld\n", fp->_IO_buf_end - fp->_IO_buf_base);//0
    


    //关闭
    fclose(fp);


    return 0;
}

5> 行缓存的刷新时机

1、换行会刷新行缓存

2、程序结束后,会自动刷新行缓存

3、当文件指针关闭后,会刷新行缓存

4、当使用fflush函数刷新文件指针时,会刷新行缓存

5、当输入输出切换时,会刷新行缓存

6、当缓存区满了后,再放数据时,会刷新行缓存

#include<myhead.h>

int main(int argc, const char *argv[])
{
    /*1、如果行缓存刷新时机未到,则不会刷新
    printf("hello world");            //不会输出
    while(1);
    */

    /*2、遇到换行,会刷新行缓冲
    printf("hello world\n");            //不会输出
    while(1);
    */

    /*3、当程序结束后,会刷新行缓冲
    printf("hello world");
    */

    /*4、当文件指针关闭后,会刷新行缓冲
    printf("hello world");
    fclose(stdout);            //关闭标准输出文件指针
    while(1);
    */

    /*5、手动调用fflush刷新缓冲器时,会刷新行缓冲
    printf("hello world");
    fflush(stdout);
    while(1);
    */


    /*6、当输入输出发生切换时,也会刷新行缓冲
    int num = 0;
    printf("请输入num的值:");
    scanf("%d", &num);
    */

    //7、当行缓冲区满了后,再往缓冲器区中放数据时,会刷新行缓冲
    
    for(int i=0; i<1999; i++)
    {
        printf("A");
    }
    while(1);


    return 0;
}

 6> 全缓存的刷新时机

1、程序结束后,会自动刷新全缓存

2、当文件指针关闭后,会刷新全缓存

3、当使用fflush函数刷新文件指针时,会刷新全缓存

4、当输入输出切换时,会刷新全缓存

5、当缓存区满了后,再放数据时,会刷新全缓存

#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件指针
    FILE *fp = NULL;
    if((fp = fopen("./tt.c", "w+")) == NULL)
    {
        perror("fopen error");
        return -1;
    }

    /*1、换行不会刷新全缓冲
    fputs("hello a\n", fp);
    while(1);
    */

    /*2、程序结束后,会刷新全缓冲
    fputs("hello a\n", fp);
    */

    /*3、关闭文件指针时,会刷新全缓冲
    fputs("hello a\n", fp);
    fclose(fp);
    while(1);
    */
    
    /*4、使用fflush刷新文件指针时,会刷新全缓冲
    fputs("hello a\n", fp);
    fflush(fp);
    while(1);
    */

    /*5、输入输出发生切换时,会刷新全缓冲
    fputs("hello a\n", fp);
    fgetc(fp);
    while(1);
    */

    //6、当缓冲区满了后,会刷新全缓冲
    for(int i=0; i<4097; i++)
    {
        fputc('A', fp);
    }
    while(1);








    return 0;
}

 2.9 标准IO函数也可以使用stdin、stdout、stderr

#include<myhead.h>

int main(int argc, const char *argv[])
{
    char buf[20] = "";

    printf("请输入一个字符串:");
    fgets(buf, sizeof(buf), stdin);    //从终端读取数据到buf中
    buf[strlen(buf)-1] = '\0';   //将回车换成 '\0'
    printf("buf = %s", buf);


    fputs("我是一个错误", stderr);   //向标准出错中写入数据

    return 0;
}

作业

1> 使用fgets统计给定文件的行号

#include<myhead.h>

int main(int argc,char const *argv[])
{
    if(argc!=2)
    {
        printf("input error!!!");
        return -1;
    }

    FILE *fp=NULL;
    if((fp=fopen(argv[1],"r"))==NULL)
    {
        perror("fopen error");
        return -1;
    }
    char buf[128]="";
    int sum=0;
    
    while(1)
    {
        bzero(buf,sizeof(buf));
        if(fgets(buf,sizeof(buf),fp)==NULL)
        {
            break;
        }
        sum++;
    }
    printf("%d",sum);
    fclose(fp);
    return 0;
}

 

2> 使用fgets、fputs完成两个文件的拷贝

#include<myhead.h>

int main(int argc, const char *argv[])
{
    //判断传入的文件个数
    if(argc != 3)
    {
        fputs("input file error\n", stderr);
        return -1;
    }

    //以只读的形式打开源文件
    FILE *sfp = NULL;
    if((sfp = fopen(argv[1], "r")) == NULL)
    {
        printf("fopen error %d,%s,%s\n", __LINE__, __func__, __FILE__);
        return -1;
    }

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

    //定义搬运工
    char buf[128] = "";
    while(1)
    {
        char *ptr = fgets(buf, sizeof(buf), sfp);   //从源文件中读取数据
        if(NULL==ptr)
        {
            break;
        }
        //将内容写入目标文件
        fputs(buf, dfp);
    }

    printf("拷贝成功\n");

    //关闭文件
    fclose(sfp);
    fclose(dfp);


    return 0;
}

2.10 有关系统时间的函数

#include <time.h>

        time_t time(time_t *tloc);

                功能:获取系统时间,从1970年1月1日0时0分0秒,到目前累计的秒数 7

                参数:用于接收的秒数

                返回值:秒数

                使用方式:

                                1、time_t sys_time = time(NULL);

                                2、time_t sys_time = 0; time(&sys_time);

        struct tm *localtime(const time_t *timep);

                功能:将time_t 秒数,转换为时间类型的结构体

                参数:time_t 类型的时间秒数

                返回值:时间结构体

                        struct tm {

                                int tm_sec; /* 秒数 */

                                int tm_min; /* 分钟 (0-59) */

                                int tm_hour; /* 小时 (0-23) */

                                int tm_mday; /* 月中天数 (1-31) */

                                int tm_mon; /* 月份+1 (0-11) */

                                int tm_year; /* 年 + 1900 */

                                int tm_wday; /* 周中天数 (0-6, Sunday = 0) */

                                int tm_yday; /* 年中天数 (0-365, 1 Jan = 0) */

                                int tm_isdst; /* Daylight saving time */

                        };

2.11 sprintf\snprintf:将格式串转换为字符串

int sprintf(char *str, const char *format, ...);

        功能:将格式串转换为字符串放入字符数组中

        参数1:存放格式串的字符数组

        参数2:格式串,可以包含格式控制符

        参数3:可变参数,根据参数2中的格式控制符个数确定

        返回值:成功返回转换的字符个数,失败返回-1       

int snprintf(char *str, size_t size, const char *format, ...);

        该函数比sprintf更加安全,因为多了一个size的限制

#include<myhead.h>
#include<time.h>
int main(int argc, const char *argv[])
{
    //定义变量存储秒数
    time_t sys_time = time(NULL);

    //将秒数转换为结构体
    struct tm *time_ptr = localtime(&sys_time);

    char buf[128] = "";      //存放转变后的字符串
    sprintf( buf,"%4d-%2d-%2d %2d:%2d:%2d\n",\
            time_ptr->tm_year+1900,\
            time_ptr->tm_mon+1,\
            time_ptr->tm_mday,\
            time_ptr->tm_hour,\
            time_ptr->tm_min,\
            time_ptr->tm_sec);

    printf("buf = %s\n", buf);

    return 0;
}

2.12 fprintf\fscanf:格式化读写函数

int fprintf(FILE *stream, const char *format, ...);

        功能:向指定文件中写入一个格式串

        参数1:文件指针

        参数2:格式串,可以包含格式控制符

        参数3:可变参数,根据参数2而定

        返回值:成功返回写入文件的字符实际个数,失败返回-1 \

int fscanf(FILE *stream, const char *format, ...);

功能:将从文件中读取一些数据,放入到程序中来

        参数1:文件指针

        参数2:格式控制串

        参数3:根据参数2而定

        返回值:成功返回读取数据的个数,失败返回EOF并置位错误码

#include<myhead.h>

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


    //定义要存储的数据
    int num = 18;          //整形数据

    printf("num = %d\n", num);       //向终端输出
    fprintf(fp ,"%d\n", num);       //向fp指向的文件中输出
    //关闭文件
    fclose(fp);

    //以只读的形式打开文件
    if((fp = fopen("./file.txt", "r")) == NULL)
    {
        perror("fopen error");
        return -1;
    }

    int key = 0;

    fscanf(fp, "%d", &key);      //从文件中读取一个数据


    printf("key = %d\n", key);

    //关闭文件
    fclose(fp);

    return 0;
}
#include<myhead.h>
//定义登录函数
int do_login()
{
    //定义变量接受登录账号和密码
    char login_name[20] = "";
    char login_pwd[20] = "";

    printf("请输入登录账号:");
    fgets(login_name, sizeof(login_name), stdin);
    login_name[strlen(login_name)-1] = '\0';    //将换行换成'\0'
    printf("请输入登录密码:");
    fgets(login_pwd, sizeof(login_pwd), stdin);
    login_pwd[strlen(login_pwd)-1] = 0;

    //打开文件读取数据
    FILE *fp = NULL;
    if((fp = fopen("./usr.txt", "r")) == NULL)
    {
        perror("fopen error");
        return -1;
    }

    //定义接收文件中的变量
    char name[20] = "";
    char pwd[20] = "";
    while(1)
    {
        //读取数据
        int res = fscanf(fp, "%s %s", name, pwd);
        if(res != 2)
        {
            break;
        }
        //判断是否登录成功
        if(strcmp(login_name,name)==0 && strcmp(login_pwd,pwd)==0)
        {
            printf("登录成功\n");
            fclose(fp);
            return 0;
        }
    }

    //关闭文件
    fclose(fp);
    return -1;

}





//定义注册函数
void do_regist()
{
    //定义变量接受注册账号和密码
    char regist_name[20] = "";
    char regist_pwd[20] = "";

    printf("请输入注册账号:");
    fgets(regist_name, sizeof(regist_name), stdin);
    regist_name[strlen(regist_name)-1] = '\0';    //将换行换成'\0'
    printf("请输入注册密码:");
    fgets(regist_pwd, sizeof(regist_pwd), stdin);
    regist_pwd[strlen(regist_pwd)-1] = 0;

    //将上述内容写入文件
    FILE *fp = NULL;
    if((fp = fopen("./usr.txt", "a")) == NULL)
    {
        perror("fopen error");
        return;
    }

    //将数据写入文件
    fprintf(fp, "%s %s\n", regist_name, regist_pwd);

    //关闭文件
    fclose(fp);
    printf("注册成功\n");
}




/************************主程序************************/
int main(int argc, const char *argv[])
{
    //制作菜单
    int menu = 0;        //选项
    while(1)
    {
        //调用函数执行shell指令
        system("clear");
        printf("\t\t======XXX 登录系统======\n");
        printf("\t\t======1、 登录======\n");
        printf("\t\t======2、 注册======\n");
        printf("\t\t======0、 退出======\n");
        printf("请输入功能:");
        fscanf(stdin, "%d", &menu);
        while(getchar() !='\n');       //吸收垃圾字符
        
        //对菜单多分支选择
        switch(menu)
        {
        case 1:
            {
                //do_login();
                int res = do_login();
                if(res == 0)
                {
                    //执行相关功能
                }else
                {
                    printf("登录失败\n");
                }
                
            }
            break;

        case 2:
            {
                do_regist();
            }
            break;

        case 0:goto END;
        default:fprintf(stdout, "您输入的功能有误,请重新输入!!\n");
        }

        printf("请输入任意键,按回车结束\n");
        while(getchar() != '\n');
    }

END:

    return 0;
}

 

2.13 fread\fwrite:模块化读写

#include <stdio.h>

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

        功能:对文件进行模块化读写操作

        参数1:要写入(存放数据)数据的起始地址 

        参数2:每一项的大小

        参数3:总的项数

        参数4:文件指针

        返回值:成返回读取的项数值,失败返回小于项数的值

        注意:fread并不能区分是文件结束了,还是因为出现错误了,需要使用feof或ferror来区分

//1> 使用fread、fwrite读写字符串 
#include<myhead.h>

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

    //准备字符串写入文件
    char buf[] = "hello a\n";
    fwrite(buf, 1, strlen(buf), fp);    //单字符写入
    fwrite(buf, strlen(buf), 1, fp);    //单字串写入

    //关闭文件
    fclose(fp);


    //以只读的形式打开文件
    if((fp = fopen("./usr.txt", "r")) == NULL)
    {
        perror("fopen error");
        return -1;
    }

    char rbuf[16] = "";     //要读取的内容
    int res = fread(rbuf, 1, sizeof(rbuf), fp);
    //printf("rbuf = %s\n", rbuf);               //?
    fwrite(rbuf, 1, res, stdout);        //向标准输出写入内容



    //关闭文件
    fclose(fp);

    return 0;
}

 

//2>    使用fread、fwrite读写整形数据
#include<myhead.h>

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

    //向文件中写入一个整数
    int num = 18;

    fwrite(&num, sizeof(num), 1, fp);



    //关闭文件
    fclose(fp);


    //以只读的形式打开文件
    if((fp = fopen("./usr.txt", "r")) == NULL)
    {
        perror("fopen error");
        return -1;
    }

    int key = 0;
    //读取数据
    fread(&key, sizeof(key), 1, fp);

    printf("key = %d\n", key);



    //关闭文件
    fclose(fp);

    return 0;
}
3>    使用fread、fwrite读写结构体
#include<myhead.h>
//定义结构体类型
typedef struct
{
    int numb;         //学号
    char name[20];       //姓名
    int age;             //年龄
    double score;        //成绩
}Stu;



/**********************主程序*********************/
int main(int argc, const char *argv[])
{
    //定义文件指针
    FILE *fp = NULL;
    if((fp = fopen("./usr.txt", "w")) == NULL)
    {
        perror("fopen error");
        return -1;
    }

    //定义三个学生
    Stu s[3] = {{1001, "大狗", 30, 98}, \
                {1002, "二狗", 25, 100}, \
                {1003, "天明", 23, 80}};

    //将这三名学生信息写入文件中
    fwrite(s, sizeof(Stu), 3, fp);

    //关闭文件
    fclose(fp);


    //以只读的形式打开文件
    if((fp = fopen("./usr.txt", "r")) == NULL)
    {
        perror("fopen error");
        return -1;
    }

    //定义接收取出的学生
    Stu temp;

    fread(&temp, sizeof(temp), 1, fp);
    fread(&temp, sizeof(temp), 1, fp);

    printf("学号:%d, 姓名:%s,年龄:%d,成绩%.2lf\n",\
            temp.numb, temp.name, temp.age, temp.score);


    //关闭文件
    fclose(fp);

    return 0;
}

2.14 关于文件光标的函数

#include <stdio.h>

int fseek(FILE *stream, long offset, int whence);

        功能:对文件光标进行偏移

        参数1:文件指针

        参数2:偏移量 >0:表示向后偏移 =0:表示不偏移 <0:表示向前偏移

        参数3:偏移位置

                                        SEEK_SET:文件开头

                                        SEEK_END:文件结束

                                        SEEK_CUR:当前位置

        返回值:成功返回0,失败返回-1并置位错误码

long ftell(FILE *stream);

        功能:返回文件当前位置(就是当前位置相对于文件起始位置的偏移字节数)

        参数:文件指针

        返回值:偏移字节数 常用:

                                        fseek(fp, 0, SEEK_END);

                                        ftell(fp); //文件大小

void rewind(FILE *stream);

        功能:将光标定位到开头 类似于 fseek(fp, 0, SEEK_SET)

        参数:文件指针

        返回值:无

#include<myhead.h>
//定义结构体类型
typedef struct
{
    int numb;         //学号
    char name[20];       //姓名
    int age;             //年龄
    double score;        //成绩
}Stu;



/**********************主程序*********************/
int main(int argc, const char *argv[])
{
    //定义文件指针
    FILE *fp = NULL;
    if((fp = fopen("./usr.txt", "w")) == NULL)
    {
        perror("fopen error");
        return -1;
    }

    //定义三个学生
    Stu s[3] = {{1001, "大狗", 30, 98}, \
                {1002, "二狗", 25, 100}, \
                {1003, "天明", 23, 80}};

    //将这三名学生信息写入文件中
    fwrite(s, sizeof(Stu), 3, fp);

    //关闭文件
    fclose(fp);


    //以只读的形式打开文件
    if((fp = fopen("./usr.txt", "r")) == NULL)
    {
        perror("fopen error");
        return -1;
    }

    //定义接收取出的学生
    Stu temp;

    fread(&temp, sizeof(temp), 1, fp);
    fread(&temp, sizeof(temp), 1, fp);
    //此时,光标已经定位在第三个学生的开头
    //目的:将光标定位到“大狗”前面
    //fseek(fp, 0, SEEK_SET);
    fseek(fp, -(sizeof(Stu)*2), SEEK_CUR);
    fread(&temp, sizeof(temp), 1, fp);

    printf("学号:%d, 姓名:%s,年龄:%d,成绩%.2lf\n",\
            temp.numb, temp.name, temp.age, temp.score);


    //关闭文件
    fclose(fp);

    return 0;
}

 

//使用标准IO完成对图像的操作
#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件指针
    FILE *fp = NULL;
    if((fp = fopen("./gg.bmp", "r+")) == NULL)
    {
        perror("fopen error");
        return -1;
    }

    //获取文件大小
    int img_size = 0;

    //将文件光标偏移2个字节
    fseek(fp, 2, SEEK_SET);

    //读取4字节的内容
    fread(&img_size, sizeof(img_size), 1, fp);

    printf("size = %d\n", img_size);        //图像大小

    //从头向后偏移54字节后,就是图像数据
    fseek(fp, 54, SEEK_SET);

    //定义一个像素
    unsigned char color[3] = {0, 0, 255};     //正红色
    for(int i=0; i<960/2; i++)          //外行
    {
        for(int j=0;j<1280; j++)       //内列
        {
            fwrite(color, sizeof(color), 1, fp);
        }
    }
    



    //关闭文件
    fclose(fp);

    return 0;
}

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值