一、如何理解文件IO(了解)
在Linux下有两种文件操作方法 标准IO 文件IO
标准IO与文件IO的区别:
标准IO : 遵循C标准 、带缓冲 、 流(FILE结构体)
文件IO: POSIX规范 、无缓冲 、 文件描述符(fcl)
什么是文件IO:
— posix(可移植操作系统接口) 定义的一组函数。
— 不提供缓冲机制,每次读写操作都引起系统调用。
— 核心概念是 文件描述符。
— 访问各种类型文件。(除了普通文件,终端文件,Linux下7中文件类型都可以访问)
— Linux下,标准IO基于 文件IO。(基于文件IO 封装了一个 缓冲机制)
二、文件IO描述符的含义(了解)
- 每个打开的文件都对应一个文件描述符。
- 文件描述符是一个非负整数。Linux为程序中每打开的文件分配一个文件描述符。
- 文件描述符从0开始分配,依次递增,且每次分配都是先找最小非负整数分配。
例如:打开了0,1,2三个文件,关闭1后再打开一个文件,分配的文件描述符为1.
- 不同函数中 相同文件描述符 代表的是不同的文件,互不影响。
- 文件IO操作通过文件描述符来完成。
- 0,1,2的含义?
每打开一个文件,系统自动创建三个文件(标准输入stdin(0),标准输出stdout(1),标准错误stderr(2))使用这三个文件描述符表示。
三、文件的打开和关闭(了解)
1、文件IO ------ open
- Open :函数用来创建或打开一个文件。
#include <fcntl.h>
Int open(const char *path,into flag,……..);
Path: 要打开文件的路径。
Flag: 要打开的模式。
- 成功时返回文件描述符:出错时返回EOF
- 打开文件时使用两个参数。
- 创建文件时第三个参数指定新文件的权限。
- 只能打开设备文件,不能用来创建设备文件,设备文件需要使用专门的函数创建。
示例:
以只写的形式打开文件test.txt ,如果文件不存在则创建,如果文件存在则清空。
以只写的形式打开文件test.txt ,如果文件不存在则创建,如果文件存在则报错。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(int argc, const char *argv[])
{
int fp;
if(argc < 2)
{
printf("Usage : %s <src_file> <dst_file> \n",argv[0]);
return -1;
}
if((fp = open(argv[1],O_WRONLY|O_CREAT|O_EXCL,0666))<0)
{
printf("%s:%s \n",argv[1],strerror(errno));
//error("argv[1])");
return -1;
}
close(fp);
return 0;
}
2、文件IO ------ close
#include <unistd>
Int close(int fd)
- 成功返回0 ;失败返回EOF
- 程序结束时会自动关闭打开的文件。
- 文件关闭后,其对应的文件描述符将与该文件无关。
- 系统打开的文件个数有限(Linux 1024个),所以打开的文件使用完后应该及时关闭。
四、文件的读取、写入及定位(了解)
1、读取文件(熟练)
#include <unistd>
ssize_t read(int fd , int *buf ,int count);
- Fd : 文件描述符 buf : 存放读出数据的缓冲区 count : 需要读出的字节数
- 成功时 返回实际读出的字节大小, 失败返回 EOF ; 文件的数据少于指定读出的大小,有多少读出多少并返回实际读出大小;读到文件末尾 返回0;
- Buf 是接收数据的缓冲区。
- Conunt的大小不应该 大于 buff的大小。
实例: 从指定的文件(文本文件)中,读取文件并统计大小。
2、写入文件(熟练)
#include <unistd>
ssize_t write(int fd , int *buf ,int count);
- Fd : 文件描述符 buf : 存放写入数据的缓冲区 count : 需要写入的字节数
- 成功时 返回实际写入的字节大小, 失败返回 EOF ;
- Buf 是发送数据的缓冲区。
- Conunt的大小不应该 大于 buff的大小。
3、定位文件(熟练)
Lseek() 函数用来定位文件
#include <unistd.h>
off_t lseek (int fd,off_t offset, int whence);
- 成功时返回当前的文件读写的位置;出错时返回EOF。
- 参数offset和参数wehence 同fseek 完全一样。
— 思考与练习
- 利用文件IO实现文件的复制。
文件名通过命令行参数的拟定
打开文件的方式?
如何判断是否读到了源文件的末尾?
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
int main(int argc, const char *argv[])
{
int fp;
int fpd;
int buff[20];
int ch;
if(argc<3) //判断一下输入的参数 是否小于三个
{
printf("Usage : %s <src_file><dst_file>\n",argv[0]);
return -1;
}
if((fp = open(argv[1],O_RDONLY)) == -1) // 以只读 方式打开必须存在的文件
{
perror("fopen src file ");
return -1 ;
}
if((fpd = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0666)) == -1) //以只写 清空方式打开文件,不存在则 创建
{
perror("fopen dst file ");
return -1 ;
}
while((ch = read(fp,buff,20))>0) // 从文件中读出数据放入 buffhu缓冲中
{
fwrite(fpd,buff,20); // 将buff缓冲中的 数据 写入 文件fpd中
}
fclose(fp); //关闭文件 fp
fclose(fpd); //关闭文件 fp
return 0;
}
五、文件目录的操作
1、读取目录
opendir 函数用打开一个目录文件。
#include <dirent.h>
DIR *opendir(const char * name);
- DIR是用来描述一个打开的目录文件的结构体类型。
- 成功是返回目录的流指针,出错时返回NULL.
readdir 函数用来读取目录流中的内容。
struct dirent * readdir(DIR * dirp);
- struct dirent 是用来描述目录流中一个目录项的结构体类型。
- 包含成员 char d_name[256] 等 (具体参考帮助文档)
- 成功时返回目录流dirp中下一个目录项。
- 出错或到末尾时 返回NULL。
On Linux, the dirent structure is defined as follows:
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* not an offset; see NOTES */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported
by all filesystem types */
char d_name[256]; /* filename */
};
示例:打印指定的目录下所有文件名称。
Int main(argc,char *argv[])
{
DIR *dirp;
struct dirent *dp;
if(argc <2 )
{
printf(“Usage:%s <directory> \n”,argv[0]);
return -1;
}
if((dirp = opendir(argv[1] ))==NULL)
{
perror(“opendir”);
return -1;
}
while(dp = readdir(dirp) !=NULL)
{
printf(“%s \n”,dp->d_name);
}
closedir(dirp);
}
注意: 如果该目录中 还有 子目录文件,该函数是读取不出来的,该函数只能读取当前目录下的所以有内容。
2、修改文件访问权限
Chmod / fchmod 函数用来修改文件的访问权限
#include <sys/stat.h>
Int chmod (const char *path,mode_t mode);
Int fchmod(int fp, mode_t mode );
- 成功时返回0,出错时返回EOF.
- Root 和文件的拥有者能修改文件的访问权限(其他人不能修改)
示例:
Chmod (“test.txt”,0666); //以八进制的形式
3、获取文件属性
stat / lstat/fstat 函数 用来获取文件属性。
int stat (const char* path, struct stat *buf);
int lstat(const char* path, struct stat *buf);
int fstat( int pd, struct stat *buf);
- 成功时返回0;失败时返回EOF。
- 如果path是符号链接符,stat函数获取的是 目标文件的属性,而lstat 获取的是链接文件的属性。 (一般使用 lstat函数)
- struct stat 结构体定义如下:
struct stat {
dev_t st_dev; // 文件所在设备ID
ino_t st_ino; // 结点(inode)编号
mode_t st_mode; // 保护模式
nlink_t st_nlink; // 硬链接个数
uid_t st_uid; // 所有者用户ID
gid_t st_gid; // 所有者组ID
dev_t st_rdev; // 设备ID(如果是特殊文件)
off_t st_size; // 总体尺寸,以字节为单位
blksize_t st_blksize; // 文件系统 I/O 块大小
blkcnt_t st_blocks; // 已分配 512B 块个数
time_t st_atime; // 上次访问时间
time_t st_mtime; // 上次更新时间
time_t st_ctime; // 上次状态更改时间
};
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 & 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
S_IFMT 0170000 文件类型位域掩码
S_IFSOCK 0140000 套接口
S_IFLNK 0120000 符号链接
S_IFREG 0100000 普通文件
S_IFBLK 0060000 块设备
S_IFDIR 0040000 目录
S_IFCHR 0020000 字符设备
S_IFIFO 0010000 FIFO
S_ISUID 0004000 设置 UID 位
S_ISGID 0002000 设置 组ID 位 (看下面)
S_ISVTX 0001000 粘滞位(看下面)
S_IRWXU 00700 文件所有者权限掩码
S_IRUSR 00400 所有者有读权限
S_IWUSR 00200 所有者有写权限
S_IXUSR 00100 所有者有执行权限
S_IRWXG 00070 组权限掩码
S_IRGRP 00040 组有读权限
S_IWGRP 00020 组有写权限
S_IXGRP 00010 组有执行权限
S_IRWXO 00007 其他用户权限掩码(不在组内)
S_IROTH 00004 其他有读权限
S_IWOTH 00002 其他有写权限
S_IXOTH 00001 其他有执行权限
通过系统提供的宏来获取文件访问权限:
- S_IRUSR 00400 bit:8
- S_IWUSR 00200 7
- S_IXUSR 00100 6
- S_IRGRP 00040 5
- S_IWGRP 00020 4
- S_IXGRP 00010 3
- S_IROTH 00004 2
- S_IWOTH 00002 1
- S_IXOTH 00001 0
示例:获取并显示文件属性
以下面格式打印指定文件的主要信息:
$ ./a.out test.c
-rw-r—r-- 317 2019 – 11 – 18 test.c
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, const char *argv[])
{
struct stat buf;
struct tm *tp;
int n;
if(argc<2)
{
printf("Usage : %s <file>\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("-");
case S_IFDIR:
printf("d");
break;
}
for(n=8;n>=0;n--)
{
if(buf.st_mode & (1<<n)) // 辨别 文件的 权限 (9位)
{
switch(n%3)
{
case 2:
printf("r");
break;
case 1:
printf("w");
break;
case 0:
printf("x");
break;
}
}
else
{
printf("-");
}
}
printf(" %lu",buf.st_size); //输出文件的目录的大小
tp = localtime(&buf.st_mtime); //得到上次更新时间
printf(" %d-%02d-%02d",tp->tm_year+1900,tp->tm_mon+1,tp->tm_mday);
printf(" %s\n",argv[1]);
return 0;
}