标准IO
#include <stdio.h>
//stdio:标准的输入输出 标准IO
printf/scanf//就是标准IO的接口
文件IO和标准IO
文件IO:是系统调用
标准IO:是库函数
系统调用和库函数
- 系统调用
- 从用户空间进入内核空间的一次过程就是一次系统调用
- 系统调用没有缓冲区,效率比较低。系统调用的可移植性比较差
- 库函数
- 库函数 = 缓冲区 + 系统调用
- 效率比系统调用的高,可移植性高
标准IO和文件IO的常用接口
- 文件IO:
open read write close ...
- 标准IO:
printf scanf fopen fread fwrite fclose
标准IO
-
FILE指针
-
FILE是一个结构体,fopen函数的返回值是FILE的指针,这个FILE指针记录了打开文件的所有的信息以后在操作文件的时候就通过FILE完成
-
typedef struct _IO_FILE FILE;
-
struct _IO_FILE { char* _IO_buf_base; //缓冲区的起始地址 char* _IO_buf_end; //缓冲区的结束地址 ... }
-
在一个正在运行的程序中默认已经产生了三个FILE指针
- stdin:标准输入
- stdout:标准输出
- stderr:标准出错
-
标准IO常用函数
fopen函数
-
#include <stdio.h> FILE *fopen(const char *pathname, const char *mode);
-
功能:使用标准IO接口打开文件,在用户空间申请一片缓冲区
-
参数:
-
文件的路径和名字
-
打开文件的方式
-
参数 功能 r 只读方式打开文件,光标定位到文件开头 r+ 以读写方式打开文件,光标定位到文件开头 w 有则清空,无则创建,以只写的方式打开文件,光标定位到文件开头 w+ 有则清空,无则创建,以读写的方式打开文件,光标定位到文件开头 a 无则创建,以追加的方式打开文件,光标定位到文件末尾 a+ 无则创建,以读和追加的方式打开文件,读则开头,尾则结尾 -
返回值
- 成功:返回FILE*类型
- 失败:返回NULL,更新errno(错误码)
- 不同的错误,errno不同
-
-
perrop
-
功能:根据errno打印对应的错误信息
-
原型:
-
#include <stdio.h> void perror(const char *s);
-
-
参数:
char *s
用于提示的字符串- 例如:函数名 error:
strerror
-
功能:将给定的错误码,变成对应的字符串信息
-
原型
-
#include <string.h> char *strerror(int errnum);
-
-
参数:错误码
-
返回值
- 成功:指定错误码信息
- 失败:Unknown error nnn
errno错误码头文件
#include <errno.h>
fclose
-
功能:关闭文件,同时释放缓冲区等其他资源;(关闭文件描述符)
-
原型:
-
#include <stdio.h> int fclose(FILE *stream);
-
-
参数:
FILE *stream
:指定关闭哪个文件 -
返回值:
- 成功:返回0
- 失败:返回EOF,更新errno
#define EOF (-1)
fprintf
-
功能:将数据输出到指定的文件中
-
原型
-
#include <stdio.h> int printf(const char *format, ...); int fprintf(FILE *stream, const char *format, ...);
-
-
参数:
FILE *stream
:指定要输出到哪个文件中,填上对应的流指针char *format
:格式化字符串,其中可以填字符,占位符,转义符等等- …:不定参数,不定数据类型,不定数据个数
-
返回值:
- 成功:返回成功输出的字符个数
- 失败:返回负数
fscanf
-
功能:从指定的文件中以格式化获取数据,%d %s %f不会获取空格和换行,%c可以获取空格和换行字符
-
原型:
-
#include <stdio.h> int scanf(const char *format, ...); int fscanf(FILE *stream, const char *format, ...);
-
-
参数:
FILE *stream
:指定要从哪个文件中读取数据;char *format
:格式化输出字符串:占位符- …:不定参数,不定数据类型,不定数据个数
-
返回值:
-
成功:返回成功读取到的数据个数
-
=EOF:不更新errno,文件读取到结尾; errno == 0;
-
=EOF:更新errno,函数运行失败
-
fputc
-
功能:将单个字符输出到指定的文件中
-
原型
-
#include <stdio.h> int fputs(int c, FILE *stream); int putchar(int c);
-
-
参数:
FILE *stream
:指定要将字符输出到哪个文件中,填上对应的流指针int c
:指定要输出的字符对应的整型形式,或者字符形式
-
返回值:
- 成功:返回成功输出的字符对应的整型形式
- 失败:EOF
fgetc
-
功能:从指定的文件中读取单个字符
-
原型:
-
#include <stdio.h> int fgetc(FILE *stream); int getchar(void);
-
-
参数:
FILE *stream
:指定要从哪个文件中读取数据
-
返回值:
- 成功:返回成功读取到的字符对应的整型形式
- 失败:EOF
- 读取完毕:EOF
fseek
如果偏移量在文件开头的时候,不允许继续向前偏移
如果偏移量在文件结尾的时候,允许继续往后偏移。且偏移后写入数据,前面偏移部分内容会自动补0对应的字符形式就是^@
-
功能:修改文件偏移量(可以理解成是修改光标所在位置)
-
原型:
-
#include <stdio.h> int fseek(FILE *stream, long offset, int whence);
-
-
参数:
-
FILE *stream
:指定要修改哪个文件的偏移量 -
long offset
:偏移量,新的位置尾whence加上offset参数 若想往结尾偏移,则填正数
若想往开头便宜,则填负数
-
int whence
:SEEK_SET//文件开头
SEEL_CUR//文件当前位置
SEEK_END//文件结尾位置
-
-
返回值:
- 成功:返回0
- 失败:返回-1,更新errno
rewind
-
功能:将文件偏移量修改到文件开头
-
原型:
-
#include <stdio.h> void rewind(FILE *stream); void fseek(FILE *stream, 0, iSEEK_SET);
-
ftell
-
功能:获取文件当前位置距离文件开头的偏移量
-
原型:
-
#include <stdio.h> long ftell(FILE *stream);
-
-
参数:
FILE *strem
:指定要获取哪个文件的偏移量
-
返回值:
- 成功:返回文件当前位置距离开头的偏移量
- 失败:返回-1,更新errno
//获取文件大小
fseek(fp, 0, SEEK_END);
size = ftell(fp);
printf("size = %ld\n", size);
fputs
-
功能:将字符串输出到指定的文件中,与
puts
的区别:打印的字符串不会自动补’\n’ -
原型:
-
#include <stdio.h> int fputs(const char *s, FILE *stream); int puts(const char *s); puts(str);
-
-
参数:
const char *s
:指定要输出的字符串的首地址FILE *stream
:指定要输出到哪个文件夹中
-
返回值:
- 成功:返回非负数(字符串长度)
- 失败:EOF
fgets
-
功能:从指定的文件中获取字符串
- fgets读取完’\n’字符后,会停止读取,遇到EOF即文件结尾,也会停止读取
- fgets读取数据完毕后,会在读取的有效字符后面自动补充’\0’字符,
sizeof()
获取长度需要减一
-
原型:
-
#include <stdio.h> char *fgets(char *s, int size, FILE *stream);
-
-
参数:
char *s
:存储获取到的字符串int size
:指定一次最多获取多少个:size-1个FILE *stream
;
-
返回值:
- 成功:返回存储获取到的数据的内容数组首地址
- 失败或者读取到文件结尾,返回NULL
fwrite
-
功能:将数据的二进制形式写入到文件中
- 二进制形式:会将数据拆分成一个一个的字节,并将字节转换成字符形式,写入到文件中
-
原型:
-
#include <stdio.h> size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
-
-
参数:
void *ptr
:任意类型指针,该指针的内存空间中存放着要输出的数据首地址,它可以输出任意类型数据size_t size
:每个数据的大小,以字节为单位size_t nmemb
:指定要输出多少个数据FILE *stream
-
返回值:
- 成功:返回指定要输出的数据个数
- 失败:小于指定要输出的个数,或者等于0
int arr[3] = {1, 2, 3}; fwrite(arr, 4, 3, fp); ==>返回3 fwrite(arr, sizeof(arr), 1, fp); ==>返回1
fread
-
功能:将数据的二进制形式从文件中读取出来,并转换成指定类型数据
-
原型:
-
#include <stdio.h> size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
-
-
参数:
void *ptr
:任意类型指针,该指针指向的内存空间中,存储读取到的数据。它可以读取任意类型的数据size_t size
:每个数据的大小,以字节为单位size_t nmemb
:指定要读多少个数据FILE *stream
-
返回值:
- 成功:返回指定要读取的数据个数
- 失败:小于指定要读取的数据个数,或者等于0
-
使用建议
- 因为每次读取的数据不满足函数参数需要读取的数据就会读取失败
- 所以建议每次读取一个数据,因为任何数据都是1的倍数,或者一次读取一个字节,这样就可以避免读取失败
fread(arr, sizeof(int), 1, fp);
一次读取1个数据,每个数据大小为4字节fread(arr,1,sizeof(arr), fp);
一次读取sizeof(arr)个数据,每个数据大小为1字节
sprintf
-
功能
- 在给定的字符数组str中,存入一个格式串
-
原型
-
#include <stdio.h> int sprintf(char *str, const char *format, ...);
-
-
参数
char *str
:字符数组容器const char *format
:格式串,可以包含格式控制符...
:可变参数,参数个数由格式串中的格式控制符决定
-
返回值
- 成功:字符个数
- 失败:负数
snprintf
-
功能
-
原型
-
#include <stdio.h> int snprintf(char *str, size_t size, const char *format, ...);
-
-
参数
char *str
:字符数组容器size_t size
:容器大小const char *format
:格式串...
:可变参数,参数个数由格式串中的格式控制符决定
-
返回值
- 成功:字符个数
- 失败:负数
时间相关的函数
time
-
功能:获取1970-1-1至今的秒数
-
原型
-
#include <time.h> time_t time(time_t *tloc);
-
-
参数:
time_t *tloc
:该指针指向的内存空间中,会存储获取到的秒数,也可以写NULL,代表参数中不存储秒数
-
返回值:
- 成功:返回获取到的秒数
- 失败:返回-1,更新errno
localtime
-
功能:将1970-1-1至今的秒数转换成日历格式
-
原型:
-
#include <time.h> struct tm *localtime(const time_t *timep);
-
-
参数:
time_t *timep
:指定要转换成日历格式的秒数
-
返回值:
- 成功:返回结构体指针
- 失败:返回NULL,更新errno;
//vi -t tm
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) */ 月=tm_mon+1
int tm_year; /* Year - 1900 */ 年=tm_year+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 */
};
缓冲区
全缓冲
-
操作对象:手动用fopen打开文件后,获取到的缓冲区均为全缓冲
-
大小:4096byte = 4k
fputc('a', fp);//先往缓冲区中存放一个字符'a' printf("%ld\n",fp->_IO_buf_end - fp_>_IO_buf_base);
-
刷新缓冲区机制
- 缓冲区满
- 用fflush函数强制刷新
#include <stdio.h> int fflush(FILE *stream);
-
关闭流指针
fclose(fp)
-
主函数调用return
-
调用exit退出程序
-
无论在程序什么位置调用
exit()
,都会导致整个程序退出 -
#include <stdio.h> void exit(int status);
-
参数:
int status
:整型参数
-
行缓冲
-
操作对象:
- 标准输入流指针
FILE *stdin
- 标准输出流指针
FILE *stdout
- 标准输入流指针
-
大小:1024bytes = 1k
-
int num; scanf("%d", &num); printf("%ld\n", stdin->_IO_buf_end - stdin->_IO_buf_base);
-
刷新缓冲区的机制
-
缓冲区满
-
用fflush函数强制刷新
-
#include <stdio.h> int fflush(FILE *stream);
-
关闭流指针
fclose(stdout);
基本不使用 -
主函数调用
return
-
调用
exit
退出程序
-
无缓冲
- 操作对象:标准错误输出流指针
FILE *stderr
- 大小:0
peror
函数默认调用的就是stderr流指针
一般用于打印错误信息,防止错误信息在缓冲区中没有刷新,直接被销毁。
实时性比stdout
高,但是效率会比stdout
低
概念
- 文件IO是没有缓冲区的,只有标准IO有缓冲区
- 文件IO函数是由操作系统提供的,与操作系统绑定,又称之为系统调用
- 文件IO函数是通过文件描述符来维护一个文件
文件描述符
-
当我们要去操作一个文件的时候首先要打开一个文件。尝试打开一个文件的时候,系统会自动给这个文件分配一个编号,这个编号就是文件描述符,用文件描述符来维护描述这个文件
-
标准IO是对文件IO的二次封装,最终标准IO依然会去调用文件IO,所以FILE结构体中会有文件描述符成员
_fileno
- 在文件IO的基础上,封装了一个缓冲区,同时将文件描述符也一起封装到了FILE结构体中
-
文件描述符的本质
-
文件描述符的本质是数组下标,该数组的容量默认为1024,范围是[0,1023]
-
文件描述符是有上限的,所以在不使用的情况下,需要关闭
-
获取文件描述符的规则是,从小向大,依次获取,直到找到一个没有被使用的元素,返回该元素的下标。
-
其中有三个特殊的文件描述符,分别为0,1,2
特殊的流指针(FILE*) 特殊文件描述符 结构体位置 FILE *stdin 0 stdin->_fileno FILE *stdout 1 stdout->_fileno FILE *stderr 2 stderr->_fileno
-
-
getdtablesize(void)
获取一个进程最多能打开几个文件描述符
文件IO函数
open
-
功能:打开一个文件
-
原型:
-
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags, ...); int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode);
-
-
参数:
-
char *pathname
:指定要打开的文件路径以及名字 -
int flags
:打开方式-
参数 功能 O_RDONLY 只读 O_WRONLY 只写 O_RDWR 读写 -
以上三种,必须且只能包含一个
参数 功能 O_APPEND 追加的方式 O_CREAT 如果文件不存在则创建 O_TRUNC 如果文件存在,则清空 -
如果需要多个选项,可以用按位或连接
-
-
mode_t mode
:在文件创建时,指定文件的权限使用 当flags中指定了O_CREAT或者O_TMPFILE的时候,mode参数必须填写
当flags没有指定上述两种选项,则mode参数会被忽略
-
-
返回值:
- 成功:返回新的文件描述符
- 失败:返回-1,更新errno
-
fopen的打开方式,在open中组合
-
fopen open r O_RDONLY w O_WRONLY |O_CREAT|O_TRUNC a O_WRONLY|O_CREAT|O_APPEND r+ O_RDWR w+ O_RDWR|O_CREAT|O_TRUNC a+ O_RDWR|O_CREAT|O_ARREND
-
close
-
功能:关闭文件,释放文件描述符对应的该空间
-
原型
-
#include <unistd.h> int close(int fd);
-
-
参数:
int fd
:指定要关闭的文件
-
返回值:
- 成功:0
- 失败:-1,更新errno
write
-
功能:将数据写入文件中
-
原型:
-
#include <unistd.h> ssize_t write(int fd, const void *buf, size_t count);
-
-
参数
int fd
:指定要写入哪个文件中,填对应的文件描述符void *buf
:指定要输出的数据首地址,可以输出任意类型的数据size_t count
:指定要输出的数据大小,以字节为单位
-
返回值:
- 成功:返回成功输出的字节数
- 失败:返回-1,更新errno
read
-
功能:从指定的文件中读取数据
-
原型:
-
#include <unistd.h> ssize_t read(int fd, void *buf, size_t count);
-
-
参数
int fd
:指定要读取哪个文件,填对应的文件描述符void *buf
:存储读取到的数据size_t count
:指定要读取多少个字节
-
返回值
- >0:成功,返回成功读取到的字节数
- =0:读取到文件结尾
- -1:失败,返回-1,更新errno
-
read不会自动补充\0字符
-
read函数遇到\n不会停止读取
lseek
-
功能:修改文件偏移量
-
原型:
-
#include <sys/types.h> #include <unistd.h> off_t lseek(int fd, off_t offset, int whence);
-
-
参数:
int fd
:指定要修改哪个文件偏移量,填上对应的文件描述符off_t offset
:偏移量,新的位置为whence加上offset参数- 往结尾偏移,填正数
- 往开头偏移,填负数
int whence
:SEEK_SET
,文件开头位置SEEK_CUR
,文件当前位置SEEK_END
,文件结尾位置
-
返回值
- 成功:返回文件偏移后的当前位置,距离文件开头的偏移量
- 失败,返回
(off_t)-1
,更新errno
-
lseek(fd, 0, SEEK_END);
,返回值为文件的大小
将系统时间以 年-月-日 时:分:秒 的格式一秒一次的写入指定文件中并记录行数
int get_fileline(FILE *fp_r)
{
int line = 0;
char buf[32] = " ";
while(fgets(buf, sizeof(buf), fp_r) != NULL)
{
if(buf[strlen(buf)-1] == '\n')
{
line++;
}
}
return line;
}
void time_to_file(FILE *fp_w)
{
//定义一个接收系统时间的变量
time_t t;
//每次执行程序都能获取文件行数
int line = get_fileline(fp_w);
//定义一个localtime结构体指针
struct tm* info = NULL;
while(1)
{
//获取系统时间
t = time(NULL);
info = localtime(&t);
//写入文件
fprintf(fp_w, "[%d] %d-%02d-%02d %02d:%02d:%02d\n",\
++line,\
info->tm_year+1900,\
info->tm_mon+1,\
info->tm_mday,\
info->tm_hour,\
info->tm_min,\
info->tm_sec);
//刷新缓存区
fflush(fp_w);
//休眠一秒后再写入
sleep(1);
}
}
使用fread和fwrite实现文件拷贝功能
void copy_file(FILE* f_r, FILE* f_w)
{
//分次读写,一次32字节
char buf[32] = "";
size_t res = 0;
while(1)
{
//每次读一个字节,每次读sizeof(buf)-1,留一个位置给'\0'
if((res = fread(buf, 1, sizeof(buf)-1, f_r)) == 0)
break;
//字符串结尾补0
buf[res] = 0;
fwrite(buf, 1, res, f_w);
}
puts("copy complete");
}