文件IO
文件IO的特点:
- 文件IO是POXIC(可移植操作系统接口)定义的一组函数
- 不提供缓冲机制,每次读写都会引起系统调用
- 文件描述符,是一个非负整数,文件描述符是0开始的
标准IO默认打开3个文件 stdin 标准输入 0
stdout 标准输出 1
stderr 标准出错 2
4.Linux下,标准IO基于文件IO实现
文件的打开和关闭
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
参数1:文件名(可包含路径)
参数2:打开方式
O_RDONLY 以只读方式打开文件
O_WRONLY 以只写方式打开文件
O_RDWR 以读写方式打开文件
O_CREAT 如果该文件不存在,就创建一个新的文件
并用第三参数为其设置权限
O_TRUNC 如果文件存在,那么打开文件时先删除文件中原有数据
O_EXCL 如果该文件存在,则报错
O_APPEND 以添加方式打开文件,所以对文件的写操作都在文件的末尾进行
O_NOCTTY
第三个参数:文件的权限 0777
返回值:成功返回文件描述符,若失败,则返回-1
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
#include <unistd.h>
int close(int fd);
文件掩码:
0777 0 111 111 111 0 111 111 111
& ~ 000 000 010 & 111 111 101
_____________________________
111 111 101------->775
文件的读写
#include <unistd.h>
参数1:文件描述符
参数2:空间的首地址
参数3:一次性读取多少个字节
返回值:成功返回具体的个数,失败则返回-1,
返回0表示到达文件末尾或无可读取的数据
ssize_t read(int fd, void *buf, size_t count);
函数功能:将buf指向空间中count个字节写入到fd这个文件中
参数1:文件描述符
参数2:首地址
参数3:写入的字节数
返回值:成功具体写入的字节数
ssize_t write(int fd, const void *buf, size_t count);
练习:模拟cp命令 (复制照片)
1------以只读方式打开文件1.png给fr open()
2------以只写方式打开文件2.png给fw open()
3------从fr中读取n个字节给buf read()
4------将读到的n个字节写入到fw中去 write()
5------循环3,4,直到n==0
6------关闭fr,fw close()
#include <stdio.h>
#include "io.h"
#include <string.h>
#define SIZE 20
int main(int argc, const char *argv[])
{
//1.以只读方式打开文件
int fr=0;
fr=open(argv[1],O_RDONLY);
if(0>fr)
{
perror("file open error");
return -1;
}
//2.以只写方式打开文件,文件不存在则新建,文件存在则清空文件内容
int fw=0;
fw=open(argv[2],O_WRONLY|O_CREAT|O_APPEND,0777);
if(0>fw)
{
perror("file open error");
return -1;
}
//3.从文件读取字节
char buf[SIZE]={'\0'};
while(1)
{
memset(buf,'\0',sizeof(buf));
int n=read(fr,buf,SIZE);
if(0==n)
{
break;
}
//4.给文件写入字节
write(fw,buf,sizeof(buf));
}
//5.关闭文件
close(fr);
close(fw);
return 0;
}
文件其他相关函数
#include <sys/types.h>
#include <unistd.h>
函数功能:移动文件指针
参数1:文件描述符
参数2:偏移量
参数3:位置 SEEK_SET
SEEK_CUR
SEEK_END
off_t lseek(int fd, off_t offset, int whence);
空洞文件
1.什么是空洞文件?
“在UNIX文件操作中,文件位移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将延长该文件,并在文件中构成一个空洞,这一点是允许的。位于文件中但没有写过的字节都被设为 0。”
2.空洞文件有什么用?
例如:迅雷下载文件时,在未下载完成时就已经占据了全部文件大小的空间,这时候就是空洞文件。下载的时候如果没有空洞文件,多线程下载时文件就都只能从一个地方写入,这就不是多线程了。如果有了空洞文件,可以从不同的地址写入,就完成了多线程的优势任务。
如何创建一个空洞文件
- 以只写的方式打开该文件
- 移动文件指针,移动1G
- 在末尾写一个字节
- 关闭文件
#include <stdio.h>
#include "io.h"
int main(int argc, const char *argv[])
{
//1.以只写的方式打开该文件,文件不存在则新建,文件存在,则清空文件内容
int fw=0;
fw=open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,0777);
if(0>fw)
{
perror("opne error");
return -1;
}
//2.移动文件指针,移动1G
lseek(fw,1024*1024*1024,SEEK_SET);
//3.在文件末尾写个字节
write(fw,"\0",1);
//4.关闭文件
close(fw);
return 0;
}
目录打开和关闭
#include <sys/types.h>
#include <dirent.h>
参数1: 目录名
返回值: 成功返回DIR *,失败则返回NULL
DIR *opendir(const char *name);
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
目录的读取
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 <dirent.h>
参数1:打开的目录指针
返回值:成功struct dirent *,失败则返回NULL
struct dirent *readdir(DIR *dirp);
ls -a(查看所有以./开头的文件,表示隐藏文件)
#ifndef _DIR_H
#define _DIR_H
//引入库头文件
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
ls(默认查看当前位置)
获得文件的属性
stat/lstat/fstat 获得文件属性
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
fstat参数是文件描述符,不建议使用
int fstat(int fd, struct stat *buf);
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
如果path是符号链接,stat获得是目标文件的属性
lstat获得的是链接文件的属性
参数1:文件名(绝对路径)
参数2: struct stat * 获得文件属性之后,将文件属性的值放到buf所指的空间
返回值:若失败则返回-1
int lstat(const char *path, struct stat *buf);
ls -l(详细显示文件信息)
#include <stdio.h>
以某种格式显示在屏幕上
int printf(const char *format, ...);
printf(“%s/%s”,argv[1],pd->d_name); “/home/linux/1.c“
以某种格式显示到文件中
int fprintf(FILE *stream, const char *format, ...);
fprintf(fp,“%s/%s”,argv[1],pd->d_name);
以某种格式存入到文件中去
char path_name[100];
int sprintf(char *str, const char *format, ...);
sprintf(path_name,”%s/%s”,argv[1],pd->d_name);
struct stat
{
dev_t st_dev; /* ID of device containing file -文件所在设备的ID*/
ino_t st_ino; /* inode number -inode节点号*/
mode_t st_mode; /* protection -保护模式?*/
nlink_t st_nlink; /* number of hard links -链向此文件的连接数(硬连接)*/
uid_t st_uid; /* user ID of owner -user id*/
gid_t st_gid; /* group ID of owner - group id*/
dev_t st_rdev; /* device ID (if special file) -设备号,针对设备文件*/
off_t st_size; /* total size, in bytes -文件大小,字节为单位*/
blksize_t st_blksize; /* blocksize for filesystem I/O -系统块的大小*/
blkcnt_t st_blocks; /* number of blocks allocated -文件所占块数*/
time_t st_atime; /* time of last access -最近存取时间*/
time_t st_mtime; /* time of last modification -最近修改时间*/
time_t st_ctime; /* time of last status change - 最近状态改变时间*/
};
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "dir.h"
int main(int argc, const char *argv[])
{
//1.打开目录
DIR *pdir=NULL;
pdir=opendir(argv[1]);
if(NULL==pdir)
{
perror("dir open error");
return -1;
}
printf("dir open success\n");
//2.目录读取
struct dirent *pd=NULL;
struct tm *pt=NULL;
struct stat buf;
char path_name[100]={'\0'};
while(1)
{
memset(path_name,'\0',sizeof(path_name));
//读取文件夹
pd=readdir(pdir);
if(NULL==pd)
{
break;
}
if(strncmp(pd->d_name,".",1)==0)
{
continue;
}
//printf("%s/%s",argv[1],pd->d_name);
//3.通过文件名获得文件的属性
//参数1:文件名(最好是绝对路径)
//参数2:struct stat *
//返回值:失败则返回-1
sprintf(path_name,"%s/%s",argv[1],pd->d_name);
if(lstat(path_name,&buf)<0)
{
perror("lstat error");
return -1;
}
//printf("lstat success\n");
//显示文件大小
printf("%20ld",buf.st_size);
//获得当地时间
pt=localtime(&buf.st_atime);
//显示当地时间,月 日 时 分
printf(" %02d %02d %02d:%02d",pt->tm_mon+1,pt->tm_mday,pt->tm_hour,pt->tm_min);
//显示文件名字
printf("%20s",pd->d_name);
printf("\n");
}
printf("\n");
//3.关闭目录
closedir(pdir);
return 0;
}