什么是文件I/O?
posix(可移植操作系统接口)定义的一组函数
不提供缓冲机制,每次读写操作都引起系统调用
核心概念是文件描述符
访问各种类型文件
Linux下, 标准IO基于文件IO实现
文件I/O 又称为低级磁盘I/O,遵循POSIX相关标准。任何兼容POSIX标准的操作系统上都支持文件I/O。标准I/O被称为高级磁盘I/O,遵循ANSI C相关标准。只要开发环境中有标准I/O库,标准I/O就可以使用。(Linux 中使用的是GLIBC,它是标准C库的超集。不仅包含ANSI C中定义的函数,还包括POSIX标准中定义的函数。因此,Linux 下既可以使用标准I/O,也可以使用文件I/O)。
通过文件I/O读写文件时,每次操作都会执行相关系统调用。这样处理的好处是直接读写实际文件,坏处是频繁的系统调用会增加系统开销,标准I/O可以看成是在文件I/O的基础上封装了缓冲机制。先读写缓冲区,必要时再访问实际文件,从而减少了系统调用的次数。
文件I/O中用文件描述符表现一个打开的文件,可以访问不同类型的文件如普通文件、设备文件和管道文件等。而标准I/O中用FILE(流)表示一个打开的文件,通常只用来访问普通文件
open()函数用于创建或打开文件,在打开或创建的时可以指定文件打开的方式及文件访问的权限
close()函数用于关闭一个被打开的文件,当一个进程终止时,所有打开的文件都由内核自动关闭。很多程序都利用这一特性而不显式地关闭一个文件。
open函数用来创建或打开一个文件:
#include
int open(const char *path, int oflag, …);
成功时返回文件描述符;出错时返回EOF
打开文件时使用两个参数
创建文件时第三个参数指定新文件的权限
只能打开设备文件
返回值:
若所有欲核查的权限都通过了检查则返回0 值, 表示成功, 只要有一个权限被禁止则返回-1。
open – 示例1
以只写方式打开文件1.txt。如果文件不存在则创建,如果
文件存在则清空:
int fd;
if ((fd = open(“1.txt”, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
{
perror(“open”);
return -1;
}
open – 示例2
以读写方式打开文件1.txt。如果文件不存在则创建,如果
文件存在则报错:
int fd;
if ((fd = open(“1.txt”, O_RDWR|O_CREAT|O_EXCL, 0666)) < 0)
{
if (errno == EEXIST)
{
perror(“exist error”);
}
else
{
perror(“other error”);
}
}
close函数用来关闭一个打开的文件:
#include
int close(int fd);
所需头文件 | #include |
---|---|
函数原型 | int close(int fd); |
函数参数 | fd :文件描述符 |
函数返回值 | 若文件顺利关闭则返回0, 发生错误时返回-1。 |
read()函数从文件中读取数据存放到缓存区中,并返回实际读取的字节数。
write()函数将数据写入文件中,并返回实际写入的字节数。
read()函数语法要点
所需头文件 | #include |
---|---|
函数原型 | ssize_t read(int fd,void * buf ,size_t count); |
函数参数 | fd :文件描述符 ; buf:指定存储器读出的数据的缓冲区; count: 指定读出的字节数 |
函数返回值 | 成功:读到的字节数 ; 0:已到达文件尾; -1:出错 |
write()函数语法要点
所需头文件 | #include |
---|---|
函数原型 | ssize_t write(int fd, void *buf, size_t count); |
函数参数 | fd :文件描述符 ; buf:指定存储器写入数据的缓冲区; count: 指定读出的字节数 |
函数返回值 | 成功:已写的字节数 ; -1:出错 |
I/O – read – 示例
从指定的文件(文本文件)中读取内容并统计大小
int main(int argc, char *argv[])
{
int fd, n, total = 0;
char buf[64];
if (argc < 2)
{
printf(“Usage : %s \n”, argv[0]);
return -1;
}
if ((fd = open(argv[1], O_RDONLY)) < 0)
{
perror(“open”); return -1;
}
//循环从文件中读取数据,直到文件末尾
while ((n = read(fd, buf, 64)) > 0)
{
total += n;// 累加每次读取的字节数
}
close(fd);
}
write函数用来向文件写入数据:
#include
ssize_t write(int fd, void *buf, size_t count);
int fd;
char buf[20];
if ((fd = open(argv[1], O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
{
perror(“open”); return -1;
}
while (fgets(buf, 20, stdin) != NULL)
{
if (strcmp(buf, “quit\n”) == 0) break;
write(fd, buf, strlen(buf));
}
lseek函数用来定位文件:
#include
off_t lseek(int fd, off_t offset, intt whence);
成功时返回当前的文件读写位置;出错时返回EOF
参数offset和参数whence同fseek完全一样
lseek()函数语法要点
所需头文件 | #include |
---|---|
函数原型 | off_t lseek(int fildes,off_t offset ,int whence); |
函数参数 | fd :文件描述符 ;offset:相对于基准点 whence 的偏移量。以字节为单位,正数表示向前移动,负数表示向后移动 |
whence:当前位置的基点(SEEK_SET:文件的起始位置; SEEK_CUR:文件当前读写位置; SEEK_END:文件的结束位置) | |
函数返回值 | 成功:文件当前读写位置; -1:出错 |
利用文件IO实现文件的复制
#define N 64
int main(int argc, const char *argv[])
{
int fds, fdt, n;
char buf[N];
if(argc < 3)
{
printf("Input Error %s\n",argv[0]);
return -1;
}
if((fds = open(argv[1], O_RDONLY)) == -1)
{
fprintf(stderr, "open %s: %s\n", argv[1], strerror(errno));
return -1;
}
if((fdt = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC,0666)) == -1)
{
fprintf(stderr, "open %s: %s\n", argv[2], strerror(errno));
return -1;
}
while((n=read(fds, buf, N)) > 0)
{
write(fdt, buf, n);
}
close(fds);
close(fdt);
return 0;
}
实现的功能是从一个文件(源文件)中读取最后 10KB 数据并复制到另一个文件(目标文件)。源文件
以只读方式打开,目标文件是以只写方式打开,若目标文件不存在,可以创建并设置权限的初始值为 644,
即文件所有者可读可写,文件所属组和其他用户只能读。
#define BUFFER_SIZE (1024) /* 每次读写缓存大小,影响运行效率 */
#define SRC_FILE_NAME "src_file" /* 源文件名 */
#define DEST_FILE_NAME "dest_file"/* 目标文件名 */
#define OFFSET (10240) /* 复制的数据大小 */
int main(int argc, const char *argv[])
{
int fd_src, fd_dest;
/* 以只读方式打开源文件 */
if((fd_src = open(SRC_FILE_NAME, O_RDONLY)) < 0)
{
perror(" fail to open src_file");
return -1;
}
/* 以只写方式打开目标文件,若此文件不存在则创建该文件,访问权限值为 644 */
if((fd_dest = open(DEST_FILE_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0)
{
perror(" fail to open dest_file");
return -1;
}
/* 将源文件的读写指针移到最后 10KB 的起始位置 */
lseek(fd_src, -OFFSET, SEEK_END);
unsigned char buff[BUFFER_SIZE];
int read_len = 0;
/* 读取源文件的最后 10KB 数据并写到目标文件中,每次读写 1KB */
while((read_len = read(fd_src, buff, sizeof(buff))) > 0 )
{
write(fd_dest, buff, read_len);
}
close(fd_src);
close(fd_dest);
return 0;
}
opendir函数用来打开一个目录文件:
#include
DIR *opendir(const char *name);
DIR是用来描述一个打开的目录文件的结构体类型
成功时返回目录流指针;出错时返回NULL
定义:
DIR * opendir(const char * name);
表头文件:
#include
#include
说明:
opendir()用来打开参数name指定的目录, 并返回DIR*形态的目录流, 和open()类似, 接下来对目录的读取和搜索都要使用此返回值。
返回值
成功则返回DIR* 型态的目录流, 打开失败则返回NULL。
readdir函数用来读取目录流中的内容:
#include
struct dirent *readdir(DIR *dirp);
struct dirent是用来描述目录流中一个目录项的结构体类型
包含成员char d_name[256] 参考帮助文档
成功时返回目录流dirp中下一个目录项;
出错或到末尾时时返回NULL
closedir函数用来关闭一个目录文件:
#include
int closedir(DIR *dirp);
成功时返回0;出错时返回EOF
访问目录 – 示例代码
int main(int argc, const char *argv[])
{
DIR *dir;
struct dirent *dp;
if(argc < 2)
{
printf("Input Error :%s \n", argv[0]);
return -1;
}
if((dir = opendir(argv[1])) == NULL)
{
perror("opendir");
return -1;
}
while((dp=readdir(dir))!= NULL)
{
printf("%s\n",dp->d_name);
}
closedir(dir);
return 0;
}
chmod/fchmod函数用来修改文件的访问权限:
#include
int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);
成功时返回0;出错时返回EOF
root和文件所有者能修改文件的访问权限
示例: chmod(“test.txt”, 0666);
获取文件属性 – stat/ lstat/ fstat
stat/lstat/fstat函数用来获取文件属性:
#include
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
成功时返回0;出错时返回EOF
如果path是符号链接stat获取的是目标文件的属性;而
lstat获取的是链接文件的属性
struct stat是存放文件属性的结构体类型:
mode_t st_mode; 类型和访问权限
uid_t st_uid; 所有者id
uid_t st_gid; 用户组id
off_t st_size; 文件大小
time_t st_mtime; 最后修改时间
文件类型 – st_mode
通过系统提供的宏来判断文件类型:
st_mode & 0170000
S_ISREG(st_mode) 0100000
S_ISDIR(st_mode) 0040000
S_ISCHR(st_mode) 0020000
S_ISBLK(st_mode) 0060000
S_ISFIFO(st_mode) 0010000
S_ISLNK(st_mode) 0120000
S_ISSOCK(st_mode) 0140000
文件访问权限 – st_mode
通过系统提供的宏来获取文件访问权限:
S_IRUSR 00400 bit:8
S_IWUSR 00200 bit 7
S_IXUSR 00100 bit6
S_IRGRP 00040 bit5
S_IWGRP 00020 bit4
S_IXGRP 00010 bit3
S_IROTH 00004 bit2
S_IWOTH 00002 bit1
S_IXOTH 00001 bit0
练习 – 获取并显示文件属性
以下面格式打印指定文件的主要信息:
$ ./a.out test.c
-rw-r–r-- 317 2014-11-08 test.c
调用lstat函数获取文件的属性
从stat结构体中获取相应信息并输出
int main(int argc, const char *argv[])
{
struct stat buf;
int n;
struct tm *tp;
if(argc < 2)
{
printf("Input Error :%s \n", argv[0]);
return -1;
}
if(lstat(argv[1], &buf) < 0)
{
perror("lstat");
return -1;
}
switch(buf.st_mode & S_IFMT)
{
case S_IFREG:
printf("-");
break;
case S_IFDIR:
printf("d");
break;
default:
break;
}
for(n=8; n>=0; n--)
{
//000 000 000
//权限位一共有9位二进制位,从左为高位,称作8位到0位,且分成了3组,3位一组,一组从左开始称作2位到0位,分别表示
//r w x, 所以8 % 3 可以用来找第几个位,比如找都是表示r的位置。
if(buf.st_mode & (1<tm_year+1900, tp->tm_mon+1, tp->tm_mday);
printf(" %s\n", argv[1]);
return 0;
}
利用stat函数实现下命令ll的功能
#include
#include
#include
#include
#include
#include
#include
int main(int argc, const char *argv[])
{
if(argc < 2)
{
printf(" Input Error %s \n",argv[0]);
return -1;
}
struct stat st;//保存获取的文件的属性
int ret = lstat(argv[1], &st);//获取文件的属性
if( -1 == ret)
{
perror("stat");
return -1;
}
//通过st_mode的值判断文件类型
switch(st.st_mode & S_IFMT)
{
case S_IFREG:
printf("-");
break;
case S_IFDIR:
printf("d");
break;
default:
break;
}
//文件的权限
int i=0;
for(i=8; i>=0; i--)
{
if(st.st_mode & (1<pw_name);
struct group *gp = getgrgid(st.st_gid);
printf(" %s",gp->gr_name);
printf(" %ld",st.st_size);
printf(" %s %s",argv[1], ctime(&(st.st_mtime)));
return 0;
}