标准IO
什么是标准IO
概念
标准IO:是在C库中定义的一组专门用于输入输出的函数。
特点
(1)通过缓冲机制减少系统调用,提高效率。
系统调用:是内核向上提供的一组接口。
(2)围绕流进行操作,流用FILE *来描述
(3)标准IO默认打开三个流,stdin(标准输入) stdout(标准输出) stderr(标准错误)。
缓存区
(1)全缓存:和文件相关的
(2)行缓存:和终端相关的
刷新标准输出缓存区的条件:
●\n
●程序正常退出
●缓存区满
●强制刷新:fflush(NULL)
#include <stdio.h>
int main(int argc, char const *argv[])
{
//printf("hello world\n"); //\n不光可以换行,还可以刷新标准输出的缓冲区
printf("hello world");
fflush(NULL); //强制刷新缓存区
while (1);
return 0; //正常退出刷新刷存区
}
(3)不缓存:没有缓存,标准错误。
综上:当我们每次要打印数据时,并不是将数据直接发送给标准输出设备,也就是并直接发送给显示器,而是将要打印的数据先存放到缓存区,当缓冲存数据满时,或者遇到\n,或者程序结束时,或者手动刷新缓存区时,缓冲区才会把数据传输到标准输出设备中,也就是显示器中进行输出。
计算标准输出的缓存区大小 KB
方法一:利用循环打印展示
方法二:利用结构体指针stdout
#include <stdio.h>
int main(int argc, char const *argv[])
{
printf("buf:"); //不进行输出的话缓存去不会开辟,所以需要开辟缓存区的话需要先调用标准输出函数。
printf("%d\n",stdout->_IO_buf_end - stdout->_IO_buf_base);
return 0;
}
函数接口
打开文件 fopen
FILE *fopen(const char *path, const char *mode);
功能:打开文件
参数:
path:打开的文件路径
mode:打开的方式
r:只读,当文件不存在时报错,文件流定位到文件开头
r+:可读可写,当文件不存在时报错,文件流定位到文件开头
w:只写,文件不存在创建,存在则清空
w+:可读可写,文件不存在创建,存在则清空
a:追加(在末尾写),文件不存在创建,存在追加,文件流定位到文件末尾
a+:读和追加,文件不存在创建,存在追加,读文件流定位到文件开头,写文件流定位到文件末尾
注:当a+的方式打开文件时,写只能在末尾进行追加,定位操作是无法改变写的位置,但是可以改变读的位置
返回值:
成功:文件流
失败:NULL,并且会设置错误码
关闭文件
int fclose(FILE* stream);
功能:关闭文件
参数:stream:文件流
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp;
//1.打开文件
//fp = fopen("test.txt","w");
fp = fopen("test.txt", "r");
if (NULL == fp)
{
perror("fopen err"); //如果以r方式打开,没有这个文件会报错,打印错误信息。
return -1;
}
printf("fopen success\n");
//2. 关闭文件
fclose(fp);
return 0;
}
文件读写操作
每次读写一个字符:fgetc()、fputc()
每次读一个字符fgetc()
int fgetc(FILE * stream);
功能:从文件中读取一个字符,并将当前文件指针位置向后移动一个字符。
参数:stream:文件流
返回值:成功:读到的字符
失败或读到文件末尾:EOF(-1)
每次写一个字符fputc()
int fputc(int c, FILE * stream);
功能:向文件中写入一个字符, 成功写入后文件指针会自动向后移动一个字节位置。
参数:c:要写的字符
stream:文件流
返回值:成功:写的字符的ASCII
失败:EOF(-1)
(1)针对文件
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp;
//1.打开文件
fp = fopen("test.txt", "r+");
//fp = fopen("test.txt", "r");
if (NULL == fp)
{
perror("fopen err"); //如果以r方式打开,没有这个文件会报错,打印错误信息。
return -1;
}
printf("fopen success\n");
//对文件读写操作
char ch = fgetc(fp);
printf("%c %d\n", ch, ch);//h 104
ch = fgetc(fp);
printf("%c %d\n", ch, ch);//e 101
fputc('a', fp);
fputc(98, fp);
//2. 关闭文件
fclose(fp);
return 0;
}
(2)针对终端
#include <stdio.h>
int main(int argc, char const *argv[])
{
char ch = fgetc(stdin);
//printf("%d %c\n", ch, ch);
fputc(ch, stdout);
ch = fgetc(stdin);
//printf("%d %c\n", ch, ch);
fputc(ch, stdout);
return 0;
}
feof和ferror
int feof(FILE * stream);
功能:判断文件有没有到结尾,也就是当前所在位置后面还有没有字符。
返回:如果到达文件末尾,返回非零值。如果后面还有字符则返回0。
例子:
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp;
//1.打开文件
fp = fopen("test.txt", "r+");
if (NULL == fp)
{
perror("fopen err");
return -1;
}
printf("fopen success\n");
char ch = fgetc(fp);
printf("%c %d\n", ch, ch);
ch = fgetc(fp);
printf("%c %d\n", ch, ch);
if (feof(fp))
{
printf("end1\n");
//不会打印出来,因为此时末尾还有一个EOF。
}
ch = fgetc(fp);
printf("%c %d\n", ch, ch);
if (feof(fp))
{
printf("end2\n");
//会打印出来,因为此时已经到末尾了
}
//2. 关闭文件
fclose(fp);
return 0;
}
int ferror(FILE * stream);
功能:检测文件有没有出错
返回:文件出错,返回非零值。如果没有出错,返回0。
例子;
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp;
//1.打开文件
fp = fopen("test.txt", "w");
if (NULL == fp)
{
perror("fopen err");
return -1;
}
printf("fopen success\n");
char ch = fgetc(fp); //因为权限是只写,所以读操作有错误。
if (ferror(fp))
{
printf("err!\n");
return -1;
}
fclose(fp);
return 0;
}
每次一串字符的读写 fgets() 和 fputs()
char * fgets(char *s, int size, FILE * stream);
功能:从文件中每次读取一行字符串
参数: s:存放字符串的地址
size:期望一次读取的字符个数
stream:文件流
返回值:成功:s的地址
失败或读到文件末尾:NULL
特性: 每次实际读取的字符个数为size-1个,会在末尾自动添加\0
每次读一行,遇到\n或者到达文件末尾后不再继续读下一行
并把它存储在s所指向的字符串内。
int fputs(const char *s, FILE * stream);
功能:向文件中写字符串
参数:s:要写的内容
stream:文件流
返回值:成功:非负整数
失败:EOF
(1)针对终端
(2)针对文件
二进制读写fread()和fwrite()
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从文件流读取多个元素(将二进制数据从文件读出)
参数: ptr :是一个指针,是存放数据的存储空间的起始地址,用来存放读取元素
size :元素大小 sizeof(元素数据类型)
nmemb :读取元素的个数
stream :要读取的文件流
返回值:成功:读取的元素的个数
读到文件尾或失败: 0
size_t fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream);
功能:将二进制数据写入文件
参数: ptr :是一个指针,保存要输出数据的空间的地址。
size :要写入的字节数 sizeof(数据类型)
nmemb : 要进行写入元素的个数
strem: 目标文件流指针
返回值:成功:写的元素个数
失败 :-1
(1)针对终端
char buf[32]="";
fread(buf,1,10,stdin);
printf("%s\n",buf);
fwrite(buf,1,10,stdout);
(2)针对文件
#include <stdio.h>
int main(int argc, char const *argv[])
{
float arr[3] = {1.2, 3.4, 5.6};
float data[3] = {};
FILE *fp = fopen(argv[1], "r+");
if (NULL == fp)
{
perror("fopen err");
return -1;
}
fwrite(arr, sizeof(float), 3, fp);
//定位操作
rewind(fp); //将位置指针定位到文件开头,不然是接着上一步写的位置继续读的,由于是末尾所以读不到东西。
fread(data, 4, 3, fp);
printf("%f %f %f\n", data[0], data[1], data[2]);
return 0;
}
文件定位到开头:rewind(fp);
重定向流到指定文件freopen
FILE * freopen(const char *pathname, const char *mode, FILE* fp);
功能:将指定的文件流重定向到打开的文件中
参数:path:文件路径
mode:打开文件的方式(同fopen)
fp:文件流指针
返回值:成功:返回文件流指针
失败:NULL
#include <stdio.h>
int main(int argc, char const *argv[])
{
printf("hello\n");
//将标准输出重定向到打开的test.txt中
freopen("test.txt", "w+", stdout);
printf("world\n");
//将标准输出流重定向到终端文件/dev/tty
freopen("/dev/tty", "r+", stdout);
printf("66666\n");
return 0;
}
文件定位操作
void rewind(FILE *stream);
功能:将文件位置指针定位到起始位置
int fseek(FILE *stream, long offset, int whence);
功能:文件的定位操作
参数:stream:文件流
offset:偏移量:正数表示向后文件尾部偏移,负数表示向文件开头偏移
whence:相对位置:
SEEK_SET:相对于文件开头
SEEK_CUR:相对于文件当前位置
SEEK_END:相对于文件末尾
返回值:成功:0
失败:-1
注:当打开文件的方式为a或a+时,fseek不起作用
补充:其中SEEK_SET,SEEK_CUR和SEEK_END和依次为0,1和2.
例子:
把fp指针移动到离文件开头100字节处:fseek(fp,100,0);
把fp指针移动到离文件当前位置100字节处:fseek(fp,100,1);
把fp指针退回到离文件结尾100字节处: fseek(fp,-100,2);
long ftell(FILE *stream);
功能:获取当前的文件位置
参数:要检测的文件流
返回值:成功:当前的文件位置,出错:-1
文件IO
什么是文件IO
概念
又称为系统IO,是系统调用,是操作系统提供的函数接口。posix中定义的一组用于输入输出的函数。POSIX接口 (英语:Portable Operating System Interface)可移植操作系统接口 。
特点
(1)没有缓冲机制,每次调用都会引起系统调用。
(2)围绕文件描述符进行操作,非负整数(>=0),依次分配
(3)文件IO默认打开了三个文件描述符,分别是0(标准输入)、1(标准输出)、2(标准错误)
(4)操作除了目录(d类型)文件的任意其他类型文件:b c - l s p
问题:打开三个文件,描述符分别是:3 4 5
关闭4以后,重新打开这个文件,文件描述符是几?
答:还是4
问题:一个进程的文件描述符最大到几?最多能打开多少个文件描述符?最多能打开多少个文件
答: 一个进程的文件描述符最大到1023(0-1023),最多能打开1024个文件描述符,最多能打开1024-3=1021个文件
操作
打开文件:open
关闭文件:close
读写操作:read、write
定位操作:lseek
函数接口
打开文件open()
int open(const char *pathname, int flags);
功能:打开文件
参数:pathname:文件路径名
flags:打开文件的方式
O_RDONLY:只读
O_WRONLY:只写
O_RDWR:可读可写
O_CREAT:不存在创建
O_TRUNC:存在清空
O_APPEND:追加
返回值:成功:文件描述符
失败:-1
例如:O_WRONLY|O_CREAT|O_TRUNC ==> "w" 可写,不能存在时创建,存在则清空
当第二个参数中有O_CREAT选项时,需要给open函数传递第三个参数,指定创建文件的权限
int open(const char *pathname, int flags, mode_t mode);
最后权限=创建出来的文件指定权限值&(~umask)
例如:指定权限为0666(8进制)
最终权限为0666&(~umask) = 0666&0775 = 0664
例子:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char const *argv[])
{
int fd;
//fd = open("./a.c", O_RDONLY);
fd = open("./a.c",O_WRONLY|O_CREAT|O_TRUNC,0777);
if (fd < 0)
{
perror("open err");
return -1;
}
printf("fd:%d\n", fd);
return 0;
}
文件IO和标准IO的打开方式的对应关系
关闭文件
int close(int fd);
功能:关闭文件
参数:fd:文件描述符
读写文件
读文件
ssize_t read(int fd, void *buf, size_t count);
功能:从一个已打开的可读文件中读取数据
参数: fd 文件描述符
buf 存放位置
count 期望的个数
返回值:成功:实际读到的个数(小于期望值说明实际没这么多)
返回0:表示读到文件结尾
返回-1:表示出错,并设置errno号
fgetc -> 失败和末尾:EOF
fgets ->失败和末尾:NULL
fread ->失败和末尾:0
read ->失败:-1 ;末尾:0
写文件
ssize_t write(int fd, const void *buf, size_t count);
功能:向指定文件描述符中,写入 count个字节的数据。
参数:fd 文件描述符
buf 要写的内容
count 期望写入字节数
返回值:成功:实际写入数据的个数
失败 : -1
//返回值小于期望值是错误行为,可能磁盘满了无法再写。
文件定位操作
off_t lseek(int fd, off_t offset, int whence);
功能:设定文件的偏移位置
参数:fd:文件描述符
offset: 偏移量
正数:向文件结尾位置移动
负数:向文件开始位置
whence: 相对位置
SEEK_SET 开始位置
SEEK_CUR 当前位置
SEEK_END 结尾位置
补充:和fseek一样其中SEEK_SET,SEEK_CUR和SEEK_END和依次为0,1和2.
返回值:成功:文件的当前位置
失败:-1
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
char buf[32] = "";
int fd;
fd = open("./a.c", O_RDWR);
if (fd < 0)
{
perror("open err");
return -1;
}
lseek(fd, 10, SEEK_END);
write(fd, "k", 1);
off_t off = lseek(fd, 0, SEEK_END);
printf("%ld\n", off); //利用结尾偏移求出长度
return 0;
}
标准IO和文件IO总结;