文件 IO 是 Linux 系统提供的接口, 针对文件和磁盘进行操作, 不带缓存机制; 标准 IO 是 C 语言函数库里的标准 I/O 模型, 在 stdio.h 中定义, 通过缓冲区操作文件, 带缓存机制。 Linux 系统中一切皆文件, 包括普通文件, 目录, 设备文件(不包含网络设备) , 管道, fifio 队列, socket 套接字等, 在终端输入“ls -l”可查看文件类型和权限。
标准 IO 和文件 IO 常用 API 如下:
标准 IO | 文件 IO | |
打开/创建 | fopen | open |
读 | getc,fgetc,getchar,fgets,gets, fread | read |
写 | putc,fputc,putc,fputs,puts, fwrite | write |
关闭 | fclose | close |
标准 IO 和文件 IO 的区别如下图所示:
文件 IO 是直接调用内核提供的系统调用函数, 头文件是 unistd.h, 标准 IO 是间接调用系统调用函数,头文件是 stdio.h, 文件 IO 是依赖于 Linux 操作系统的, 标准 IO 是不依赖操作系统的, 所以在任何的操作系统下, 使用标准 IO, 也就是 C 库函数操作文件的方法都是相同的。
对于文件 IO 来说, 一切都是围绕文件操作符来进行的。 在 Linux 系统中, 所有打开的文件都有一个对应的文件描述符。 文件描述符的本质是一个非负整数, 当我们打开一个文件时, 系统会给我们分配一个文件描述符。 当我们对一个文件做读写操作的时候, 我们使用 open 函数返回的这个文件描述符会标识该文件,并将其作为参数传递给 read 或者 write 函数。 在 posix.1 应用程序里面, 文件描述符 0,1,2 分别对应着标准输入, 标准输出, 标准错误。
文件 IO open()
函数 | int open(const char *pathname, int flags) int open(const char *pathname, int flags, mode_t mode) |
头文件 | #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> |
参数 pathname | 路径和文件名 |
参数 flags | 文件打开方式, 可用多个标志位按位或设置 |
参数 mode | 权限掩码, 对不同用户和组设置可执行, 读, 写权限, 使用八进制数表示, 此参 数可不写 |
返回值 | open()执行成功会返回 int 型文件描述符, 出错时返回-1。 |
作用 | 通过系统调用, 可以打开文件, 并返回文件描述符 |
参数 flags 可选标志:
O_CREAT O_EXCL O_RDONLY O_WRONLY O_RDWR O_APPEND | 要打开的文件名不存在时自动创建改文件。 要和 O_CREAT 一起使用才能生效, 如果文件存在则 open()调用失败。 只读模式打开文件。 只写模式打开文件。 可读可写模式打开文件。 以追加模式打开文件。 |
O_NONBLOCK 以非阻塞模式打开。
实验代码
可读可写文件 a.c。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{
int fd;
fd=open("a.c",O_CREAT|O_RDWR,0666);
if(fd<0)
{
printf("open is error\n");
}
printf("fd is %d\n",fd);
return 0;
}
编译运行:
在 Ubuntu 上编译 ,并运行 a.out, 如下图所示:
文件 IO close()
函数 | int close(int fd) |
头文件 | #include <unistd.h> |
参数 fd | 文件描述符 |
返回值 | 成功返回 0; 错误返回-1 |
实验代码:
编写程序, 创建一个可读可写文件, 获取完属性后关闭。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int fd;
fd=open("a.c",O_CREAT|O_RDWR,0666);
if(fd<0)
{
printf("open is error\n");
}
printf("fd is %d\n",fd);
close(fd);
return 0;
}
编译运行:
在 Ubuntu 上编译 如下图所示:
文件 IO read()
函数 | ssize_t read(int fd, void *buf, size_t count) |
头文件 | #include <unistd.h> |
参数 fd | 要读的文件描述符 |
参数 buf | 缓冲区, 存放读到的内容 |
参数 count | 每次读取的字节数 |
返回值 | 返回值大于 0, 表示读取到的字节数; 等于 0 在阻塞模式下表示到达文件末尾或没有数据可读(EOF) , 并调用阻塞; 等于-1 表示出错, 在非阻塞模式下表示没有数据可读。 |
实验代码
在程序中打开 a.c 文件, 并读取打印文件内容。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd;
char buf[32] = {0};
ssize_t ret;
fd = open("a.c", O_CREAT | O_RDWR, 0666);
if (fd < 0)
{
printf("open is error\n");
return -1;
}
printf("fd is %d\n", fd);
ret = read(fd, buf, 32);
if (ret < 0)
{
printf("read is error\n");
return -2;
}
printf("buf is %s\n", buf);
printf("ret is %ld\n", ret);
close(fd);
return 0;
}
编译运行:
在 Ubuntu 上编译,运行 a.out,如下图所示:
编辑 a.c 文件, 内容如下图所示:
运行 a.out,如下图所示, 成功读取到 a.c 文件中的 hello world!
文件 IO write()
函数 | ssize_t write(int fd, const void *buf, size_t count); |
头文件 | #include <unistd.h> |
参数 fd | 文件描述符; |
参数 buf | 缓存区, 存放将要写入的数据 |
参数 count | 每次写入的个数 |
功能 | 每次从 buf 缓存区拿 count 个字节写入 fd 文件。 |
返回值 | 大于或等于 0 表示执行成功, 返回写入的字节数; 返回-1 代表出错。 |
实验代码
在程序中使用 write 函数向标准输出(屏幕) 打印 hello。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int fd;
char buf[32]={0};
ssize_t ret;
fd=open("a.c",O_CREAT|O_RDWR,0666);
if(fd<0)
{
printf("open is error\n");
return -1;
}
write(1,"hello\n",6);
close(fd);
return 0;
}
编译程序并运行 a.out ,如下图所示, 向标准输出(屏幕) 写入 hello。
在程序中使用 write 函数向文件写入 hello。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int fd;
char buf[32]={0};
ssize_t ret;
fd=open("a.c",O_CREAT|O_RDWR,0666);
if(fd<0)
{
printf("open is error\n");
return -1;
}
write(fd,"hello\n",6);
close(fd);
return 0;
}
运行测试
编译运行打开 a.out,并且查看写入的文件 a.c,如下图所示, a.c 文件成功写入 hello。
文件 IO lseek()
所有打开的文件都有一个当前文件偏移量(current file offset) , 以下简称为 cfo。 cfo 通常是一个非负整数, 用于表明文件开始处到文件当前位置的字节数。 读写操作通常开始于 cfo, 并且使 cfo 增大, 增量为读写的字节数。 文件被打开时, cfo 会被初始化为 0, 除非使用了 O_APPEND 。 使用 lseek 函数可以改变文件的 cfo 。
函数定义 | off_t lseek(int fd, off_t offset, int whence); |
头文件 | #include <sys/types.h> #include <unistd.h> |
参数 fd | 文件描述符 |
参数 off_t offset | 偏移量, 单位是字节的数量, 可以正负, 如果是负值表示向前移动; 如果是正值, 表示向后移动。 |
参数 whence | 当前位置的基点, 可以使用以下三组值。 SEEK_SET: 相对于文件开头 SEEK_CUR:相对于当前的文件读写指针位置 SEEK_END:相对于文件末尾 |
功能 | 移动文件读写指针; 获取文件长度; 拓展文件空间。 |
返回值 | 成功返回当前位移, 失败返回-1 |
举个例子
把文件位置指针设置为 100 lseek(fd,100,SEEK_SET);
把文件位置设置成文件末尾 lseek(fd,0,SEEK_END);
确定当前的文件位置 lseek(fd,0,SEEK_CUR);
实验代码
在程序中使用 lseek 函数移动文件读写位置。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd;
char buf[32] = {0};
ssize_t ret;
fd = open("a.c", O_CREAT | O_RDWR, 0666);
if (fd < 0)
{
printf("open is error\n");
return -1;
}
printf("fd is %d\n", fd);
ret = read(fd, buf, 32);
if (ret < 0)
{
printf("read is error\n");
return -2;
}
printf("buf is %s\n", buf);
printf("ret is %ld\n", ret);
lseek(fd,0,SEEK_SET);
ret = read(fd, buf, 32);
printf("ret is %ld\n", ret);
close(fd);
return 0;
}
运行测试
编译运行打开 a.out ,a.c 里面里面有 hello world! , 当使用 lseek 函数移动读写位置时, 再次读 a.c 文件的内容, 依旧可以读到 a.c 的内容, 如下图所示:
实验代码
在程序中读 a.c 文件的 2 字节, 使用 lseek 函数确定文件的当前位置。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd;
char buf[32] = {0};
ssize_t ret;
fd = open("a.c", O_CREAT | O_RDWR, 0666);
if (fd < 0)
{
printf("open is error\n");
return -1;
}
ret = read(fd, buf, 2);
if (ret < 0)
{
printf("read is error\n");
return -2;
}
printf("buf is %s\n", buf);
printf("ret is %ld\n", ret);
ret=lseek(fd,0,SEEK_CUR);
printf("ret is %ld\n", ret);
close(fd);
return 0;
}
运行测试
编译运行打开 a.out ,a.c 里面里面有 hello world! 运行结果如下图所示:
实验代码
在程序中读 a.c 文件, 使用 lseek 函数确定文件的长度。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd;
char buf[32] = {0};
ssize_t ret;
fd = open("a.c", O_CREAT | O_RDWR, 0666);
if (fd < 0)
{
printf("open is error\n");
return -1;
}
ret = read(fd, buf, 2);
if (ret < 0)
{
printf("read is error\n");
return -2;
}
printf("buf is %s\n", buf);
printf("ret is %ld\n", ret);
ret=lseek(fd,0,SEEK_END);
printf("ret is %ld\n", ret);
close(fd);
return 0;
}
运行测试
编译运行打开 a.out ,a.c 里面里面有 hello world! 运行结果如下图所示: