IO即是input和output, 是程序与外部设备进行信息交换的过程。
1. IO分为标准IO和文件IO
标准IO:调用封装好的相关库函数,来实现数据的输入输出
文件IO:调用系统(内核)提供的相关函数,来实现数据的输入输出
标准IO和文件IO的区别:
1、标准IO属于库函数,文件IO属于系统调用
2、标准IO操作的是文件指针,文件IO操作的是文件描述符
3、标准IO有缓冲器,文件IO没有缓冲区
左图:
描述了一个传统计算机系统的架构,具体来说:
- 在最上层,有多个应用程序(app),它们位于用户空间。
- 应用程序通过库函数(如printf)与系统进行交互。
- 库函数调用系统调用(write),将数据从用户空间传递给内核空间。
- 内核空间包含驱动层,负责处理硬件相关的任务。
- 最底层是硬件平台,例如磁盘、终端和网络接口等。
右图:
是GNU/Linux操作系统的架构,具体来说:
- 用户空间包含了应用程序(app)和GNU C库(glibc)。glibc提供了系统调用的封装,使得应用程序能够方便地访问系统资源。
- 系统调用(sys_write)是用户空间与内核空间之间的重要桥梁,它允许应用程序请求内核执行某些特权操作。
- 内核空间包括Kernel层和架构依赖内核代码。Kernel层提供了一些基本的服务,而架构依赖内核代码则针对特定硬件平台进行了优化。
IO分为阻塞IO和非阻塞IO;
常用的IO接口函数:
标准IO:fprint、fscanf、fputc、fgetc、fputs、fgets、fread、 fwrite、fopen、fclose、fseek、ftell、rewind...
文件IO:open、close、read、write、seek...
<stdio.h>提供的IO函数
FILE结构体:
struct_IO_FILE
{
char* _IO_buf_base; /* 缓冲区开始地址 */
char* _IO_buf_end; /* 缓冲区结束地址 */
int _fileno; //用于系统调用的文件描述符
};
当一个程序启动后,系统会自动打开三个文件指针:
stdin:标准输入文件指针 scanf、getchar、gets
stdout:标准输出文件指针 printf、putchar、puts
stderr:标准出错文件指针 perror
以上三个文件指针,都是针对于终端文件操作而言的
fopen()
用法:FILE *fopen(const char *pathname, const char *mode);
DESCRIPTION
The fopen() function opens the file whose name is the string pointed to by pathname and
associates a stream with it.
┌─────────────┬───────────────────────────────┐
│fopen() mode │ open() flags │
├─────────────┼───────────────────────────────┤
│ 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_APPEND │
└─────────────┴───────────────────────────────┘
r 以只读的形式打开文件,文件光标定位在开头.如果文件不存在,则报错
r+ 以读写的形式打开文件,文件光标定位在开头.如果文件不存在,则报错
w 以只写的形式打开文件,如果文件存在,则清空文件内容,如果文件不存在,则创建该文件,文件光标定位在开头.
w+ 以读写的形式打开文件,如果文件存在,则清空文件内容,如果文件不存在,则创建该文件,文件光标定位在开头.
a 以追加的形式打开文件,如果文件不存在则创建文件,文件光标定位在结尾
a+ 以读或者追加的形式打开文件,如果文件不存在,则创建文件,如果第一次是读数据,则光标定位在开头,否则定位在结尾 返回值:成功调用返回打开的文件地址,失败返回NULL,并置位错误码
fclose()
用法 :fclose(FILE *stream);
功能:关闭给定的文件指针
参数1:要关闭的文件指针
返回值:成功返回0,失败返回EOF,并置位错误码
fgetc\fputc()
单字符的输入输出
int fputc(int c, FILE *stream);
功能:将给定的字符,写入到文件指针stream指向的文件中去
参数1:要写入的字符
参数2:打开的文件指针
返回值:成功返回写入字符的ascii值,失败返回EOF,并置位错误码
int fgetc(FILE *stream);
功能:从指定文件中,读取一个字符
参数:打开的文件指针
返回值:从文件中读取的第一个字符的ascii值,失败返回EOF并置位错误码
有关错误码问题
strerror()
char *strerror(int errnum);
功能:将给定的错误码,转变成错误信息
参数1:错误码
返回值:错误码对应的错误信息的字符串
perror()
void perror(const char *s);
功能:向标准出错缓冲区中,写入最新的错误码对应的信息
参数:提示字符串,会自动提供一个冒号,并且输出结束后,会自动加一个换行
返回值:无
fputs\fgets:字符串输入输出
int fputs(const char *s, FILE *stream);
功能:将给定的字符串,写入到文件中
参数1:要写入的字符串起始地址
参数2:打开的文件指针 返回值:成功返回写入的字符个数(字符串长度),失败返回EOF
char *fgets(char *s, int size, FILE *stream);
功能:从stream所指向的文件中,最多读取size-1的字符到s中,在读取过程中,如果遇到回车或者文件结束,会结束本次读取,并且会把回车也放入容器中。在后面自动加上'\0'
参数1:存放数据的容器,一般是一个字符数组
参数2:读取的大小
参数3:文件指针
返回值:成功返回容器的起始地址,失败返回NULL
关于缓冲区
1> 标准IO提供了三种缓冲区:行缓存、全缓存、不缓存
2> 行缓存:有关标准输入、标准输出指针对应的缓冲区,其大小位1024字节
3> 全缓存:有关普通文件指针对应的缓冲区,其大小位4096字节
4> 不缓存:有关标准出错文件指针对应的缓冲区,其大小位 0
行缓存的刷新时机
1、换行会刷新行缓存
2、程序结束后,会自动刷新行缓存
3、当文件指针关闭后,会刷新行缓存
4、当使用fflush函数刷新文件指针时,会刷新行缓存
5、当输入输出切换时,会刷新行缓存 6、当缓存区满了后,再放数据时,会刷新行缓存
全缓存的刷新时机
2、程序结束后,会自动刷新全缓存
3、当文件指针关闭后,会刷新全缓存
4、当使用fflush函数刷新文件指针时,会刷新全缓存
5、当输入输出切换时,会刷新全缓存 6、当缓存区满了后,再放数据时,会刷新全缓存
练习:
使用fgets统计给定文件的行号
#include <myhead.h>
int main(int argc, char const *argv[])
{
FILE *file;
char line[1024]; // 假设每一行不一般超过1023个字符
int count = 0;
// 打开文件
file = fopen(argv[1], "r");
if (file == NULL)
{
printf("无法打开文件。\n");
return 1;
}
// 逐行读取文件
while (fgets(line, sizeof(line), file))
{
count++;
}
// 关闭文件
fclose(file);
printf("文件中有 %d 行\n", count);
return 0;
}
使用fgets、fputs完成两个文件的拷贝
#include <myhead.h>
int main(int argc, char const *argv[])
{
FILE *file1;
FILE *file2;
char line[1024]; // 假设每一行不一般超过1023个字符
int count = 0;
// 打开文件
file1 = fopen(argv[1], "r");
file2 = fopen(argv[2], "w");
if (file1 == NULL || file2 == NULL)
{
printf("无法打开文件\n");
return 1;
}
// 逐行读取文件
while (fgets(line, sizeof(line), file1))
{
count++;
fputs(line, file2);
}
// 关闭文件
fclose(file1);
fclose(file2);
printf("文件中有 %d 行\n", count);
return 0;
}