Presentation is loading. Please wait.

Presentation is loading. Please wait.

第 5 章 文件I/O操作.

Similar presentations


Presentation on theme: "第 5 章 文件I/O操作."— Presentation transcript:

1 第 5 章 文件I/O操作

2 本章重点 Linux系统的文件属性 不带缓存的文件I/O操作 基于流的文件I/O操作 特殊文件的操作 2

3 5.1 Linux系统文件和文件系统 文件是指有名字的一组相关信息的集合。文件系统是操作系统用来管理和保存文件的。
Linux把不同文件系统挂载(mount)在根文件系统下不同的子目录(挂载点)上,用户可以从根(/)开始方便找到存放在不同文件系统的文件。 在安装Linux系统时,系统会建立一些默认的目录,而每个目录都有其特殊功能。

4 5.1 Linux系统文件和文件系统 linux文件系统结构

5 Linux文件类型 Linux文件类型分为普通文件、目录文件、符号链接(symbolic link)文件、设备文件、管道文件、socket文件等。 例5.1 设计一个程序,要求列出当前目录下的文件信息,以及系统“/dev/sda1”和“/dev/lp0”的文件信息。 步骤1 编辑源程序代码。 root]#vi 5-1.c

6 Linux文件类型

7 5.1.1 Linux文件类型 步骤2 用gcc编译程序。 步骤3 运行程序。
root]#gcc 5-1.c –o 5-1 步骤3 运行程序。 root]#./5-1 列出当前目录下的文件信息: -rwxr-xr-x 2 root root 月 11 04:22 file01 -rw-r--r root root 月 11 04:21 file02 drwxr-xr-x 7 root root 月 11 04:21 file03 lrwxrwxrwx 1 root root 月 11 04:20 file04-> etc/passwd -rwxr-xr-x 2 root root 月 11 04:22 file05 列出“/dev/sda1”的文件信息: brw-rw root disk , /dev/sda1 列出“/dev/ lp0”的文件信息: crw-rw root lp , /dev/lp0

8 Linux文件类型 用ls命令长列表显示文件类型含义如表5.1所示

9 Linux文件类型 system函数说明 :

10 5.1.2 文件权限 Linux系统是一个多用户系统。为了保护系统中文件的安全,Linux统对不同用户访问同一文件的权限做了不同的规定。
文件权限 Linux系统是一个多用户系统。为了保护系统中文件的安全,Linux统对不同用户访问同一文件的权限做了不同的规定。 对于Linux系统中的文件来说,它的权限可以分为四种:可读取(Read)、可写入(Write)、可执行(eXecute)和无权限,分别用r、w、x和-表示。 例5.2 设计一个程序,要求把系统中“/etc”目录下的passwd文件权限,设置成文件所有者可读可写,所有其他用户为只读权限。 10

11 5.1.2 文件权限 步骤1 编辑源程序代码。 [root@localhost root]#vi 5-2.c 程序代码如下:
文件权限 步骤1 编辑源程序代码。 root]#vi 5-2.c 程序代码如下: /*5-2.c 设置“/etc/passwd”文件权限*/ #include<sys/types.h> /*文件预处理,包含chmod函数库*/ #include<sys/stat.h> /*文件预处理,包含chmod函数库*/ int main () /*C程序的主函数,开始入口*/ { chmod("/etc/passwd",S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); /*S_IRUSR表示拥有者具有读权限,S_IRGRP表示组内人具有读权限,S_IROTH表示其他人具有读权限*/ return 0; }

12 5.1.2 文件权限 步骤2 用gcc编译程序。 步骤3 运行程序。 步骤4 使用ls –l命令来查看“/etc/passwd”文件的权限。
文件权限 步骤2 用gcc编译程序。 root]#gcc 5-2.c –o 5-2 步骤3 运行程序。 root]#./5-2 如果程序没有出错,此时系统中没有任何显示。 步骤4 使用ls –l命令来查看“/etc/passwd”文件的权限。 root]#ls -l /etc/passwd -rw-r--r root root 月 15 00:20 /etc/passwd

13 5.1.2 文件权限 例5.3 设计一个程序,要求设置系统文件与目录的权限掩码。
文件权限 例5.3 设计一个程序,要求设置系统文件与目录的权限掩码。 分析 先将系统的权限掩码改为0666(指建立文件时预设的权限为0000),然后调用touch命令新建文件liu1;接着将系统的权限掩码设为0444(指建立文件时预设的权限为0222),然后调用touch命令新建文件liu2;最后调用ls命令观察这些文件的权限,是否按题意的要求已实现。 步骤1 编辑源程序代码。 root]#vi 5-3.c

14 5.1.2 文件权限 步骤2 用gcc编译程序。 步骤3 运行程序。
文件权限 步骤2 用gcc编译程序。 root]#gcc 5-3.c –o 5-3 步骤3 运行程序。 root]#./5-3 系统原来的权限掩码是:123 系统新的权限掩码是:666 创建了文件liu1 系统原来的权限掩码是:666 系统新的权限掩码是:444 创建了文件liu2 root root 月 17 20:59 liu1 --w--w--w root root 月 17 20:59 liu2

15 文件权限 上述结果说明如下: 1)先将系统的权限掩码为0666,所以新建的文件liu1访问权限为0000,即“ ”。 2)再将系统的权限掩码为0444,所以新建的文件liu2访问权限为0222,即“--w--w--w-”。 语句system("touch liu1")的作用是调用system函数来运行shell命令“touch liu1”,touch命令的作用是更改时间标记,若文件不存在,则新建文件。 运行一次此例的程序后,修改源程序中的掩码后,再次编译运行,文件“liu1”和“liu2”的权限并不改变。因为如果文件已经存在,touch只修改时间标记。如果要再次验证新的掩码,需要再次运行程序前删除原来的文件。

16 文件权限 chmod函数说明 :

17 文件权限 mode参数说明 :

18 文件权限 umask函数说明 : 思考题:设计一个程序,要求Linux系统新建的文件权限是0400,提示umask中的参数设置为0266。

19 5.1.3 Linux文件的其他属性 在Linux系统中,文件还有创建时间、大小等其他的属性。这些信息定义在stat结构体中。
struct stat { dev_t st_dev; /*文件所在设备的ID*/ ino_t st_ino; /*索引节点号*/ mode_t st_mode; /*文件保护模式*/ nlink_t st_nlink; /*文件的连接数(硬连接)*/ uid_t st_uid; /*用户ID*/ gid_t st_gid; /*组ID*/ dev_t st_rdev; /*设备号,针对设备文件*/ off_t st_size; /*文件字节数*/ unsigned long st_blksize; /*系统块的大小*/ unsigned long st_blocks; /*文件所占块数 */ time_t st_atime; /*最后一次访问时间*/ time_t st_mtime; /*最后一次修改时间*/ time_t st_ctime; /*最后一次改变时间(指属性)*/ };

20 5.1.3 Linux文件的其他属性 要获得文件的其他属性,可以使用stat函数、fstat或lstat函数。
fstat函数返回与打开的文件描述符相关的文件的状态信息,该信息将会写到stat结构中,stat的地址以参数形式传递给fstat。 stat和lstat返回的是通过文件名查到的状态信息。它们的结果基本一致,但当文件是一个符号链接时,lstat返回的是该符号链接本身的信息,而stat返回的是该链接指向的文件的信息。

21 5.1.3 Linux文件的其他属性 例5.4 设计一个程序,应用系统函数stat获取系统中“/etc”目录下的passwd文件的大小。
步骤1 编辑源程序代码。 root]#vi 5-4.c 程序代码如下: /*5-4.c 获取“/etc/passwd”文件的大小*/ #include<unistd.h> /*文件预处理,包含stat函数库*/ #include<sys/stat.h> /*文件预处理,包含stat函数库*/ int main () /*C程序的主函数,开始入口*/ { struct stat buf; stat("/etc/passwd",&buf); printf("“/etc/passwd”文件的大小是:%d\n",buf.st_size); return 0; }

22 5.1.3 Linux文件的其他属性 步骤2 用gcc编译程序。 步骤3 运行程序。
root]#gcc 5-4.c –o 5-4 步骤3 运行程序。 root]#./5-4 “/etc/passwd”文件的大小是:1635 由结果可知,运行此程序后,在没有打开文件“/etc/passwd”的情况下,通过stat函数取得了文件大小。

23 Linux文件的其他属性 stat函数说明 :

24 5.1.3 Linux文件的其他属性 思考题: 设计一个程序,要求判断“/etc/passwd”的文件类型。
使用st_mode属性,可以使用几个宏来判断:S_ISLNK(st_mode) 是否是一个连接,S_ISREG是否是一个常规文件S_ISDIR是否是一个目录,S_ISCHR是否是一个字符设备,S_ISBLK是否是一个块设备,S_ISFIFO是否是一个FIFO文件,S_ISSOCK是否是一个SOCKET文件。 设计一个程序,要求打开文件“/etc/passwd”,判断它的最后一次访问时间。 应用命令:man fstat,查阅文件状态相关的应用: int stat(const char *file_name, struct stat *buf); int fstat(int filedes, struct stat *buf); int lstat(const char *file_name, struct stat *buf); 请查阅相关资料,如何利用结构体struct stat成员获取文件类型。

25 5.2 不带缓存的文件I/O操作 Linux系统把目录、设备等的操作,都等同于文件的操作。Linux系统通过一个文件描述符来进行区分引用。文件描述符是一个非负的整数,是一个索引值,指向内核中每个进程打开文件表。 Linux系统中,基于文件描述符的文件操作主要有:不带缓存的文件I/O操作和带缓存的文件流I/O操作。 不带缓存的文件I/O操作是系统调用或API的I/O操作,由操作系统提供的,符合POSIX标准,设计的程序能在各种支持POSIX标准的操作系统中方便地移植。 不带缓存的文件I/O程序不能移植到非POSIX标准的系统(如Windows系统)上去,但是在嵌入式程序设计、TCP/IP的Socket套接字程序设计、多路I/O操作程序设计等方面应用广泛。

26 5.2 不带缓存的文件I/O操作

27 5.2.1 文件的创建 在Linux C程序设计中,创建文件可以调用creat函数。
文件的创建 在Linux C程序设计中,创建文件可以调用creat函数。 例5.5 设计一个程序,要求在“/root”目录下创建一个名称为“5-5file”的文件,并且把此文件的权限设置为所有者具有只读权限,最后显示此文件的信息。 步骤1 编辑源程序代码。 root]#vi 5-5.c

28 5.2.1 文件的创建 程序代码如下: /*5-5.c程序:在“/root”目录下创建一个名称为“5-5file”的文件*/
文件的创建 程序代码如下: /*5-5.c程序:在“/root”目录下创建一个名称为“5-5file”的文件*/ #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> int main() { int fd; fd=creat("/root/5-5file",S_IRUSR); /*所有者具有只读权限 */ system("ls /root/5-5file -l");/*调用system函数执行命令ls显示此文件的信息 */ return 0; }

29 5.2.1 文件的创建 步骤2 用gcc编译程序。 步骤3 运行程序。
文件的创建 步骤2 用gcc编译程序。 root]#gcc 5-5.c –o 5-5 步骤3 运行程序。 root]#./5-5 -r root root 月 17 22:00 /root/5-5file

30 文件的创建 creat函数说明 : 思考题:设计一个程序,要求在“/mnt”目录下创建一个名称为“usb”的文件,编辑、调试成功后,运行两次是否有问题?为什么?

31 文件的打开和关闭 文件的打开可以用open函数,即使原来的文件不存在,也可以用open函数创建文件。在打开或者创建文件时,可以指定文件的属性及用户的权限等参数。 关闭一个打开的文件,用close函数。当一个进程终止时,它所有已打开的文件都由内核自动关闭。 例5.6 设计一个程序,要求在“/root”下以可读写方式打开一个名为“5-6file”的文件。如果该文件不存在,则创建此文件;如果存在,将文件清空后关闭。 步骤1 编辑源程序代码。 root]#vi 5-6.c

32 文件的打开和关闭

33 5.2.2 文件的打开和关闭 步骤2 用gcc编译程序。 步骤3 运行程序。
文件的打开和关闭 步骤2 用gcc编译程序。 root]#gcc 5-6.c –o 5-6 步骤3 运行程序。 root]# ./5-6 打开(创建)文件“5-6file”,文件描述符为:3 -rw root root 月 17 22:07 /root/5-6file

34 文件的打开和关闭 open函数说明:

35 文件的打开和关闭 flags参数说明 :

36 文件的打开和关闭 close函数说明 : 思考题:设计一个程序,要求在“/mnt”目录下打开名称为“usb”的文件。如果该文件不存在,则创建此文件;如果存在,将文件清空后关闭。

37 5.2.3 文件的读写操作 文件读写操作中,经常用到的函数是read、write和lseek。
文件的读写操作 文件读写操作中,经常用到的函数是read、write和lseek。 read函数用于将指定的文件描述符中读出数据。 write函数用于向打开的文件写数据,写操作从文件当前位置开始。 lseek函数用于在指定的文件描述符中将文件指针定位到相应的位置。

38 5.2.3 文件的读写操作 例5.7 程序从终端读数据再写回终端。 #include <unistd.h>
文件的读写操作 例5.7 程序从终端读数据再写回终端。 #include <unistd.h> #include <stdlib.h> int main(void) { char buf[80]; int n; n = read(STDIN_FILENO, buf,80); if (n < 0) { perror("read STDIN_FILENO"); exit(1); } write(STDOUT_FILENO, buf, n); printf("\n"); return 0;

39 文件的读写操作 例5.8 设计一个C程序,完成文件的复制工作。要求通过使用read函数和write函数复制“/etc/passwd”文件到目标文件中,目标文件名在程序运行时从键盘输入。 分析 由用户输入目标文件的名称,接着打开源文件“/etc/passwd”及目标文件,利用read函数读取源文件的内容,再利用write函数将读取到的内容写入至目标文件。 步骤1 编辑源程序代码。 root]#vi 5-8.c

40 文件的读写操作

41 文件的读写操作

42 5.2.3 文件的读写操作 步骤2 用gcc编译程序。 步骤3 运行程序。
文件的读写操作 步骤2 用gcc编译程序。 root]#gcc 5-8.c –o 5-8 步骤3 运行程序。 root]#./5-8 请输入目标文件名:5-8test 复制"/etc/passwd"文件为"5-8test"文件成功!

43 文件的读写操作 read函数说明 :

44 文件的读写操作 write函数说明 :

45 文件的读写操作 思考题: 1.设计一个程序,使用read函数从源文件读取数据,再用write函数写入到目标文件,源文件名和目标文件名都由键盘输入。 2.设计一个程序,要求在“/mnt”目录下,打开名称为“usb”的文件,如果该文件不存在,则创建此文件,如果已存在,把字符串“usb作为优盘设备文件”写入此文件后关闭。

46 文件的读写操作 如果在open一个设备时指定了O_NONBLOCK标志,read/write就不会阻塞。以read为例,如果设备暂时没有数据可读就返回-1,同时置errno为EWOULDBLOCK,表示本来应该阻塞但事实上并没有阻塞而是直接返回错误,通过轮询方式试着再读一次,而不是阻塞在这里死等,这样可以同时监视多个设备。 在使用非阻塞I/O时,通常不会在一个while循环中一直不停地查询(这称为Tight Loop),而是每延迟等待一会儿来查询一下,以免做太多无用功,在延迟等待的时候可以调度其它进程执行。

47 文件的读写操作 例5.9 以下是一个非阻塞I/O的例子,程序打开当前终端文件/dev/tty,在打开时指定O_NONBLOCK标志。程序运行时每隔一定时间(6秒)等待用户从终端输入,等待30秒,每次等待时屏幕都有提示“”,30秒后程序继续执行主程序,输出以下图形后结束。

48 文件的读写操作

49 文件的读写操作

50 文件的读写操作 程序运行时,轮询等待用户的输入,等待期间如有输入,即转入主程序执行,如没有输入,30秒后执行主程序,执行结果如下: root]# ./5-9 try again timeout * * * * * * * * * * * * * * * root]# ./5-9 try again ls -l * * * * * * * * * * * * * * *

51 文件上锁 可以用fcntl函数改变一个已打开的文件的属性,可以重新设置读、写、追加、非阻塞等标志,使用函数fcntl通过F_GETFL、F_SETFL可以分别用于读取、设置文件的属性,能够更改的文件标志有O_APPEND,O_ASYNC, O_DIRECT, O_NOATIME 和 O_NONBLOCK。 1、获取文件的flags,即open函数的第二个参数: flags = fcntl(fd,F_GETFL,0); 2、设置文件的flags: fcntl(fd,F_SETFL,flags); 3、增加文件的某个flags,比如文件是阻塞的,想设置成非阻塞: flags |= O_NONBLOCK; 4、取消文件的某个flags,比如文件是非阻塞的,想设置成为阻塞: flags &= ~O_NONBLOCK;

52 5.2.4 文件上锁 例5.10 应用函数fcntl获取和设置文件flags举例。 #include <stdio.h>
#include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <error.h> char buf[500000]; int main(int argc,char *argv[]) { int ntowrite,nwrite; const char *ptr ; int flags; ntowrite = read(STDIN_FILENO,buf,sizeof(buf)); if(ntowrite <0) perror("read STDIN_FILENO fail:"); exit(1); } fprintf(stderr,"read %d bytes\n",ntowrite); 文件上锁

53 5.2.4 文件上锁 例5.10 应用函数fcntl获取和设置文件flags举例。(继)
if((flags = fcntl(STDOUT_FILENO,F_GETFL,0))==-1) { perror("fcntl F_GETFL fail:"); exit(1); } flags |= O_NONBLOCK; if(fcntl(STDOUT_FILENO,F_SETFL,flags)==-1) perror("fcntl F_SETFL fail:"); ptr = buf; while(ntowrite > 0) nwrite = write(STDOUT_FILENO,ptr,ntowrite); if(nwrite == -1) perror("write file fail:"); 文件上锁

54 5.2.4 文件上锁 例5.10 应用函数fcntl获取和设置文件flags举例。(继) if(nwrite > 0) {
ptr += nwrite; ntowrite -= nwrite; } flags &= ~O_NONBLOCK; if(fcntl(STDOUT_FILENO,F_SETFL,flags)==-1) perror("fcntl F_SETFL fail2:"); return 0; 文件上锁

55 文件上锁 例5.11 设计一个程序,要求在“/root”下打开一个名为“5-11file”的文件,如果该文件不存在,则创建此文件。打开后对其加上强制性的写入锁F_WRLCK,按回车后解锁F_UNLCK,然后加上读出锁F_RDLCK,按回车后再解锁F_UNLCK。程序在终端1运行后会显示程序的进程号,再打开终端2,会提示此文件处于锁定状态,此时在终端2可以多按回车,观察程序的运行结果。然后在终端1按回车,等待终端1解锁后,在终端2才可锁定此文件,你可观察到强制性锁是独占状态,当在终端2解锁后,在终端1或2可加读出锁,在读出锁状态终端1或2的运行不需要等待,因为读出锁是处于共享状态,请编写程序并测试程序运行的结果。

56 文件上锁 分析 主程序先用open函数打开文件“5-11file”,如果该文件不存在,则创建此文件;接着调用自定义函数lock_set:先传递参数“F_WRLCK”给文件“5-11file”加锁,并打印输出给文件加锁进程的进程号,然后先传递参数“F_UNLCK”给文件“5-11file”解锁,并打印输出给文件解锁进程的进程号;在自定义函数lock_set给文件上锁语句前,加上判断文件是否上锁的语句,如果文件已经被上锁,打印输出给文件上锁进程的进程号。

57 5.2.4 文件上锁 步骤1 编辑源程序代码。 [root@localhost root]#vi 5-11.c 程序代码如下:
文件上锁 步骤1 编辑源程序代码。 root]#vi c 程序代码如下: /*5-11.c程序:打开“/home/5-11file”后对其加上强制性的写入锁,然后释放写入锁*/ #include<stdio.h> #include<stdlib.h> #include <unistd.h> #include <sys/file.h> #include <sys/types.h> #include <sys/stat.h> void lock_set(int fd, int type) { struct flock lock; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len =0; while(1){ lock.l_type = type;

58 5.2.4 文件上锁 if((fcntl(fd,F_SETLK,&lock))==0){/*根据不同的type值给文件加锁或解锁*/
文件上锁 if((fcntl(fd,F_SETLK,&lock))==0){/*根据不同的type值给文件加锁或解锁*/ if( lock.l_type == F_RDLCK ) /*F_RDLCK为共享锁,表示读取锁或建议性锁*/ printf("加上读取锁的是: %d\n",getpid()); else if( lock.l_type == F_WRLCK ) /*F_WRLCK为排斥锁,表示强制性锁*/ printf("加上写入锁的是: %d\n",getpid()); else if( lock.l_type == F_UNLCK ) printf("释放强制性锁: %d\n",getpid()); return; } fcntl(fd, F_GETLK,&lock); /*读取文件锁的状态*/ if(lock.l_type != F_UNLCK){ if( lock.l_type == F_RDLCK ) printf("文件已经加上了读取锁,其进程号是: %d\n",lock.l_pid); else if( lock.l_type == F_WRLCK ) printf("文件已加上写入锁,其进程号是: %d\n",lock.l_pid); getchar(); }}}

59 5.2.4 文件上锁 int main () { int fd;
文件上锁 int main () { int fd; fd=open("/root/5-11file",O_RDWR | O_CREAT, 0666); if(fd < 0) perror("打开出错"); exit(1); } lock_set(fd, F_WRLCK); getchar(); lock_set(fd, F_UNLCK); lock_set(fd, F_RDLCK); close(fd); exit(0);

60 5.2.4 文件上锁 步骤2 用gcc编译程序。 步骤3 运行程序。
文件上锁 步骤2 用gcc编译程序。 root]#gcc c –o 5-11 步骤3 运行程序。 终端1: root]# ./5-11 加上写入锁的是: 5403 释放强制性锁: 5403 文件已加上写入锁,其进程号是: 5404 加上读取锁的是: 5403

61 5.2.4 文件上锁 终端2: [root@localhost root]# ./5-8 文件已加上写入锁,其进程号是: 5403
文件上锁 终端2: root]# ./5-8 文件已加上写入锁,其进程号是: 5403 加上写入锁的是: 5404 释放强制性锁: 5404 加上读取锁的是: 5404

62 文件上锁 flock函数说明 :

63 文件上锁 fcntl函数说明 : 思考题:设计一个程序,要求在“/mnt”目录下,打开名称为“usb”的文件,如果该文件不存在,则创建此文件。打开后对其加上强制性的写入锁,然后释放写入锁。

64 5.3 带缓存的流文件I/O操作 带缓存的流文件I/O操作,是在内存开辟一个“缓存区”,为程序中的每一个文件使用。
带缓存的文件I/O操作,又称标准I/O操作,符合ANSI C标准,由C库提供这些函数中。设计的程序比不带缓存的文件I/O程序方便移植。 当执行读文件的操作时,从磁盘文件将数据先读入内存“缓存区”,装满后再从内存“缓存区”依此读入接收的变量。当执行写文件的操作时,先将数据写入内存“缓存区”,待内存“缓存区”装满后再写入文件。 由此可以看出,内存“缓存区”的大小,影响着实际操作外存的次数,内存“缓存区”越大,则操作外存的次数就少,执行速度就快、效率高。

65 5.3 带缓存的流文件I/O操作

66 流文件的打开和关闭 带缓存的流文件I/O操作,是基于输入/输出(I/O)流机制的文件操作,又做文件流(File Stream)的操作。下面具体说明文件流的关闭与打开。 例5.12 设计一个程序,要求用流文件I/O操作打开文件“5-12file”, 如果该文件不存在,则创建此文件。 分析 带缓存的基于输入/输出(I/O)流机制的文件操作时,打开文件用fopen函数,关闭文件用fclose函数。

67 5.3.1 流文件的打开和关闭 步骤1 编辑源程序代码。 [root@localhost root]#vi 5-12.c 程序代码如下:
流文件的打开和关闭 步骤1 编辑源程序代码。 root]#vi c 程序代码如下: /*5-12.c程序:用fopen函数打开文件“5-12file”*/ #include<stdio.h> int main() { FILE * fp; /*定义文件变量指针*/ if((fp=fopen("5-12file","a+"))==NULL) /*打开(创建)文件*/ printf("打开(创建)文件出错"); /*出错处理*/ exit(0); } fclose(fp); /*关闭文件流*/

68 5.3.1 流文件的打开和关闭 步骤2 用gcc编译程序。 步骤3 运行程序。
流文件的打开和关闭 步骤2 用gcc编译程序。 root]#gcc c –o 5-12 步骤3 运行程序。 root]#./5-12 root]#ls –l 5-12file -rw-r--r root root 月 17 15: file 由程序运行前后比较,此程序确实可以打开(创建)文件“5-12file”。

69 流文件的打开和关闭 fopen函数说明:

70 5.3.1 流文件的打开和关闭 fclose函数说明 :
流文件的打开和关闭 fclose函数说明 : 思考题:设计一个程序,要求用带缓存的流文件I/O操作,在“/tmp”目录下,打开名称为“tmpfile”的文件。如果该文件不存在,则创建此文件;如果存在,将文件清空后关闭。

71 5.3.2 流文件的读写操作 当流文件按指定的工作方式打开以后,就可以执行对文件的读和写。
流文件的读写操作 当流文件按指定的工作方式打开以后,就可以执行对文件的读和写。 在Linux系统中,流文件的可按字符读写、按字符串读写和成块的读写。 例5.13 设计一个程序,要求把键盘上输入的字符写入文件“5-13file”, 如果该文件不存在,则创建此文件。 分析 带缓存的基于输入/输出(I/O)流机制的文件操作时,读字符用fgetc函数,写字符用fputc函数。

72 5.3.2 流文件的读写操作 步骤1 编辑源程序代码。 [root@localhost root]#vi 5-13.c 程序代码如下:
流文件的读写操作 步骤1 编辑源程序代码。 root]#vi c 程序代码如下: /*5-13.c程序:把键盘上输入的字符写入文件“5-13file”*/ #include<stdio.h> int main() { FILE * fp; /*定义文件变量指针*/ char ch; if((fp=fopen("5-13file","a+"))==NULL) /*打开(创建)文件*/ printf("打开(创建)文件出错"); /*出错处理*/ exit(0); } printf("请输入要写入文件的一个字符:"); /*提示输入一个字符*/ fputc((ch=fgetc(stdin)),fp); /*把键盘输入的一个字符写入文件*/ fclose(fp); /*关闭文件流*/

73 5.3.2 流文件的读写操作 步骤2 用gcc编译程序。 步骤3 运行程序。
流文件的读写操作 步骤2 用gcc编译程序。 root]#gcc c –o 5-13 步骤3 运行程序。 root]#./5-13 请输入要写入文件的一个字符:a 请输入要写入文件的一个字符:b 可以用编辑器软件打开“5-13file”文件,也可以在终端中用cat命令查看文件内容,发现几次从终端输入的字符都写入了“5-13file”文件。

74 流文件的读写操作 fgetc函数说明 :

75 5.3.2 流文件的读写操作 fputc函数说明 : 思考题:程序运行后,输入的字符如果是半角的中文,文件中能否写入?为什么?
流文件的读写操作 fputc函数说明 : 思考题:程序运行后,输入的字符如果是半角的中文,文件中能否写入?为什么? 思考题:设计一个程序,要求用带缓存的流文件I/O操作,利用fputc函数把键盘上输入的字符串写入文件“/tmp/5-13tmp”,如果该文件不存在,则创建此文件。

76 流文件的读写操作 读字符用fgetc函数,写字符用fputc函数。当需要读写非常多的字符时,只能是把字符读写函数和循环、判断等语句结合起来完成任务,实际上ANSI C提供了fgets函数和fputs函数,用于流文件对字符串的读写操作。 例5.14 设计一个程序,要求把键盘上输入的字符写入文件“5-14file”,如果该文件不存在,则创建此文件。 分析 带缓存的基于输入/输出(I/O)流机制的文件操作时,读字符串用fgets函数,写字符串用fputs函数。

77 5.3.2 流文件的读写操作 步骤1 编辑源程序代码。 [root@localhost root]#vi 5-14.c 程序代码如下:
流文件的读写操作 步骤1 编辑源程序代码。 root]#vi c 程序代码如下: /*5-14.c程序:把键盘上输入的字符写入文件“5-14file”*/ #include<stdio.h> int main() { FILE * fp; /*定义文件变量指针*/ char s[80]; if((fp=fopen("5-14file","a+"))==NULL) /*打开(创建)文件*/ printf("打开(创建)文件出错"); /*出错处理*/ exit(0); } printf("请输入要写入文件的字符串:"); /*提示输入一个字符*/ fputs(fgets(s,80,stdin),fp); /*把键盘输入的字符串写入文件*/ fclose(fp); /*关闭文件流*/

78 5.3.2 流文件的读写操作 步骤2 用gcc编译程序。 步骤3 运行程序。
流文件的读写操作 步骤2 用gcc编译程序。 root]#gcc c –o 5-14 步骤3 运行程序。 root]#./5-14 请输入要写入文件的字符串:测试一下 请输入要写入文件的字符串:This is a test! 可以用编辑器软件打开“5-14file”文件,也可以在终端中用cat命令查看文件内容,发现几次从终端输入的字符串都写入了“5-14file”文件。

79 流文件的读写操作 fgets函数说明:

80 5.3.2 流文件的读写操作 fputs函数说明 : 思考题:
流文件的读写操作 fputs函数说明 : 思考题: 1.设计一个程序,要求用带缓存的流文件I/O操作,把键盘上输入的字符串写入文件“/tmp/5-14tmp”。如果该文件不存在,则创建此文件,多次运行程序多次输入字符串后,文件“/tmp/5-14tmp”中只保存最后一次输入的字符串。 2.设计一个程序,要求用带缓存的流文件I/O操作,把文件“/tmp/5-14tmp”中的内容读取出来,在终端中打印输出。

81 5.3.2 流文件的读写操作 比读写字符串更复杂的操作,一般称作块信息,可以通过fread函数和fwrite函数来完成。
流文件的读写操作 比读写字符串更复杂的操作,一般称作块信息,可以通过fread函数和fwrite函数来完成。 例5.15 设计两个程序,要求一个程序把三个人的姓名和帐号余额信息通过一次流文件I/O操作写入文件“5-15file”,另一个格式输出帐号信息,把每个人的帐号和余额一一对应显示输出。 分析 这个需要读写的信息包含字符串和数字,而且要一一对应输出,把块信息写入文件用fwrite函数,从文件中读取块信息用fread函数。

82 5.3.2 流文件的读写操作 步骤1 编辑源程序代码。 [root@localhost root]#vi 5-15fwrite.c
流文件的读写操作 步骤1 编辑源程序代码。 root]#vi 5-15fwrite.c root]#vi 5-15fread.c

83 5.3.2 流文件的读写操作 步骤2 用gcc编译程序。 步骤3 运行程序。
流文件的读写操作 步骤2 用gcc编译程序。 root]#gcc 5-15fwrite.c –o 5-15fwrite root]#gcc 5-15fread.c –o 5-15fread 步骤3 运行程序。 root]#./5-15fwrite root]#./5-15fread 帐号[0]:张三 余额[0]:12345 帐号[1]:李四 余额[1]:200 帐号[2]:王五 余额[2]:50000 可以用编辑器软件打开“5-15file”文件,也可以在终端中用cat命令查看文件内容,发现几次从终端输入的字符串都写入了“5-15file”文件。

84 流文件的读写操作 fwrite函数说明 :

85 流文件的读写操作 fread函数说明 :

86 5.3.2 流文件的读写操作 思考题: 1.完善例5.15的程序,使得帐号和余额都可以从键盘输入,余额可以输入小数。
流文件的读写操作 思考题: 1.完善例5.15的程序,使得帐号和余额都可以从键盘输入,余额可以输入小数。 2.设计一个程序,要求把一个文本文件“5-15test”中的数据读出,文本文件“5-15test”有两列数据,第一列是帐号(11位整数表示),第二列是帐号余额(double数据类型),两列数据间用逗号隔开,按帐号余额从小到大排序后,把排序后的数据写入文本文件“5-15sort”,帐号要和余额一一对应。

87 文件的定位 在实际问题中常要求只读写文件中某一指定的部分。为了解决这个问题,可移动文件内部的位置指针到需要读写的位置,再进行读写,这种读写称为随机读写。 实现随机读写的关键是要按要求移动位置指针,这称为文件的定位。文件定位移动文件内部位置指针的函数主要有三个,即rewind函数、fseek函数和ftell函数。

88 文件的定位 例5.16 设计一个程序,要求用fopen函数打开系统文件“/etc/passwd”,先把位置指针移动到第10个字符前,再把位置指针移动到文件尾,最后把位置指针移动到文件头,输出三次定位的文件偏移量的值。 分析 先调用fseek函数定位到距文件开头SEEK_SET位移量为10, 调用ftell函数取得文件流的偏移量并输出;然后调用fseek函数定位到距文件尾SEEK_END位移量为0,调用ftell函数取得文件流的偏移量并输出;最后调用rewind函数重设文件流的读写位置为文件开头,调用ftell函数取得文件流的偏移量并输出。

89 5.3.3 文件的定位 步骤1 编辑源程序代码。 [root@localhost root]#vi 5-16.c 程序代码如下:
文件的定位 步骤1 编辑源程序代码。 root]#vi c 程序代码如下: /*5-16.c程序:文件的定位*/ #include<stdio.h> int main() { FILE *stream; stream=fopen("/etc/passwd","r"); fseek(stream,10,SEEK_SET); printf("文件流的偏移量:%d\n",ftell(stream)); fseek(stream,0,SEEK_END); rewind(stream); fclose(stream); return 0; }

90 5.3.3 文件的定位 步骤2 用gcc编译程序。 步骤3 运行程序。
文件的定位 步骤2 用gcc编译程序。 root]#gcc c –o 5-16 步骤3 运行程序。 root]#./5-16 文件流的偏移量:10 文件流的偏移量:1635 文件流的偏移量:0

91 文件的定位 fseek函数说明 :

92 文件的定位 ftell函数说明 :

93 文件的定位 rewind函数说明 : 思考题:设计一个程序,要求从系统文件“/etc/passwd”读取偏移量从100至200之间的字符,写入“/tmp/pass”文件。

94 目录文件的操作 目录文件是Linux中一种比较特殊的文件,它是Linux文件系统结构中骨架,对构成整个树型层次结构的Linux文件系统非常重要。 对目录文件的操作可以使用mkdir函数、opendir函数、closedir函数、readdir函数和scandir函数等。 例5.17 设计一个程序,要求打印系统目录“/etc/rc.d”下所有的文件和子目录的名字。 步骤1 编辑源程序代码。 root]#vi c

95 5.4.1 目录文件的操作 步骤2 用gcc编译程序。 步骤3 运行程序。
目录文件的操作 步骤2 用gcc编译程序。 root]#gcc c –o 5-17 步骤3 运行程序。 root]#./5-17 /etc/rc.d目录中文件或子目录有: . .. init.d rc0.d rc1.d rc2.d rc3.d rc4.d rc5.d rc6.d rc.local rc rc.sysinit

96 目录文件的操作 opendir函数说明 :

97 目录文件的操作 readdir函数说明 :

98 目录文件的操作 closedir函数说明 :

99 5.4.1 目录文件的操作 思考题:设计一个程序,要求读取“/etc”目录下所有的目录结构,并依字母顺序排列。
目录文件的操作 思考题:设计一个程序,要求读取“/etc”目录下所有的目录结构,并依字母顺序排列。 #include<dirent.h> 考虑以下语句: scandir("/etc",&namelist,0,alphasort); 程序段: while(n--) { printf("%s\n", namelist[n]->d_name); free(namelist[n]); }

100 目录文件的操作 例5.18 设计一个程序,要求用递归的方法列出某一目录下的全部文件的大小和文件夹及创建日期,包括子文件和子文件夹。 步骤1 编辑源程序代码。 root]#vi

101 链接文件的操作 Linux操作系统可以通过链接实现文件或目录的共享,链接文件有两种方式:符号链接(软链接)和硬链接。符号链接文件类似于Windows操作系统中的“快捷方式”。 1.符号链接文件 符号链接文件可以跨越不同文件系统。符号链接可以在目录间建立链接;符号链接指向的文件可以被任何编辑器编辑而不会产生不好的影响,只要文件路径名称不变。 符号链接也有缺点,如果链接指向的文件从一个目录移动到另一个目录,就无法通过符号链接访问它。原因是符号链接文件含有源文件在文件结构中的路径信息。建立符号链接文件需要一个索引节点,需要占用空间。

102 链接文件的操作 例5.19 设计一个程序,要求为“/etc/passwd”文件建立符号链接“5-19link”,并查看此链接文件和“/etc/passwd”文件。 步骤1 编辑源程序代码。 root]#vi c 程序代码如下: /*5-19.c程序: 为“/etc/passwd”文件建立符号链接“5-19link”*/ #include<unistd.h> int main() { symlink("/etc/passwd","5-19link"); system("ls -l 5-19link "); system("ls -l /etc/passwd "); }

103 5.4.2 链接文件的操作 步骤2 用gcc编译程序。 步骤3 运行程序。
链接文件的操作 步骤2 用gcc编译程序。 root]#gcc c –o 5-19 步骤3 运行程序。 root]#./5-19 lrwxrwxrwx 1 root root 月 17 23: link -> /etc/passwd -rw-r--r root root 月 15 00:20 /etc/passwd 从程序运行结果看,在新创建的“5-19link”文件代表权限的10个字符中,第一位是“l”, 而且最后显示“-> /etc/passwd”,表明链接目标是“/etc/passwd”文件。可见,确实建立了一个链接文件“5-19link”。

104 链接文件的操作 symlink函数说明 : 思考题:设计一个程序,要求为“/bin”目录文件建立软链接为“bin”,并查看此链接文件和“/bin”目录文件

105 链接文件的操作 2.硬链接文件 硬链接是Linux系统整合文件系统的传统方式。硬链接也存在一些问题和限制,不允许给目录创建硬链接。 硬链接不可以在不同文件系统的文件间建立链接。当你只是在自己目录下的文件间建立链接关系,以主目录作为最高层目录,或者同一个文件系统里与另一个用户目录里的文件建立链接关系,这个限制不成问题。但是,假设/bin目录和用户目录属于不同的文件系统,如果你想在/bin目录下的文件和你的目录里的文件之间建立链接,这种链接是行不通的。

106 链接文件的操作 例5.20 设计一个程序,要求为“/etc/passwd”文件建立硬链接“5-20link”,并查看此链接文件和“/etc/passwd”文件。 步骤1 编辑源程序代码。 root]#vi c 程序代码如下: /*5-20.c程序: 为“/etc/passwd”文件建立硬链接“5-20link”*/ #include<unistd.h> int main() { link("/etc/passwd","5-20link"); system("ls 5-20link -l"); system("ls /etc/passwd -l"); }

107 5.4.2 链接文件的操作 步骤2 用gcc编译程序。 步骤3 运行程序。
链接文件的操作 步骤2 用gcc编译程序。 root]#gcc c –o 5-20 步骤3 运行程序。 root]#./5-20 -rw-r--r root root 月 15 00: link -rw-r--r root root 月 15 00:20 /etc/passwd 从程序运行结果看,在权限的10个字符中,第一位是“-”,表面上看起来像是建立了一个普通文件,除了文件名,新建立的文件跟原文件显示的属性一模一样,而且第二项是“2”,因此,确实建立了一个硬链接文件“5-20link”。

108 链接文件的操作 link函数说明 : 思考题:设计一个程序,要求为“/bin/ls”文件建立硬链接为“ls”,并查看此链接文件和“/bin/ls”文件。

109 思考与实验 设计一个程序,要求打开文件“pass”,如果没有这个文件,新建此文件,权限设置为只有所有者有只读权限。
设计一个程序,要求新建一个文件“hello”,利用write函数将“Linux下C软件设计”字符串写入该文件。 设计一个程序,要求利用read函数读取系统文件“/etc/passwd”,并在终端中显示输出。 设计一个程序,要求打开文件“pass”,如果没有这个文件,新建此文件;读取系统文件“/etc/passwd”,把文件中的内容都写入“pass”文件。 109

110 思考与实验 设计一个程序,要求将10分别以十进制、八进制和十六进制输出。 设计一个程序,要求新建一个目录,预设权限为 ---x--x--x。
设计一个程序,要求为“/bin/ls”文件建立一个软链接“ls1”和一个硬链接为“ls2”,并查看两个链接文件和“/bin/ls”文件。 110


Download ppt "第 5 章 文件I/O操作."

Similar presentations


Ads by Google