Linux系统编程—文件IO
一.文件IO 与 标准IO 的对比:
标准IO | 标准IO | |
---|---|---|
缓冲区 | 全缓存、行缓存、无缓存 | 没有缓存区 |
操作对象 | 流(FILE *) | 文件描述符(int fd) |
标准输入 | 输入流(stdin) | 0 |
标准输出 | 输出流(stdout) | 1 |
标准错误 | 错误流(stderr) | 2 |
遵从的标准 | ANSIC 标准 | POSIX 标准 |
打开 | fopen | open |
关闭 | fclose | close |
读 | fgetc/fread/fgets | read |
写 | fputc/fputs/fwrite | write |
定位 | fseek, ftell | lseek |
二.文件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>
参数: 参数一: 文件名 (可以包含路径)
参数二: 打开文件的方式(只读、只写、读写、创建、…)
O_RDONLY 只读
O_RDWR 读写
O_WRONLY 只写
O_CREAT 创建
O_TRUNC 清空文件
O_EXCL 如果创建失败,报错误信息
O_APPEND 打开文件时,定位到文件尾。
参数三: 只有在创建文件时,才使用参数三。参数三的功能:创建文件时,文件的权限。
对应的功能与参数:
标准IO | 文件IO |
---|---|
“r” | O_RDONLY |
“w” | O_WRONLY |
“a” | O_WRONLY |
//示例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{
int fd = open(argv[1],O_RDONLY);
if(fd != -1)
printf("open success\n");
else
printf("fail to open\n");
return 0;
}
- 关闭文件 close
函数原型: int close(int fd);
头文件: #include <unistd.h>
示例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd = open("a.c",O_WRONLY|O_CREAT|O_TRUNC);
if(fd == -1)
return -1;
printf("%d\n",fd);
close(fd);
}
- 读 read
函数原型: ssize_t read(int fd,void *buf,size_t count);
------------->
参数:参数二: 从fd中读到的数据,存放在此位置。
参数三: 申请读取数据的大小。
返回值: 实际读到的数据的个数 (有可能小于 参数 count)
头文件: #include <unistd.h>
/示例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
char buf[100] = {0};
int fd = open("cl.c",O_RDONLY);
if(fd == -1)
{
printf("fail to open\n");
return -1;
}
ssize_t s = read(fd,buf,20);
printf("read buf: %s\n",buf);
close(fd);
return 0;
}
//练习: 实现shell命令 cat 的功能。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
//#include <strings.h>
#include <string.h>
int main(int argc,char *argv[])
{
if(argc < 2)
{
printf("%s <filename>\n",argv[0]);
return -1;
}
char buf[100] = {0};
int fd = open(argv[1],O_RDONLY);
ssize_t n = read(fd,buf,100);
while(n != 0)
{
printf("%s",buf);
// bzero(buf,100);
memset(buf,0,100);
n = read(fd,buf,100);
}
close(fd);
return 0;
}
-
清零函数 bzero memset
4.1 bzero
函数原型: void bzero(void *s,size_t n);
函数参数: 参数一: 没有指定类型。 可以是任意类型的一块空间,s指向其空间首地址。
参数二: s 空间多大。
函数功能: 将 s 这片 连续空间的n个字节 清零。
头文件: #include <strings.h>
4.2 memset
函数原型: void *memset(void *s,int c,size_t n);
函数功能: 将 s这个连续n个字节 的空间,统一填充 c。
参数: 参数一: s 指向一片连续空间
参数二: 空间填充的数值。
参数三: s指向的空间 的大小。
//示例: 见上例。 -
写 write
函数原型: ssize_t write(int fd,const void *buf,size_t count);
函数功能: 将 buf 中的数据,写入到 fd 中。
//示例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{
if(argc < 2)
{
printf("%s <filename>\n",argv[0]);
return -1;
}
char buf[100] = "Hello world!";
int fd = open(argv[1],O_WRONLY|O_TRUNC); //如果权限中没有 O_TRUNC,那么源文件不清空。
if(fd == -1) //加上这个权限后,才清空源文件,然后再写。
return -1;
write(fd,buf,strlen(buf));
close(fd);
return 0;
}
//练习: 使用 read write,实现拷贝文件的功能。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
int main(int argc,char *argv[])
{
if(argc < 3)
{
printf("%s <src> <dest> \n",argv[0]);
return -1;
}
char buf[100] = {0};
int fds = open(argv[1],O_RDONLY);
int fdd = open(argv[2],O_RDWR|O_TRUNC|O_CREAT,0777);
ssize_t n = read(fds,buf,100);
while(n != 0)
{
write(fdd,buf,n);
bzero(buf,100);
n = read(fds,buf,100);
}
close(fds);
close(fdd);
return 0;
}
- 定位 lseek
函数原型: off_t lseek(int fd, off_t offset, int whence);
头文件: #include <sys/types.h>
#include <unistd.h>
参数: 参数一: 文件描述符。
参数二: 偏移量是多少。
参数三: 偏移位置:
SEEK_SET 文件开头。
SEEK_CUR 当前位置。
SEEK_END 文件末尾。
返回值: 偏移量的大小(从文件开头算起)
//示例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{
if(argc < 2)
{
printf("%s <filename>\n",argv[0]);
return -1;
}
char string[] = "Struggle for a better future";
char buf[100] = {0};
int fd = open(argv[1],O_RDWR);
if(fd == -1)
return -1;
ssize_t n = write(fd,string,strlen(string)+1); //将string中的内容写到文件 fd 中。
off_t pos = lseek(fd,0,SEEK_SET); //文件写完之后,定位位置是在文件末尾,所以读文件之前,要把定位重新放到文件开头。
printf("pos : %ld\n",pos);
read(fd,buf,n); //从文件的起始位置开始读取。lseek(fd,0,SEEK_SET) 将读取位置重新置到文件开头。
close(fd);
puts(buf);
return 0;
}
三.目录文件
- 打开目录 opendir
函数原型: DIR *opendir(const char *name);
头文件: #include <sys/types.h>
#include <dirent.h>
参数: 目录名称。
返回值: 成功: 指向目录的指针。
失败: NULL - 关闭目录 closedir
函数原型: int closedir(DIR *pd);
头文件: #include <sys/types.h>
#include <dirent.h>
返回值: 成功: 返回 0 .
失败: 返回 -1 .
//示例:
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main()
{
DIR *pdir = opendir("/home/linux/HELLO");
if(!pdir)
{
printf("Fail to open\n");
return -1;
}
else
{
printf("OK\n");
closedir(pdir);
}
return 0;
}
- 读文件: readdir
函数原型: struct dirent *readdir(DIR *dirp);
头文件: #include <dirent.h>
参数: opendir 的返回值。
返回值:失败: NULL
成功: 一个结构体指针: struct dirent *
结构体:
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* offset to the next dirent */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported
by all file system types */
char d_name[256]; /* filename */
};
//示例:
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main()
{
DIR *pdir = opendir("/home/linux");
if(!pdir)
{
printf("Fail to open\n");
return -1;
}
struct dirent *p = readdir(pdir);
printf("ino :%ld --- type : %d --- name : %s \n",p->d_ino,p->d_type,p->d_name);
closedir(pdir);
return 0;
}
//练习: 查看 /home 下的所有文件信息: inode name …
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main()
{
DIR *pdir = opendir("/home");
if(!pdir)
{
printf("Fail to open\n");
return -1;
}
struct dirent *p = readdir(pdir);
while(p)
{
// if(p->d_name[0] != '.')
printf("ino :%-8ld --- type : %u --- name : %-8s \n",p->d_ino,p->d_type,p->d_name);
p = readdir(pdir);
}
closedir(pdir);
return 0;
}
- 文件属性 stat fstat lstat
函数原型: int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);
头文件: #include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
参数: 参数一: 目录名称。
参数二: 是结构体指针。
struct stat {
mode_t st_mode; /* protection */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
off_t st_size; /* total size, in bytes */
time_t st_atime; /* time of last access */
time_t st_ctime; /* time of last status change */
...
};
关于结构体中的 st_mode:在POSIX 标准中,为用户提供以下带参宏,来检测文件的属性。
The following POSIX macros are defined to check the file type using the st_mode field:
S_ISREG(m) is it a regular file? //是否为普通文件? 是,返回真;不是,返回假。
S_ISDIR(m) directory? //是否为目录文件?
S_ISCHR(m) character device? //字符设备?
S_ISBLK(m) block device? //块设备?
S_ISFIFO(m) FIFO (named pipe)? //管道文件?
//示例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main()
{
struct stat a;
stat("/home/linux",&a);
printf("ctime: %ld,size : %ld, uid : %u \n",a.st_ctime,a.st_size,a.st_uid);
return 0;
}
//带参宏-示例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main()
{
struct stat a;
stat("/home/linux/rand",&a);
if(S_ISDIR(a.st_mode))
printf("It's dir \n");
else if(S_ISREG(a.st_mode))
printf("It's file\n");
else
printf("Not dir \n");
return 0;
}
//练习: 遍历用户工作目录中的所有文件。标明文件类型:
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
DIR * dirp = opendir(".");
if(!dirp)
return -1;
struct stat buf;
struct dirent *readp = readdir(dirp);
if(!readp)
{
perror("readdir");
return -1;
}
while(readp)
{
stat(readp->d_name,&buf);
if(S_ISDIR(buf.st_mode) && readp->d_name[0] != '.')
printf("%8s <---------- dir \n",readp->d_name);
if(S_ISREG(buf.st_mode) && readp->d_name[0] != '.')
printf("%8s : file \n",readp->d_name);
readp = readdir(dirp);
}
closedir(dirp);
}