标准IO与文件IO
1.标准IO
1.1什么是标准IO
1.1.1概念
标准IO:在C库中定义的一组专门用来输入输出的函数

1.1.2特点
1)通过缓冲机制减少系统调用,提高效率
系统调用:内核向上提供的一组接口
2)围绕流进行操作,流FILE *描述,FILE代表的是结构体
用vi的ctags索引:
先建立ctags索引:密码是1

1.vi -t 查找的名称
输入序号,回车

2.继续追踪
将光标定位到要追踪的内容:ctrl ]
回退:ctrl t
3.跳转到上次位置:ctrl o
跳转到下次位置:ctrl i
q:退出
vscode索引:
跳转到定义:ctrl 左键
前后跳转:
ctrl alt -
ctrl shift -
3)默认打开三个流:stdin(标准输入)、stdout(标准输出)、stderr(标准错误)
1.1.3操作
打开文件:fopen
关闭文件:fclose
读写操作:fgets、fputs、fread、fwrite
定位操作:rewind、fseek、ftell
1.2缓冲区
1.2.1全缓存:和文件相关
刷新条件:
缓存区满
程序正常退出
强制刷新:fflush(NULL);
1.2.2行缓存:和终端有关
刷新条件:
缓存区满
程序正常退出
强制刷新:fflush(NULL);
\n
1.2.3不缓存:标准错误
1.3函数接口
1.3.1打开文件fopen
#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
功能:打开文件
参数:pathname:打开的文件路径
mode:打开方式
r:只读,流被定位到文件开头
r+:可读可写,流被定位到文件开头
w:只写,文件不存在创建,文件存在清空,流被定位到文件开头
w+:可读可写,文件不存在创建,文件存在清空,流被定位到文件开头
a:追加,文件不存在创建存在追加,流被定位到文件末尾
a+:可读可写,文件不存在创建,存在追加,开始进行读时从头读,进行写时流被定位到文件末尾
返回值:成功:文件流
失败:NULL, 并设置errno(错误码)
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE * fp = fopen("./test", "r");
if(NULL == fp)
{
// perror简单原理
// vi -t EEXIST // 查看相关错误码
// 使用这个函数就会根据errno的值,找到对应的错误信息打印出来
perror("fopen file err");
return -1;
}
printf("fopen file success\n");
return 0;
}
1.3.2关闭文件
#include <stdio.h>
int fclose(FILE *stream);
功能:关闭文件
参数:stream:文件流
1.3.3读写文件操作
1.3.3.1每次读写一个字符串:fgets、fputs
#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);
功能:从文件中读取一个字符串
参数:s:存放读取的字符串首地址
size:读取的大小
stream:文件流
返回值:成功:读取的字符串首地址
失败或读到文件末尾:NULL
特性:1. 一次调用最多读取一行数据,遇到\n或者达到文件末尾后不在继续下一行
2. 实际读到个数为size-1个,末尾自动添加\0
#include <stdio.h>
int fputs(const char *s, FILE *stream);
功能:向文件中写一个字符串
参数:s:要写入的内容
stream:文件流
返回值:成功:非负整数
失败:EOF

#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp = fopen("./test", "r");
if (NULL == fp)
{
// perror简单原理
// vi -t EEXIST // 查看相关错误码
// 使用这个函数就会根据errno的值,找到对应的错误信息打印出来
perror("fopen file err");
return -1;
}
printf("fopen file success\n");
char buf[32] = {};
fgets(buf, 32, fp);
fputs(buf, stdout); // buf: hello\n\0
fgets(buf, 32, fp);
fputs(buf, stdout); // buf: nihao\n\0
fgets(buf, 3, fp);
fputs(buf, stdout); // buf: 12\0ao\n\0
fgets(buf, 3, fp);
fputs(buf, stdout); // buf: 3\n\0ao\n\0
fclose(fp);
return 0;
}
练习:编程实现wc-l命令的功能
wc-l文件名:显示文件的行数
思路:打开文件,循环读取文档,当读取文件末尾时循环结束,在循环中判断是否是一行,如果是则对整型变量进行自加,关闭文件。
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
if(argc != 2)
{
printf("usage: %s <filename>\n", argv[0]);
return -1;
}
FILE *fp = fopen(argv[1], "r");
if(NULL == fp)
{
perror("fopen file err");
return -1;
}
char buf[32] = {};
int len = 0;
while(fgets(buf, sizeof(buf), fp) != NULL)
{
if(buf[strlen(buf)-1] == '\n')
{
len++;
}
}
printf("%s length: %d\n", argv[1], len);
return 0;
}
1.3.3.2二进制读写fread、fwrite
#include <stdio.h>
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 : 要进行写入元素的个数
stream: 目标文件流指针
返回值:成功:写元素的个数
失败:-1
#include <stdio.h>
int main(int argc, char const *argv[])
{
int arr[3] = {1, 2, 3};
int num[3] = {};
FILE *fp = fopen("./test", "r+");
if(NULL == fp){}
fwrite(arr, sizeof(int), 3, fp);
rewind(fp); // 将文件的位置指针(流)定位到开头
fread(num, sizeof(int), 3, fp);
for(int i = 0 ;i < 3; i++)
printf("%d\n", num[i]);
return 0;
}
1.3.4文件定位操作 rewind、fseek、ftell
rewind
void rewind(FILE* stream);
功能:将文件的位置指针定位到起始位置
fseek
#include <stdio.h>
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指针移动到离文件开头10个字节处:fseek(fp, 10, SEEK_SET);
把fp指针移动到离文件当前位置10个字节处:fseek(fp, 10, SEEK_CUR);
把fp指针移动到离文件结尾10个字节处:fseek(fp, -10, SEEK_END);

ftell
long ftell(FILE *stream)
功能:获取当前的文件位置
参数:要检测的文件流
返回值:成功:当前的文件位置
失败:-1

2.文件IO
2.1什么是文件IO
2.1.1概念
又称系统IO,是系统调用,是操作系统提供的函数接口
在posix(可以移植操作系统接口)中定义的一组输入输出函数
2.1.2特点
1.没有缓冲机制,每次操作都会经过系统调用,效率比较低
2.围绕着文件描述符进行操作,文件描述符是非负整数:0、1、2
3.默认打开三个文件描述符:0(标准输入)、1(标准输出)、2(标准错误)
4.除目录外其他任意类型的文件都可以操作b、c、-、l、s、p
问题:打开三个文件,文件描述符:3、4、5
关闭3以后,重新打开文件,描述符是几?
问题:一个进程的文件描述符最大到几?最多能打开多少个文件描述符?最多能打开1024-3=1021个文件
2.1.3操作
打开文件:open
关闭文件:close
读写操作:read、write
定位操作:lseek
2.2函数操作
2.2.1打开文件open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
功能:打开文件
参数:pathname:文件路径名
flags:打开方式
O_RDONLY:只读
O_WRONLY:只写
O_RDWR:可读可写
O_CREAT:不存在创建
O_TRUNC:清空
O_APPEND:追加
返回值:成功:文件描述符
失败:-1,并设置errno
当第二个参数中存在O_CREAT选项时,需要open函数传递第三个参数,创建文件的权限
int open(const char *pathname, int flags, mode_t mode);
0777 & (~umask) = 实际文件权限 umask: 0002
111 111 111
&
111 111 101
=
111 111 101 = 0775
补充:
1.当第二个参数中有O_CREAT选项时,需要给open函数传递第三个参数,指定创建文件的权限,open(file,xx|O_CREAT|xx,0666);
2.实际创建出来的文件权限为指定权限值&(~umask) //umask为文件权限掩码,通过umask命令查看
3.打开文件方式应对表:
|
标准IO |
文件IO |
|
r |
O_RDONLY |
|
r+ |
O_RDWR |
|
w |
O_WRONLY|O_CREAT|O_TRUNC |
|
w+ |
O_RDWR|O_CREAT|O_TRUNC |
|
a |
O_WRONLY|O_CREAT|O_APPEND |
|
a+ |
O_RDWR|O_CREAT|O_APPEND |
2.2.2关闭文件
#include <unistd.h>
int close(int fd);
参数:fd:文件描述符
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
int fd = open("./xxx", O_WRONLY | O_CREAT | O_TRUNC, 0666);
if(fd < 0)
{
perror("open err");
return -1;
}
printf("fd: %d\n", fd);
close(fd);
return 0;
}
2.2.3读写文件
read
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
功能:从一个打开的可读文件中读取数据
参数:fd:文件描述符
buf:存放位置
count:期望的个数
返回值:成功:实际读到的个数
返回-1:表示出错,并设置errno号
返回0:表示读到文件结尾
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
char buf[32] = {};
int fd = open("./xxx", O_RDONLY);
if(fd < 0)
{
perror("open err");
return -1;
}
printf("fd: %d\n", fd);
// 不同于标准IO中的fgets,read是指定多少就获取多少
// read(fd, buf, 5);
// printf("%s\n", buf);
int num = read(fd, buf, 32);
printf("buf: %s\n", buf);
printf("num: %d\n", num);
close(fd);
return 0;
}
write
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:向指定文件描述符中,写入 count个字节的数据。
参数:fd 文件描述符
buf 要写的内容
count 期望值
返回值:成功:实际写入数据的个数
失败:-1
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
char buf[32] = {};
int fd = open("./xxx", O_RDWR);
if(fd < 0)
{
perror("open err");
return -1;
}
printf("fd: %d\n", fd);
// 不同于标准IO中的fgets,read是指定多少就获取多少
// read(fd, buf, 5);
// printf("%s\n", buf);
int num = read(fd, buf, 32);
printf("buf: %s\n", buf);
printf("num: %d\n", num);
// 如果这样,文件中会出现一些乱码,因为buf空间中有很多\0
// write(fd, buf, sizeof(buf));
write(fd, buf, num);
close(fd);
return 0;
}
补充:在读和写一起使用时,一般write的字符个数指定为read的返回值,“读多少写多少”
练习:编程实现cp命令功能,用文件IO实现。
cp 源文件 新文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
char buf[32] = {};
int r_fd = open("./read.c", O_RDONLY); // r
if(r_fd < 0)
{
perror("open err");
return -1;
}
int w_fd = open("./xxx", O_WRONLY | O_CREAT | O_TRUNC, 0666); // w
if(w_fd < 0)
{
perror("open err");
return -1;
}
ssize_t n = 0;
while((n = read(r_fd, buf, sizeof(buf))) > 0)
{
write(w_fd, buf, n);
}
close(r_fd);
close(w_fd);
return 0;
}
补充:(命令:完全相同的话是没有任何输出的,有不同的地方它会把不同地方列出来)
diff 文件1 文件2:比较两个文件是否相同
2.2.4定位操作lseek
#include <sys/types.h>
#include <unistd.h>
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
标准IO和文件IO区别
|
标准IO |
文件IO | |
|
概念 |
在C库中定义的一组输入输出的函数 |
在posix中定义的一组输入输出的函数 |
|
特点 |
|
|
|
函数 |
打开文件:fopen\freopen 关闭文件:fclose 读写文件:fgetc/fputc fgets/fputs fread/fwrite 文件定位:fseek/rewind/ftell |
打开文件:open 关闭文件:close 读写文件:read/write 文件定位:lseek |
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)