C库I/O函数复习
比较常用的几个C库函数有fopen()
、fwrite()
、fread()
、fprintf()
、fscanf()
等,这里挑几个复习,其他函数可以查看man手册。
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main()
{
FILE *fp = NULL;
//FILE文件流指针——C库IO接口句柄。
size_t ret;
fp = fopen("./c_io.txt", "w+");
//FILE *fopen(const char *path, const char *mode);
// path 路径
// mode 打开方式
// r 只读打开已经存在的文件
// r+ 读写打开已经存在的文件
// w 只写打开文件,文件不存在则创建,存在则长度截断为0
// w+ 读写打开文件,文件不存在则创建,存在则长度截断为0
// 若文件不存在,则创建文件,创建的文件权限默认为664
// a 追加模式(只写)打开文件,从文件末尾开始写入数据
// a+ 读写(追加),文件读写位置刚打开的时候在起始位置,当写入的
// 时候文件读写位置移动到文件末尾
// 若文件不存在,则创建文件,创建的文件权限默认为664
if (fp == NULL) {
perror("fopen error");
return -1;
}
size_t = fwrite("hello there!\n", 11, 1, fp);
//size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
//ptr 读取数据存入的目标地址
//size 一次需要读取的字节
//nmemb 读取的次数
//stream 文件指针
if (ret == 0) {
perror("fwrite error");
}
if (feof(fp) == 1) {
printf("file pointer in the end of file!\n");
}
char buff[1024] = {0};
fseek(fp, 0, SEEK_SET);
//fseek
// SEEK_SET 从文件起始位置开始偏移
// SEEK_CUR 从当前读写位置开始偏移
// SEEK_END 从文件末尾位置开始偏移
ret = fread(buff, 1, 11, fp);
//size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
//ptr 读取数据存入的目标地址
//size 一次需要读取的字节
//nmemb 读取的次数
//stream 文件指针
if (ret == 0) {
perror("read error");
}
printf("buff->%s\n", buff);
//fseek(fp, 0, SEEK_END);
//fprintf(fp, "%s-%d\n", "bit", 666);
fclose(fp);
return 0;
}
系统调用I/O
几个知识点
文件描述符
一个int整数,每个文件描述符都对应一个文件 标准输入对应0,标准输出对应1,标准错误输出对应2
POSIX 定义了 STDIN_FILENO、STDOUT_FILENO 和 STDERR_FILENO 来代替 0、1、2文件描述符如何分配
0、1、2已经被标准输入、输出、错误输出,接下来的文件将优先分配当前进程最小可用文件描述符
例如已经分配到10,3对应文件被关闭,下一个打开的文件将优先分配3文件留置针
文件留置针是一个结构体,这个结构体中有一个成员就是文件描述符
库函数和系统调用接口的关系
上下级调用关系 下面介绍系统调用I/O的几个基本函数
进程如何通过一个数字来操作文件呢?
这个数字是进程pcb中file_struct这个结构体中描述文件描述信息的结构体数组的下标 操作系统就可以通过数字下表找到对应文件的信息,来完成对这个文件的操作
函数接口
open
int open(const char *pathname, int flags, mode_t mode);
pathname:要打开的文件路径
flags:标志选项
必选项:(这个三个选项互相冲突,只能选择其中一个)
O_RDONLY 只读
O_WRONLY 只写
O_RDWR 读写
非必选项:
O_CREAT 若文件存在在打开,不存在则创建
O_EXCL 与O_CREAT同用时,若文件存在则报错
O_TRUNC 若文件存在则将文件长度截断为0
O_APPEND 追加(针对写入)
mode:若文件不存在需要创建的时候,用于指定创建的文件权限
返回值:
成功 :非负整数(文件描述符),后续操作都通过描述符完成
失败 :-1
write
ssize_t write(int fd, const void *buf, size_t count);
从buf中向fd所代表的文件写入count个字节的数据
返回实际的写入长度,出错返回-1
read
ssize_t read(int fd, void *buf, size_t count);
从fd所代表的文件中读取count字节长度的数据放到buf中
返回实际读取的数据长度,出错返回-1
lseek
off_t lseek(int fd, off_t offset, int whence);
fd 文件描述符
offset 偏移量
whence 偏移位置
SEEK_SET 从文件头部偏移
SEEK_CUR 从当前位置偏移
SEEK_END 从文件尾部偏移
返回值:
成功返回偏移量
错误返回-1并将errno设置为错误码
代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
int main()
{
umask(0);
int fd;
fd = open("./s_io.txt", O_RDWR | O_CREAT | O_APPEND, 0777);
if (fd < 0)
{
perror("file open error");
return -1;
}
char w_buff[1024] = "Hi jo, long time no see!";
ssize_t w_len = write(fd, w_buff, strlen(w_buff));
if (w_len < 0)
{
perror("file write error");
return -1;
}
printf("write length:%ld\n", w_len);
lseek(fd, 0, SEEK_SET);
//读写位置跳转,从头开始偏移0个字节
char r_buff[1024] = { 0 };
ssize_t r_len = read(fd, r_buff, 1024);
if (r_len < 0)
{
perror("file read error");
return -1;
}
printf("read length:%ld\nread string->%s\n", r_len, r_buff);
close(fd);
return 0;
}
重定向
理解重定向前先看代码
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
int main()
{
int fd = 0;
//关闭文件描述符为1的文件,新打开的文件将占据1号描述符
close(1);
fd = open("./redirect.txt", O_RDWR | O_CREAT | O_APPEND, 0644);
if (fd < 0)
{
perror("Open file failed\n");
return -1;
}
//printf本来应该输出到屏幕,看看结果如何
printf("This is a file ostream redirect test\nFile fd -> %d\n", fd);
fflush(stdout);
close(fd);
return 0;
}
看下运行结果
运行没有任何输出,但是查看redirect.txt
却发现本来应该输出屏幕的内容输出到文件内了
为什么???
我们在前面说过文件描述符的0、1、2分别分配给了标准输入、标准输出和标准错误输出
我们关闭了文件描述符1,之后又打开了文件,按照文件描述符分配规则,优先分配最小的空闲文件描述符,因此1被分配给新打开的文件,此时如果输出,则输出到刚打开的文件中了
这里就是重定向的基本原理,linux下万物皆文件,所以重定向也是基于文件。
重定向应用
<输入重定向
标准输入重定向 <
原本从标准输入读取数据重定向文从其他文件读取数据
>和>>输出重定向
将原本要输出到标准输出的内容,重定向后输出到指定的其他文件
/dev/null
是个黑洞文件,所有写入该文件的内容都会被丢弃
标准输出STDOUT为1,但是我们一般不写1,忽略1,所以看下面语句中有>/dev/null
其实等同于1>/dev/null
2>&1
相当于2号文件重定向到1号文件,也就是STDERR重定向到STDOUT
./test.exe 2>&1 >/dev/null
//将标准错误输出重定向到标准输出,标准输出重定向到/dev/null
//也就是只输出错误输出,不输出正确输出
./test.exe >/dev/null 2>&1
//将标准输出重定向到/dev/null,标准错误输出重定向到标准输出
//由于标准输出已经重定向,所以错误输出也重定向到/dev/null
//不管正确还是错误输出均不输出