LINUX-文件I/O操作

万物皆文件

文件的概念和类型

文件:一组相关数据的有序集合
文件类型:

类型标识符
块设备文件b
字符设备文件c
目录文件d
常规文件-
符号链接文件l
套接字文件s
管道文件p

不同操作系统支持文件不一

文件I/O

文件I/O:由POSIX(可移植操作系统接口)定义的一些函数

无缓冲,每次读写操作都引起系统调用

特点:

  • 核心概念是文件描述符
  • 可访问各种类型文件
  • 标准I/O基于文件I/O实现

文件描述符

  • 每个打开的文件都对应一个文件描述符
  • 文件描述符是一个非负整数。linux为程序中每个打开的文件分配一个文件描述符
  • 文件描述符从0开始分配,依次递增
  • 文件I/O操作通过文件描述符来完成
标准I/O文件描述符文件描述符文件描述字
标准输入流0STDIN_FILENOstdin
标准输出流1STDOUT_FILENOstdout
标准错误流2STDERR_FILENOstderr

文件的打开和关闭

open函数用来创建或打开一个文件
#include<fcntl.h>
int open(const char *path,int oflag,…);

  • 成功时返回文件描述符;出错时返回EOF
  • 打开文件时使用两个参数
  • 创建文件时第三个参数指定新文件的权限
  • 只能打开设备文件,不能通过open()创建
原形int open(const char*pathname,int flags,mode_t mode);
参数pathname要打开或创建的目标文件(可包括路径名)
O_RDONLY:只读方式打开文件
flagsO_WRONLY:只写方式打开文件这三个参数互斥
O_RDWR:读写方式打开文件
O_CREAT:如果该文件不存在,就创建一个新的文件,并用第三的参数为其设置权限
O_EXCL:如果使用O_CREAT时文件存在,则可返回错误消息,这一参数可测试文件是否存在
O_NOCTTY:使用本参数时,若文件为终端,那么终端不可以作为调用open()系统调用的那个进程的控制终端
O_TRUNC:如果文件已经存在,那么打开文件时先删除文件中的原有数据
O_APPEND:以添加方式打开文件,所有对文件的写操作都在文件末尾进行
mode被打开文件的读取权限,为8进制表示法

close函数用来关闭一个打开的文件
#include<unistd.h>
int close(int fd);

  • 成功返回0;出错返回EOF
  • 程序结束时自动关闭所有打开的文件
  • 文件关闭后,文件描述符不再代表文件

测试

//只写方式创建,不存在创建,存在清空
int fd;
if((fd = open("1.txt",O_WRONLY|O_CREAT|O_TRUNC,0666))<0){
	perror("open");
	return -1;
}
//读写方式创建,不存在创建,存在报错
int fd;
if((df = open("1.txt",O_RDWR|O_CREAT|O_EXCL,0666))<0){
	if(errno == EEXIST){
		perror("exist error")
		}else{
		perror("other error");
		}
}

读取文件

read函数用来从文件中读取数据:
#include<unistd.h>
ssize_t read(int fd,void *buf,size_t count);

  • 成功时返回实际读取的字节数;出错时返回EOF
  • 读到文件末尾时返回0
  • buf是接收数据的缓冲区
  • count应不超过缓冲区大小

测试

从指定的文件(文本文件)中读取内容并统计大小

int main(int argc,char **argv[]){
	int fd,n,total = 0;
	char buf[64];
	if(argc<2){
	printf("Usage: %s<file>\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;
}
	printf("file size is %d\n",total);
	return 0
}

写入文件

write 函数用来向文件写入数据:
#include<unistd.h>
ssize_t write(int fd,void *buf,size_t count);

  • 成功时返回实际写入的字节数;出错时返回EOF
  • buf是发送数据的缓冲区
  • count不应超过buf大小

测试

将键盘输入的内容写入文件,直到输入quit

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<unistd.h>
off_t lseek(int fd,off_t offset,int whence);

  • 成功时返回当前的文件读写位置;出错时返回EOF
  • 参数offset和参数whence同fseek完全一样

测试

利用文件I/O实现文件的复制

#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
#include<errno.h>

#define N 64

int main(int argc,const char **argv){
	int fds,fdt,n;
	char buf[N];
	if(argc<3){
		printf("Usage : %s <src_file> <dst_file>\n",argv[0]);
		return -1;
}
if((fds = open(argv[1],O_RDONLY))==-1){
	fprintf(stderr,"open %s : %s\n",atgv[1],strerror(errno));
	return -1;
}
if((fdt = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0666))==-1){
	fprintf(stderr,"open %s : %s\n",atgv[2],strerror(errno));
	return -1;
}
	while((n = read(fds,buf,N))>0){
		write(fdt,buf,n);
}
	close(fds);
	close(fdt);
	return 0;
}

读取目录

opendir函数用来打开一个目录文件:
#include<dirent.h>
DIR opendir(const charname);

  • DIR是用来描述一个打开的目录文件的结构体类型
  • 成功时返回目录流指针;出错时返回NULL

readdir函数用来读取目录流中的内容:
#include<dirent.h>
struct dirent *readdir(DIR *dirp);

  • struct dirent是用来描述目录流中一个目录项的结构体类型
  • 包含成员char d_name[256]
  • 成功时返回目录流dirp中下一个目录项;出错或到末尾时返回NULL

closedir 函数用来关闭一个目录文件:
#include<dirent.h>
int closedir(DIR *dirp);

  • 成功返回0;失败返回EOF

测试

打印指定的目录下所有文件名称

int main(int 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);
	return 0;
}

修改文件访问权限

chmod/fchmod函数用来修改文件的访问权限:
#include<sys/stat.h>
int chmod(const char *path,mode_t mode);
int fchmod(int fd,mode_t mode);

  • 成功返回0;失败返回EOF
  • root和文件所有者能修改文件的访问权限

获取文件属性

stat/lstat/fstat函数用来获取文件属性:
#include<sys/stat.h>
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是存放文件属性的结构体类型:

类型文件属性
dev_t st_dev文件的设备编号
ino_t st_ino节点
mode_t st_mode类型和访问权限
nlink_t st_nlink连到该文件的硬链接数目,刚建立的文件值为1
uid_t st_uid所有者id
uid_t st_gid用户组id
dev_t st_rdev设备类型(若此文件为设备文件,则为其设备编号)
off_t st_size文件大小
blksize_t st_blksize块大小(文件I/O缓冲区的大小)
blkcnt_t st_blocks占用文件区块的个数
time_t st_atime最后一次访问时间
time_t st_mtime最后一次修改时间
time_t st_ctime文件属性最后改变时间
mode_t st_mode16位short类型含义
S_IFMT0170000//掩码,过滤st_mode中除文件类型以外的信息
S_IFSOCK0140000//套接字
S_IFLNK0120000//符号链接(软链接)
S_IFREG0100000//普通文件
S_IFBLK0060000//块设备
S_IFDIR0040000//目录文件
S_IFCHR0020000//字符设备
S_IFIFO0010000//管道
S_ISUID0004000//设置用户ID
S_ISGID0002000//设置组ID
S_ISVTX0001000//粘住位
S_IRWXU00700//掩码,过滤st_mode除文件所有者权限以外的信息
S_IRUSR00400//用户读权限
S_IWUSR00200//用户写权限
S_IXUSR00100//用户执行权限
S_IRWXG00070//掩码,过滤st_mode除所属组权限以外的信息
S_IRGRP00040//读权限
S_IWGRP00020//写权限
S_IXGRP00010//执行权限
S_IRWXO00007//掩码,过滤st_mode除其他人权限以外的信息
S_IROTH00004//读权限
S_IWOTH00002//写权限
S_IXOTH00001//执行权限

测试

实现ls -l功能

#include<stdio.h>
#include<string.h>	//strcmp隐式调用
#include<unistd.h> //readlink隐式调用
#include<stdlib.h>
#include<sys/stat.h>
#include<linux/fs.h>
#include<time.h>
#include<dirent.h>
#include<errno.h>
#include<grp.h>
#include<pwd.h>
void print_size(struct stat *statp){
	switch(statp->st_mode & S_IFMT){
		case S_IFCHR:
//字符设备
		case S_IFBLK:
//块设备
			printf("%u,%u",(unsigned)(statp->st_rdev >> 8),(unsigned)(statp->st_rdev & 0xFF));
//st_rdev 若此文件为设备文件,则为其设备的编号
			break;
		default:
			printf("%lu",(unsigned long)statp->st_size);
//st_size  文件字节数(文件大小)
	}
}
void print_date(struct stat *statp){
	time_t now;
	double diff;
	char buf[100],*fmt;
	if(time(&now)==-1){
		printf("??????????");
		return;
	}
	diff=difftime(now,statp->st_mtime);
//st_mtime 最后一次修改时间
/*
%Y : 完整年份 (0000..9999) 
%H : 小时(00..23) 
%M : 分钟(00..59)
%b : 月份 (Jan..Dec) 
%e : 短格式天 ( 1..31)
*/
	if(diff<0 || diff>60*60*24*182.5)/*roughly 6 months*/
		fmt = "%b%e %Y";
	else
		fmt = "%b %e %H:%M";
	strftime(buf,sizeof(buf),fmt,localtime(&statp->st_mtime));
	printf("%s ",buf);
}
void printlong(char *name){
	struct stat buf;
	struct passwd *user;
	struct group *grp;
	char linkname[64];
	int rt;
	rt=lstat(name,&buf);
	if(rt==-1){
		perror("in printlong:lstat");
		return;
	}
	switch(buf.st_mode & S_IFMT){
		case S_IFDIR:	printf("d");	break;//目录
		case S_IFLNK:	printf("l");	break;//软连接
		case S_IFREG:	printf("-");	break;//普通文件
		case S_IFBLK:	printf("b");	break;//块设备
		case S_IFCHR:	printf("v");	break;//字符设备
		case S_IFSOCK:	printf("s");	break;//套接字
		case S_IFIFO:	printf("p");	break;//管道
		default:	printf("?");
	}
//OTH其他人,GRP组,USR文件所有者,
	putchar((buf.st_mode & S_IRUSR)?'r':'-');
	putchar((buf.st_mode & S_IWUSR)?'w':'-');
	if(buf.st_mode & S_ISUID)//设置用户ID
		putchar((buf.st_mode & S_IXUSR)?'s':'S');
	else
		putchar((buf.st_mode & S_IXUSR)?'x':'-');
	putchar((buf.st_mode & S_IRGRP)?'r':'-');
	putchar((buf.st_mode & S_IWGRP)?'w':'-');
	if(buf.st_mode & S_ISGID)//设置组ID
		putchar((buf.st_mode & S_IXGRP)?'s':'S');
	else
		putchar((buf.st_mode & S_IXGRP)?'x':'-');
	putchar((buf.st_mode & S_IROTH)?'r':'-');
	putchar((buf.st_mode & S_IWOTH)?'w':'-');
	if(buf.st_mode & S_ISVTX)//S_ISVTX  0001000   设置黏住位
		putchar((buf.st_mode & S_IXOTH)?'t':'T');
	else
		putchar((buf.st_mode & S_IXOTH)?'x':'-');
	printf("%lu ",buf.st_nlink);
// st_uid连到该文件的硬链接数目,新建的文件则硬连接数为 1
	user=getpwuid(buf.st_uid);
//st_uid用户id
	printf("%s ",user->pw_name);
	grp=getgrgid(buf.st_gid);
//st_gid组id
	printf("%s ",grp->gr_name);
	print_size(&buf);
	print_date(&buf);
	if((buf.st_mode & S_IFMT)==S_IFLNK){
		rt=readlink(name,linkname,sizeof(linkname));
		linkname[rt]=0;
		printf("%s->%s ",name,linkname);
	}
	else
		printf("%s ",name);
	puts(" ");
}
int checkfiletype(char *name){
	struct stat buf;
	int typeflag;
/*S_IFMT是文件类型的位掩码(参见man stat)
直接与mystat.st_mode(mystat.st_mode& S_IFMT)进行逐位AND运算意味着只考虑所涉及的位来确定文件类型(常规文件,套接字,块或字符设备等)*/
	lstat(name,&buf);//获取目录的所以信息存储于buf中
	switch(buf.st_mode & S_IFMT){//mode st_mode; /*文件类型和存取的权限*/
		case S_IFREG:	typeflag=1;	break;//普通文件
		case S_IFDIR:	typeflag=2;	break;//目录
		case S_IFLNK:	typeflag=3;	break;//符号链接-软连接
		case S_IFCHR:	typeflag=4;	break;//字符设备
		case S_IFBLK:	typeflag=5;	break;//块设备
		case S_IFSOCK:	typeflag=6;	break;//套接字
		case S_IFIFO:	typeflag=7;	break;//管道
		default:	typeflag=0;
	}
	return typeflag;
}
int main (int argc,char *argv[]){
	int rt=-1;
	if(argc!=3){
	printf("Usage:%s -l filename\n",argv[0]);//判断输入参数是否为3
	exit(1);
	}
	if(strcmp(argv[1],"-l")!=0){
	printf("Usage:%s -l filename\n",argv[0]);//判断第二个参数是否为-l
	exit(1);
	}
	rt=checkfiletype(argv[2]);//判断文件类型
	switch(rt){
		case 0:	printf("unknown file type\n");	exit(1);
		case 1:	
		case 2:
		case 3:	
		case 4:	
		case 5:	
		case 6:
		case 7:	printlong(argv[2]);	return 0;	

	}
	return 0;
}	

例

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值