一丶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;
}