Linux应用程序设计-03_文件I/O

文件 I/O – 介绍

什么是文件I/O?
posix(可移植操作系统接口)定义的一组函数
不提供缓冲机制,每次读写操作都引起系统调用
核心概念是文件描述符
访问各种类型文件
Linux下, 标准IO基于文件IO实现

文件 I/O – 文件描述符
  1. 每个打开的文件都对应一个文件描述符 文件描述符是一个非负整数,它是一个索引值,并指向在内核中每个进程打开文件的记录数。
  2. 当打开一个现存的文件或创建一个新文件时,内核就向进程返回一个文件描述符;读写文件时,需要把文件描述符作为参数传递给相应的函数。
  3. 文件描述符从0开始分配,依次递增。 文件IO操作通过文件描述符来完成
文件I/O和标准I/O的区别(P20)

文件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(流)表示一个打开的文件,通常只用来访问普通文件

Linux应用程序设计-03_文件I/O_第1张图片

文件IO操作

文件 I/O – 打开和关闭

open()函数语法要点

函数说明:

open()函数用于创建或打开文件,在打开或创建的时可以指定文件打开的方式及文件访问的权限

close()函数用于关闭一个被打开的文件,当一个进程终止时,所有打开的文件都由内核自动关闭。很多程序都利用这一特性而不显式地关闭一个文件。

open函数用来创建或打开一个文件:

#include 
int open(const char *path, int oflag, …);

成功时返回文件描述符;出错时返回EOF
打开文件时使用两个参数
创建文件时第三个参数指定新文件的权限
只能打开设备文件

Linux应用程序设计-03_文件I/O_第2张图片
返回值:
若所有欲核查的权限都通过了检查则返回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”);
    }
}

文件I/O – close

close函数用来关闭一个打开的文件:
#include
int close(int fd);

  1. 调用成功返回0,出错返回-1,并设置errno。
  2. 当一个进程终止时,该进程打开的所有文件都由内核自动关闭。
  3. 关闭一个文件的同时,也释放该进程加在该文件上的所有记录锁。
所需头文件 #include
函数原型 int close(int fd);
函数参数 fd :文件描述符
函数返回值 若文件顺利关闭则返回0, 发生错误时返回-1。
文件 I/O – 读写
函数说明:

read()函数从文件中读取数据存放到缓存区中,并返回实际读取的字节数。

write()函数将数据写入文件中,并返回实际写入的字节数。

函数格式

read()函数语法要点

所需头文件 #include
函数原型 ssize_t read(int fd,void * buf ,size_t count);
函数参数 fd :文件描述符 ; buf:指定存储器读出的数据的缓冲区; count: 指定读出的字节数
函数返回值 成功:读到的字节数 ; 0:已到达文件尾; -1:出错
  1. read()调用成功返回读取的字节数,如果返回0,表示到达文件末尾,如果返回-1,表示出错,通过errno设置错误码。
  2. 读操作从文件的当前位移量处开始,在成功返回之前,该位移量增加实际读取的字节数。
  3. buf是存放读出的内容的缓冲区。

write()函数语法要点

所需头文件 #include
函数原型 ssize_t write(int fd, void *buf, size_t count);
函数参数 fd :文件描述符 ; buf:指定存储器写入数据的缓冲区; count: 指定读出的字节数
函数返回值 成功:已写的字节数 ; -1:出错
  1. write()调用成功返回已写的字节数,失败返回-1,并设置errno。
  2. write()的返回值通常与count不同,因此需要循环将全部待写的数据全部写入文件。
  3. write()出错的常见原因:磁盘已满或者超过了一个给定进程的文件长度限制。
  4. 对于普通文件,写操作从文件的当前位移量处开始,如果在打开文件时,指定了O_APPEND参数,则每次写操作前,将文件位移量设置在文件的当前结尾处,在一次成功的写操作后,该文件的位移量增加实际写的字节数。
文件 I/O – read

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);
}
文件 IO – write

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));
}

文件 IO – lseek

lseek函数用来定位文件:
#include
off_t lseek(int fd, off_t offset, intt whence);
成功时返回当前的文件读写位置;出错时返回EOF
参数offset和参数whence同fseek完全一样

lseek()函数语法要点

所需头文件 #include #include
函数原型 off_t lseek(int fildes,off_t offset ,int whence);
函数参数 fd :文件描述符 ;offset:相对于基准点 whence 的偏移量。以字节为单位,正数表示向前移动,负数表示向后移动
whence:当前位置的基点(SEEK_SET:文件的起始位置; SEEK_CUR:文件当前读写位置; SEEK_END:文件的结束位置)
函数返回值 成功:文件当前读写位置; -1:出错
  1. 每个打开的文件都有一个与其相关的“当前文件位移量”,它是一个非负整数,用以度量从文件开始处计算的字节数。
  2. 通常,读/写操作都从当前文件位移量处开始,在读/写调用成功后,使位移量增加所读或者所写的字节数。
  3. lseek()调用成功为新的文件位移量,失败返回-1,并设置errno。
  4. lseek()只对常规文件有效,对socket、管道、FIFO等进行lseek()操作失败。
  5. lseek()仅将当前文件的位移量记录在内核中,它并不引起任何I/O操作。
  6. 文件位移量可以大于文件的当前长度,在这种情况下,对该文件的写操作会延长文件,并形成空洞。

利用文件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

opendir函数用来打开一个目录文件:
#include
DIR *opendir(const char *name);
DIR是用来描述一个打开的目录文件的结构体类型
成功时返回目录流指针;出错时返回NULL

定义:

DIR * opendir(const char * name);

表头文件:

#include
#include

说明:
opendir()用来打开参数name指定的目录, 并返回DIR*形态的目录流, 和open()类似, 接下来对目录的读取和搜索都要使用此返回值。

返回值
成功则返回DIR* 型态的目录流, 打开失败则返回NULL。

访问目录 – readdir

readdir函数用来读取目录流中的内容:

#include 
struct dirent *readdir(DIR *dirp);

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

访问目录 – closedir

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

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

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;
}

你可能感兴趣的:(Linux应用程序设计,Linux-IO)