1.3目录与文件
1.3.1文件的操作权限
权限:文件系统为了进行安全管理需要在对文件操作时进行用户身份认证。
用C程序进行文件操作时需要设置目录的权限。
Linux中,chmod
命令可以更改文件的权限。如chmod +x a.out
是对一个文件添加可执行权限。
C编程中,需要用三个八进制来表示文件的权限。
- 第一个数字,表示本用户的权限(User)。
- 第二个数字,表示同组用户的权限(Group)。
- 第三个数字,表示其他用户的权限(Other)。
不同值数字的含义:
- 4,表示可读权限,相当于r权限。
- 2,表示可写权限,相当于w权限。
- 1,表示可执行权限,相当于x权限。
如果有多种权限,可把几个权限加起来。
- 7=4+2+1,表示这个文件可读、可写、可执行。
- 5=4+1,表示文件可读、可执行。
- 6=4+2,表示文件可读、可写,但是不可执行。
chmod
命令也可以使这些数字作为参数对一个文件更改权限。chmod 766 a.out
是把一个文件的权限设置为766。
1.3.2错误处理与错误号
进行文件操作时,需要在程序中设置错误捕捉语句并显示错误。错误捕捉和错误输出是应用错误号和strerror函数来实现的。
错误定义的理解
Linux系统已经把所有的错误定义成为不同的错误号和错误常数,程序如果发生了异常,会返回这一个错误的常数。(显示为整型数字,或用strerror来显示为已经定义的错误信息)
vim /usr/include/asm-generic/errno-base.h
打开错误定义文件。
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _ASM_GENERIC_ERRNO_BASE_H
#define _ASM_GENERIC_ERRNO_BASE_H
#define EPERM 1 /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define ESRCH 3 /* No such process */
#define EINTR 4 /* Interrupted system call */
#define EIO 5 /* I/O error */
#define ENXIO 6 /* No such device or address */
#define E2BIG 7 /* Argument list too long */
#define ENOEXEC 8 /* Exec format error */
#define EBADF 9 /* Bad file number */
#define ECHILD 10 /* No child processes */
#define EAGAIN 11 /* Try again */
#define ENOMEM 12 /* Out of memory */
#define EACCES 13 /* Permission denied */
#define EFAULT 14 /* Bad address */
#define ENOTBLK 15 /* Block device required */
#define EBUSY 16 /* Device or resource busy */
#define EEXIST 17 /* File exists */
#define EXDEV 18 /* Cross-device link */
#define ENODEV 19 /* No such device */
#define ENOTDIR 20 /* Not a directory */
#define EISDIR 21 /* Is a directory */
#define EINVAL 22 /* Invalid argument */
#define ENFILE 23 /* File table overflow */
#define EMFILE 24 /* Too many open files */
#define ENOTTY 25 /* Not a typewriter */
#define ETXTBSY 26 /* Text file busy */
#define EFBIG 27 /* File too large */
#define ENOSPC 28 /* No space left on device */
#define ESPIPE 29 /* Illegal seek */
#define EROFS 30 /* Read-only file system */
#define EMLINK 31 /* Too many links */
#define EPIPE 32 /* Broken pipe */
#define EDOM 33 /* Math argument out of domain of func */
#define ERANGE 34 /* Math result not representable */
#endif
vim /usr/include/asm-generic/errno.h
另一个错误定义文件。
* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _ASM_GENERIC_ERRNO_H
#define _ASM_GENERIC_ERRNO_H
#include <asm-generic/errno-base.h>
#define EDEADLK 35 /* Resource deadlock would occur */
#define ENAMETOOLONG 36 /* File name too long */
#define ENOLCK 37 /* No record locks available */
/*
* This error code is special: arch syscall entry code will return
* -ENOSYS if users try to call a syscall that doesn't exist. To keep
* failures of syscalls that really do exist distinguishable from
* failures due to attempts to use a nonexistent syscall, syscall
* implementations should refrain from returning -ENOSYS.
*/
#define ENOSYS 38 /* Invalid system call number */
#define ENOTEMPTY 39 /* Directory not empty */
#define ELOOP 40 /* Too many symbolic links encountered */
#define EWOULDBLOCK EAGAIN /* Operation would block */
#define ENOMSG 42 /* No message of desired type */
#define EIDRM 43 /* Identifier removed */
#define ECHRNG 44 /* Channel number out of range */
#define EL2NSYNC 45 /* Level 2 not synchronized */
#define EL3HLT 46 /* Level 3 halted */
#define EL3RST 47 /* Level 3 reset */
#define ELNRNG 48 /* Link number out of range */
#define EUNATCH 49 /* Protocol driver not attached */
#define ENOCSI 50 /* No CSI structure available */
#define EL2HLT 51 /* Level 2 halted */
#define EBADE 52 /* Invalid exchange */
#define EBADR 53 /* Invalid request descriptor */
#define EXFULL 54 /* Exchange full */
#define ENOANO 55 /* No anode */
#define EBADRQC 56 /* Invalid request code */
#define EBADSLT 57 /* Invalid slot */
#define EDEADLOCK EDEADLK
#define EBFONT 59 /* Bad font file format */
#define ENOSTR 60 /* Device not a stream */
#define ENODATA 61 /* No data available */
#define ETIME 62 /* Timer expired */
#define ENOSR 63 /* Out of streams resources */
#define ENONET 64 /* Machine is not on the network */
#define ENOPKG 65 /* Package not installed */
#define EREMOTE 66 /* Object is remote */
#define ENOLINK 67 /* Link has been severed */
#define EADV 68 /* Advertise error */
#define ESRMNT 69 /* Srmount error */
#define ECOMM 70 /* Communication error on send */
#define EPROTO 71 /* Protocol error */
#define EMULTIHOP 72 /* Multihop attempted */
#define EDOTDOT 73 /* RFS specific error */
#define EBADMSG 74 /* Not a data message */
#define EOVERFLOW 75 /* Value too large for defined data type */
#define ENOTUNIQ 76 /* Name not unique on network */
#define EBADFD 77 /* File descriptor in bad state */
#define EREMCHG 78 /* Remote address changed */
#define ELIBACC 79 /* Can not access a needed shared library */
#define ELIBBAD 80 /* Accessing a corrupted shared library */
#define ELIBSCN 81 /* .lib section in a.out corrupted */
#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
#define ELIBEXEC 83 /* Cannot exec a shared library directly */
#define EILSEQ 84 /* Illegal byte sequence */
#define ERESTART 85 /* Interrupted system call should be restarted */
#define ESTRPIPE 86 /* Streams pipe error */
#define EUSERS 87 /* Too many users */
#define ENOTSOCK 88 /* Socket operation on non-socket */
#define EDESTADDRREQ 89 /* Destination address required */
#define EMSGSIZE 90 /* Message too long */
#define EPROTOTYPE 91 /* Protocol wrong type for socket */
#define ENOPROTOOPT 92 /* Protocol not available */
#define EPROTONOSUPPORT 93 /* Protocol not supported */
#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
#define EPFNOSUPPORT 96 /* Protocol family not supported */
#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
#define EADDRINUSE 98 /* Address already in use */
#define EADDRNOTAVAIL 99 /* Cannot assign requested address */
#define ENETDOWN 100 /* Network is down */
#define ENETUNREACH 101 /* Network is unreachable */
#define ENETRESET 102 /* Network dropped connection because of reset */
#define ECONNABORTED 103 /* Software caused connection abort */
#define ECONNRESET 104 /* Connection reset by peer */
#define ENOBUFS 105 /* No buffer space available */
#define EISCONN 106 /* Transport endpoint is already connected */
#define ENOTCONN 107 /* Transport endpoint is not connected */
#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
#define ETOOMANYREFS 109 /* Too many references: cannot splice */
#define ETIMEDOUT 110 /* Connection timed out */
#define ECONNREFUSED 111 /* Connection refused */
#define EHOSTDOWN 112 /* Host is down */
#define EHOSTUNREACH 113 /* No route to host */
#define EALREADY 114 /* Operation already in progress */
#define EINPROGRESS 115 /* Operation now in progress */
#define ESTALE 116 /* Stale file handle */
#define EUCLEAN 117 /* Structure needs cleaning */
#define ENOTNAM 118 /* Not a XENIX named type file */
#define ENAVAIL 119 /* No XENIX semaphores available */
#define EISNAM 120 /* Is a named type file */
#define EREMOTEIO 121 /* Remote I/O error */
#define EDQUOT 122 /* Quota exceeded */
#define ENOMEDIUM 123 /* No medium found */
#define EMEDIUMTYPE 124 /* Wrong medium type */
#define ECANCELED 125 /* Operation Canceled */
#define ENOKEY 126 /* Required key not available */
#define EKEYEXPIRED 127 /* Key has expired */
#define EKEYREVOKED 128 /* Key has been revoked */
#define EKEYREJECTED 129 /* Key was rejected by service */
/* for robust mutexes */
#define EOWNERDEAD 130 /* Owner died */
#define ENOTRECOVERABLE 131 /* State not recoverable */
#define ERFKILL 132 /* Operation not possible due to RF-kill */
#define EHWPOISON 133 /* Memory page has hardware error */
#endif
从两个文件可知,Linux系统一共定义131种错误常数。用这些错误常数可以返回程序的错误信息。
用错误常数显示错误信息
函数strerror可以把一个错误常数转换成一个提示语句。(程序返回的错误常数是不容易理解的,需要转换成有效的语句)头文件是string.h
。
#include<stdio.h>
#include<string.h>
#include<errno.h>/*包含错误处理头文件*/
int main(void)
{
printf("ENOENT:\n");/*输出提示*/
char *mesg=strerror(ENOENT);/*将错误号转换成一个字符串*/
printf(" Errno :%d\n",ENOENT);/*输出错误号*/
printf(" Message:%s\n",mesg);/*输出错误信息*/
printf("EIO:\n");
char *mesg1=strerror(EIO);
printf(" Errno :%d\n",EIO);
printf(" Message:%s\n",mesg1);
printf("EEXIST:\n");
char *mesg2=strerror(EEXIST);
printf(" Errno :%d\n",EEXIST);
printf(" Message:%s\n",mesg2);
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim cuowuhao.cpp
yang@yang-virtual-machine:~/桌面$ gcc -o cuowuhao.out cuowuhao.cpp
yang@yang-virtual-machine:~/桌面$ ./cuowuhao.out
ENOENT:
Errno :2
Message:No such file or directory#没有相关的文件或文件夹
EIO:
Errno :5
Message:Input/output error#I/O出现错误
EEXIST:
Errno :17
Message:File exists#文件重名
I/O是指计算机的输入和输出,包括一切操作、程序或设备与计算机之间发生的数据流通。
最常见的I/O设备有打印机、硬盘、键盘和鼠标。
用错误序号显示错误信息
函数srerror使用方法:char *strerror(int errnum)
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main(void)
{
int i;
for(i=1;i<=15;i++){
printf("Errno:%d ",i);
printf("Message:%s\n",strerror(i));
}
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim cuowu.cpp
yang@yang-virtual-machine:~/桌面$ gcc -o cuowu.out cuowu.cpp
yang@yang-virtual-machine:~/桌面$ ./cuowu.out
Errno:1 Message:Operation not permitted
Errno:2 Message:No such file or directory
Errno:3 Message:No such process
Errno:4 Message:Interrupted system call
Errno:5 Message:Input/output error
Errno:6 Message:No such device or address
Errno:7 Message:Argument list too long
Errno:8 Message:Exec format error
Errno:9 Message:Bad file descriptor
Errno:10 Message:No child processes
Errno:11 Message:Resource temporarily unavailable
Errno:12 Message:Cannot allocate memory
Errno:13 Message:Permission denied
Errno:14 Message:Bad address
Errno:15 Message:Block device required
1.3.3创建与删除目录
创建目录函数mkdir
函数mkdir可以在硬盘中建立一个目录,相当于mkdir
命令。头文件sys/types.h
和sys/stat.h
。
使用:int mkdir(char *patthname,mode_t mode);
在参数列表中,
- pathname,一个字符型指针,表示要创建的目录路径;
- mode,表示权限的八进制数字。
目录创建成功返回整型数0,否则-1。
在创建目录时,可能返回的错误常数如下:
- EPERM:目录中有不合规则的名字。
- EEXIST:参数pathname所指的目录已存在。
- EFAULT:pathname指向了非法的地址。
- EACCESS:权限不足,不允许创建目录。
- ENAMETOOLONG:参数pathname太长。
- ENOENT:所指的上级目录不存在。
- ENOTDIR:参数pathname不是目录。
- ENOMEM:核心内存不足。
- EROFS:想要创建的目录在只读文件系统内。
- ELOOP:参数pathname有多个符合的链接。
- ENOSPC:磁盘空间不足。
实例:在目录/home/yang/桌面下创建一个tmp11目录。程序中设置一个错误号变量,用于捕捉程序发生的异常,如果创建不成功则输出这个错误。
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<errno.h>
int main()
{
extern int errno;
char *path="/home/yang/桌面/tmp11";
if(mkdir(path,0766)==0)/*0766第一个0表示这里的是八进制*/
{
printf("created the directory %s.\n",path);
}
else{
printf("can't create the directory %s.\n",path);
printf("errno: %d\n",errno);
printf("ERR : %s\n",strerror(errno));
}
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim cjml.c
yang@yang-virtual-machine:~/桌面$ gcc -o cjml.out cjml.c
yang@yang-virtual-machine:~/桌面$ ./cjml.out
created the directory /home/yang/桌面/tmp11.
yang@yang-virtual-machine:~/桌面$ ./cjml.out
can't create the directory /home/yang/桌面/tmp11.
errno: 17
ERR : File exists
删除目录函数rmdir
rmdir函数:删除一个目录。
使用:int rmdir(char *pathname);
参数pathname是需要删除的目录字符串的头指针。删除成功返回0,否则-1。
可能的错误:
- EACCESS:权限不足,不允许删除目录。
- EBUSY:系统繁忙,没有删除。
- EFAULT:pathname指向了非法地址。
- EINVAL:有这个目录,但是不能删除。
- ELLOP:参数pathname有多个符合的链接。
- ENAMETOOLONG:给出的参数太长。
- ENOENT:所指的上级目录不存在。
- ENOMEM:核心内存不足。
- ENOTDIR:不是目录。
- EROFS:指向的目录在一个只读目录里。
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<errno.h>
int main()
{
extern int errno;
char *path="/home/yang/桌面/tmp11";
if(rmdir(path)==0){
printf("deleted the directory %s.\n",path);
}else{
printf("can't delete the directory %s.\n'",path);
printf("errno: %d\n",errno);
printf("ERR : %s\n",strerror(errno));
}
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim scml.c
yang@yang-virtual-machine:~/桌面$ gcc -o scml.out scml.c
scml.c: In function ‘main’:
scml.c:11:12: warning: implicit declaration of function ‘rmdir’ [-Wimplicit-function-declaration]
11 | if(rmdir(path)==0){
| ^~~~~
yang@yang-virtual-machine:~/桌面$ ./scml.out
deleted the directory /home/yang/桌面/tmp11.
yang@yang-virtual-machine:~/桌面$ ./scml.out
can't delete the directory /home/yang/桌面/tmp11.
'errno: 2
ERR : No such file or directory
1.3.4文件的创建与删除
创建文件函数creat
函数creat:在目录中建立一个新文件。
使用:int creat(char *pathname,mode_t mode);
参数pathname表示需要建立的文件名和目录名,mode表示这个文件的权限。文件创建成功返回创建文件的编号,否则返回-1。
头文件:#include<sys/types.h>
、#include<sys/stat.h>
、#include<fcntl.h>
。
可能错误:
- EEXIST:参数pathname所指的文件已存在。
- EACCESS:参数pathname所指的文件不符合所要求测试的权限。
- EROFS:欲打开写入权限的文件存在于只读文件系统内。
- EFAULT:参数pathname指针超出可存取的内存空间。
- EINVAL:参数mode不正确。
- ENAMETOOLONG:参数pathname太长。
- ENOTDIR:参数pathname为一个目录。
- ENOMEM:核心内存不足。
- ELOOP:参数pathname有过多符号链接问题。
- EMFILE:已达到进程可同时打开的文件数上限。
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<fcntl.h>
#include<errno.h>
int main()
{
extern int errno;
char *path="/home/yang/桌面/tmp.txt";
if(creat(path,0766)==-1){
printf("can't create the file %s.\n",path);
printf("errno: %d\n",errno);
printf("ERR : %s\n",strerror(errno));
}else{
printf("created file %s.\n",path);
}
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim jlwj.c
yang@yang-virtual-machine:~/桌面$ gcc -o jlwj.out jlwj.c
yang@yang-virtual-machine:~/桌面$ ./jlwj.out
created file /home/yang/桌面/tmp.txt.
yang@yang-virtual-machine:~/桌面$ ./jlwj.out
created file /home/yang/桌面/tmp.txt.
yang@yang-virtual-machine:~/桌面$ ./jlwj.out
created file /home/yang/桌面/tmp.txt.
与创建目录不同,可以再次运行这个程序,新创建的文件会覆盖以前的文件。
删除文件函数remove
函数remove:删除一个文件。
使用:int remove(char *pathname);
。
参数pathname,字符型指针,表示要删除的文件。成功返回0,否则-1。头文件:#include<stdio.h>
。
错误:
- EACCESS
- EBUSY
- EFAULT
- EINVAL
- ELOOP
- ENAMETOOLONG
- ENOENT
- ENOMEM
- ENOTDIR
- EROFS
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<errno.h>
int main()
{
extern int errno;
char *path="/home/yang/桌面/tmp.txt";
if(remove(path)==0){
printf("Deleted file %s.\n",path);
}else{
printf("can't delete the file %s.\n",path);
printf("errno: %d\n",errno);
printf("ERR : %s\n",strerror(errno));
}
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim scwj.c
yang@yang-virtual-machine:~/桌面$ gcc -o scwj.out scwj.c
yang@yang-virtual-machine:~/桌面$ ./scwj.out
Deleted file /home/yang/桌面/tmp.txt.
yang@yang-virtual-machine:~/桌面$ ./scwj.out
can't delete the file /home/yang/桌面/tmp.txt.
errno: 2
ERR : No such file or directory
建立临时文件函数mkstemp
临时文件:程序运行时为了存储中间数据而建立的文件。计算机重启时,这些文件会自动删除。
如果在程序运行时需要把文件短时间地写到磁盘上,可以使用mkstemp函数创建一个临时文件。
使用:int mkstemp(char *template)
。
参数template表示需要建立临时文件的文件名字符串。文件名字符串中最后六个字符必须时XXXXXX。
mkstemp函数会以可读写模式和0600权限来打开该文件,如果文件不存在则会建立这个文件,返回值是打开文件的编号,如果文件建立不成功则返回-1。
错误可能:
- EEXIST:文件同名错误。
- EINVAL:参数template字符串最后六个字符非XXXXXX。
参数template所指的文件名称字符串必须声明为数组,char template[] = "template-xxxxxx";
,不能是指针。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>
int main()
{
extern int errno;
char path[]="mytemp-XXXXXX";
if(mkstemp(path)!=-1){
printf("created temp file %s.\n",path);
}else{
printf("can't create temp file %s.\n",path);
printf("errno: %d\n",errno);
printf("ERR : %s\n",strerror(errno));
}
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim jllswj.c
yang@yang-virtual-machine:~/桌面$ gcc -o jllswj.out jllswj.c
yang@yang-virtual-machine:~/桌面$ ./jllswj.out
created temp file mytemp-CJnkkD.
yang@yang-virtual-machine:~/桌面$ ./jllswj.out
created temp file mytemp-xGIbO7.
#系统会自动生成一个不同名的文件名来建立临时文件。
1.3.5文件的打开与关闭
文件的打开:从磁盘中找到一个文件,返回一个整型的打开文件顺序编号。打开的文件处于可读、可写状态。
文件的关闭:释放打开的文件,使文件处于不可读写的状态。
打开文件函数open
使用:int open(char *pathname,int flags)
,int open(char *pathname,mode_t mode)
文件打开方式的设置
pathname,表示要打开文件的路径字符串。
flags,是系统定义的一些整型常数,表示文件的打开方式。
- O_RDONLY:以只读方式打开文件。
- O_WRONLY:以只写方式打开文件。
- O_RDWR:以可读写方式打开文件。
上述三种不可同时使用,但可与下列的利用“|”运算符组合:
- O_CREAT:若要打开的文件不存在则自动建立该文件。
- O_EXCL:
- 如果O_CREAT已被设置,此指令会去检查文件是否存在。
- 若不存在则建立该文件,存在则将导致打开文件错误。
- O_CREAT和O_EXCL同时设置时,如果要打开的文件为一个链接,则会打开失败。
- O_NOCTTY:若要打开的文件为终端机设备时,则不会将其当成进程控制终端机。
- O_TRUNC:若文件存在并以可写的方式打开时,此标志会清空文件。原来的文件内容会丢失。
- O_APPEND:以附加的文件打开文件。当读写文件时会从文件尾开始向后移动,写入的数据会以附加的方式加入到文件后面。
- O_NONBLOCK:以不可阻断的方式打开文件(无论文件有无数据读取或等待操作,都会立即打开文件)。
- O_NDELAY:O_NONBLOCK。
- O_SYNC:以同步的方式打开,所有的文件操作不写入到缓存。
- O_NOFOLLOW:若pathname所指文件为一个符号链接,则会打开失败。
- O_DIRECTORY:若pathname所指的文件的目录不存在,则会打开失败。
打开文件的权限
打开文件时,没有这个文件则会自动创建一个文件。新建文件时需要设置新建文件权限。
系统为参数mode定义了以下参数,可以直接使用这些参数来设置文件的权限(这些权限只有在建立新文件时才会有效)。
- S_IRWXU:00700权限,文件所有者可读、可写、可执行。
- S_IRUSR/S_IREAD:00400权限,文件所有者可读取。
- S_IWUSR/S_IWRITE:00200权限,文件所有者可写入。
- S_IXUSR/S_IEXEC:00100权限,文件所有者可执行。
- S_IRWXG:00070权限,该文件用户组可写、可读、可执行。
- S_IRGRP:00040权限,文件用户组可读。
- S_IWGRP:00020权限,文件用户组可写。
- S_IXGRP:00010权限,文件用户组可执行。
- S_IRWXO:00007权限,其他用户组可读、可写、可执行
- S_IROTH:00004权限,其他用户组可读。
- S_IWOTH:00002权限,其他用户组可写。
- S_IXOTH:00001权限,其他用户组可执行。
文件打开实例
open函数头函数
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
可能错误:
- EEXIST:文件已存在,却使用了O_CREAT/O_EXCL。
- EACESS:文件没有打开权限。
- EROFS:欲写入权限的文件存在于只读文件系统内。
- EFAULT: pathname指针超出可存取内存空间。
- EINVAL:参数mode不正确。
- ENAMETOOLONG:pathname太长。
- ENOTDIR:参数pathname不在一个目录中。
- ENOMEM:核心内存不足。
- ELOOP:pathname有过多符号链接问题。
- EIO:I/O存取错误。
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<fcntl.h>
#include<string.h>
#include<errno.h>
int main()
{
int fd,fd1;
char path[]="/home/yang/桌面/txt1.txt";
extern int errno;
fd=open(path,O_WRONLY,0766);
if(fd!=-1){
printf("opened file %s .\n",path);
}else{
printf("can't open file %s.\n",path);
printf("errno: %d\n",errno);
printf("ERR : %s\n",strerror(errno));
}
fd1=open(path,O_WRONLY|O_CREAT,0766);
if(fd1!=-1){
printf("opened file %s .\n",path);
}else{
printf("can't open file %s.\n",path);
printf("errno: %d\n",errno);
printf("ERR : %s\n",strerror(errno));
}
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim dkwj.c
yang@yang-virtual-machine:~/桌面$ gcc -o dkwj.out dkwj.c
yang@yang-virtual-machine:~/桌面$ ./dkwj.out
can't open file /home/yang/桌面/txt1.txt.
errno: 2
ERR : No such file or directory
opened file /home/yang/桌面/txt1.txt .
第一次只读方式打开文件,没有文件时显示错误。第二次加入了O_CREAT,没有文件时新建,打开成功。
关闭文件函数close
作用:关闭已打开文件,会让数据写回磁盘,并释放该文件所占的资源。
使用:int close(int fd)
。头文件:#include<unistd.h>
。
参数fd,是用open函数打开文件时返回的打开序号。成功关闭返回0,失败返回-1。
虽然,进程结束时,系统会自动关闭已打开的文件,但最好在程序中关闭文件并检查返回值是否正确。
关闭时,可能返回EBADF错误,表示需要关闭的文件号不存在。
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
int main()
{
int fd;
char path[]="/home/yang/桌面/txt1.txt";
extern int errno;
fd=open(path,O_WRONLY|O_CREAT,0766);
if(fd!=-1){
printf("opened file %s .\n",path);
}else{
printf("can't open file %s.\n",path);
printf("errno: %d\n",errno);
printf("ERR : %s\n",strerror(errno));
}
if(close(fd)==0){
printf("closed.\n");
}else{
printf("close file %s error.\n",path);
printf("errno: %d\n",errno);
printf("ERR : %s\n",strerror(errno));
}
if(close(1156)==0){
printf("closed.\n");
}else{
printf("close file %s error.\n",path);
printf("errno: %d\n",errno);
printf("ERR : %s\n",strerror(errno));
}
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim gbwj.c
yang@yang-virtual-machine:~/桌面$ gcc -o gbwj.out gbwj.c
yang@yang-virtual-machine:~/桌面$ ./gbwj.out
opened file /home/yang/桌面/txt1.txt .
closed.
close file /home/yang/桌面/txt1.txt error.
errno: 9
ERR : Bad file descriptor
1.3.6文件读写
在文件中写字符串函数write
函数write可以写入一个字符串。
使用:ssize_t write(int fd,void * buf,size_t count);
。
-
fd,已经打开文件的文件号。
-
buf,需要写入的字符串。
-
count,一个整型数,表示需要写入的字符个数。
-
size_t,一个相当于整型的数据类型,表示需要写入的字节的数目。
将字符串写入文件以后,文件的写位置也会移动。
写入成功,会返回实际写入的字节数。发生错误时返回-1。
可能错误:
- EINTR:此操作被其他操作中断。
- EAGAIN:当前打开文件是不可写的方式打开的,不能写入文件。
- EADF:参数fd不是有效的文件编号,或该文件已关闭。
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
int main()
{
int fd;
char path[]="/home/yang/桌面/txt1.txt";
char s[]="hello ,Linux.\nI've leart C program for two weeks.\n";
extern int errno;
fd=open(path,O_WRONLY|O_CREAT);
if(fd!=-1){
printf("opened file %s .\n",path);
}else{
printf("can't open file %s.\n",path);
printf("errno: %d\n",errno);
printf("ERR : %s\n",strerror(errno));
}
write(fd,s,sizeof(s));
close(fd);
printf("Done.\n");
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim xrwj.c
yang@yang-virtual-machine:~/桌面$ gcc -o xrwj.out xrwj.c
yang@yang-virtual-machine:~/桌面$ ./xrwj.out
opened file /home/yang/桌面/txt1.txt .
Done.
yang@yang-virtual-machine:~/桌面$ cat txt1.txt
hello ,Linux.
I've leart C program for two weeks.
读取文件函数read
作用:从一个打开的文件中读取字符串。
使用:ssize_t read(int fd;void *buf,size_t count);
。
fd已打开文件编号,count需要读取字符个数。buf,一个空指针,读取的内容会返回到这个指针指向的字符串。
返回值表示读取到的字符个数。0表示已经达到了文件末尾或文件中没有内容可供读取。发生错误-1。
读文件时,文件的读位置会随着读取到的字节移动。
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
int main()
{
int fd;
char path[]="/home/yang/桌面/txt1.txt";
int size;
char s[100];
extern int errno;
fd=open(path,O_RDONLY);
if(fd!=-1){
printf("opened file %s .\n",path);
}else{
printf("can't open file %s.\n",path);
printf("errno: %d\n",errno);
printf("ERR : %s\n",strerror(errno));
}
size=read(fd,s,sizeof(s));
close(fd);
printf("%s\n",s);
printf("%d\n",size);
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim dqwj.c
yang@yang-virtual-machine:~/桌面$ gcc -o dqwj.out dqwj.c
yang@yang-virtual-machine:~/桌面$ ./dqwj.out
opened file /home/yang/桌面/txt1.txt .
hello ,Linux.
I've leart C program for two weeks.
51
文件读写位置的移动
每一个已打开的文件都有一个读写位置。通常是指向文件开头,以附加方式打开指向文件末尾。
lseek函数:可以在文件内容中的位置上面移动,从而在文件中不同的位置上读写。
使用:off_t lseek(int fd,off_t offset,int whence);
。
头文件:sys/types.h
andunistd.h
。
-
offset,表示根据参数whence来移动读写位置的位移数
-
whence,表示系统定义的常量:
- SEEK_SET:参数offset即为新的读写位置。
- SEEK_CUR:以目前的读写位置往后增加offset个位移量。
- SEEK_END:将读写位置指向文件末尾后,再增加offset个位移量。
当whence值为后两个时,offset可以为负数,表示向前移动。
常用的文件移动方式:
lseek(int fd,0,SEEK_SET)
:将读写位置移到开头。lseek(int fd,0.SEEK_END)
:移到文件末尾。lseek(int fd,0,SEEK_CUR)
:取得当前的文件位置。
函数调用成功返回当前的读写位置(即距文件开头的字节数),出错返回-1。
可能错误:
- EBADF:传入的参数不是一个已经打开的文件。
- EINVAL:给入的whence参数不合理。
- EOVERFLOW:给入的移动参数导致文件头指针指向了文件开头以前,产生了溢出错误。
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
int main()
{
int fd;
char path[]="/home/yang/桌面/txt1.txt";
int size;
char s[100]="";
extern int errno;
fd=open(path,O_RDONLY);
if(fd!=-1){
printf("opened file %s .\n",path);
}else{
printf("can't open file %s.\n",path);
printf("errno: %d\n",errno);
printf("ERR : %s.\n",strerror(errno));
}
size=read(fd,s,3);
printf("%d : ",size);
printf("%s\n",s);
size=read(fd,s,3);
printf("%d : ",size);
printf("%s\n",s);
lseek(fd,8,SEEK_SET);
size=read(fd,s,3);
printf("%d : ",size);
printf("%s\n",s);
lseek(fd,0,SEEK_SET);
size=read(fd,s,3);
printf("%d : ",size);
printf("%s\n",s);
close(fd);
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim dqwj.c
yang@yang-virtual-machine:~/桌面$ vim ydwz.c
yang@yang-virtual-machine:~/桌面$ gcc -o ydwz.out ydwz.c
yang@yang-virtual-machine:~/桌面$ ./ydwz.out
opened file /home/yang/桌面/txt1.txt .
3 : hel
3 : lo #空格也是一个字符
3 : inu
3 : hel
将缓冲区数据写入到磁盘函数sync
缓冲区,是Linux系统对文件的一种处理方式。在对文件进行写操作时,并没有立即把数据写入到磁盘,而是把数据写入到缓冲区中。
sync函数:会对当前程序打开的所有文件进行处理,将缓冲区中的内容写入到文件。无参数,返回值0,一般不会出错。
强制写入缓冲区数据的好处是:保证数据有同步。
使用:int sync(void)
。头文件:#include<unistd.h>
。
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
int main()
{
int fd;
char path[]="/home/yang/桌面/txt2.txt";
char s[]="hello,Linux.\nI've leart C program for two weeks.\n";
extern int errno;
fd=open(path,O_WRONLY|O_CREAT);
if(fd!=-1){
printf("opened file %s.\n",path);
}else{
printf("can't open file %s.\n",path);
printf("errno: %d",errno);
printf("ERR : %s.\n",strerror(errno));
}
write(fd,s,sizeof(s));
sync();
printf("sync function done.\n");
close(fd);
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim sync.c
yang@yang-virtual-machine:~/桌面$ gcc -o sync.out sync.c
yang@yang-virtual-machine:~/桌面$ ./sync.out
opened file /home/yang/桌面/txt2.txt.
sync function done.
将缓冲区数据写入到磁盘函数fsync
与sync不同的是,fsync可以指定打开文件的编号,执行以后会返回一个值。
使用:int fsync(int fd);
。头文件:#include<unistd.h>
。
错误:
- EBADF:参数fd不是一个正确的文件打开编号或者文件不能写入。
- EIO
- EROFS或EINVAL:fd是一个特殊的文件,不能写入内容。
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
int main()
{
int fd;
char path[]="/home/yang/桌面/txt1.txt";
char s[]="hello,LINUX.\nhhhhhh\n";
extern int errno;
fd=open(path,O_WRONLY|O_CREAT);
if(fd!=-1){
printf("opened file %s.\n",path);
}else{
printf("can't open file %s.\nerrno: %d.\nERR : %s.\n",path,errno,strerror(errno));
}
write(fd,s,sizeof(s));
if(fsync(fd)==0){
printf("fsync function done.\n");
}else{
printf("fsync function failed.\n");
}
close(fd);
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim fsync.c
yang@yang-virtual-machine:~/桌面$ gcc -o fsync.out fsync.c
yang@yang-virtual-machine:~/桌面$ ./fsync.out
opened file /home/yang/桌面/txt1.txt.
fsync function done.
1.3.7文件锁定
文件锁定的理解
文件锁定:以独占的方式打开文件。(一个程序打开文件后,其他程序不能读取或写入文件)。
好处:有利于文件内容的一致性。
文件锁定函数flock
#include<sys/file.h>
int flock(int fd,int operation);
operation,系统定义的一些整型常量:
- LOCK_SH:建立共享锁定,其他程序可以同时访问这一个文件。多个程序可同时对同一个文件建立共享锁定。
- LOCK_EX:建立互斥锁定,其他用户不能同时访问这一个文件。一个文件同时只能有一个互斥锁定。单一文件不能同时共享+互斥锁定。
- LOCK_UN:解除文件锁定状态。
- LOCK_NB:无法建立锁定时,此操作可不被阻断,马上返回进程。通常与前两个OR(|)组合。
锁定成功返回0,失败-1。
错误:
EBADF
EINTR:在进行操作时被其他的程序或信号所中断。
EINVAL:参数不合法。
ENOLCK:核心内存溢出错误。
EWOULDBLOCK:已经被建立互斥锁定。
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<sys/file.h>
int main()
{
int fd,i;
char path[]="/home/yang/桌面/txt1.txt";
extern int errno;
fd=open(path,O_WRONLY|O_CREAT);
if(fd!=-1){
printf("opened file %s.\nplease input a number to lock the file.\n",path);
scanf("%d",&i);
if(flock(fd,LOCK_EX)==0){
printf("the file was locked.\n");
}else{
printf("the file was not locked.\n");
}
printf("please input a number to unclock the file.\n");
scanf("%d",&i);
if(flock(fd,LOCK_UN)==0){
printf("the file was unlocked.\n");
}else{
printf("the file was not unlocked.\n");
}
close(fd);
}else{
printf("can't open file %s.\nerrno : %d.\nERR : %s.\n",path,errno,strerror(errno));
}
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim flock.c
yang@yang-virtual-machine:~/桌面$ gcc -o flock.out flock.c
yang@yang-virtual-machine:~/桌面$ ./flock.out
opened file /home/yang/桌面/txt1.txt.
please input a number to lock the file.
1
the file was locked.
please input a number to unclock the file.
3
the file was unlocked.
文件锁定函数fcntl
fcntl可以设定对文件的某一部分进行锁定。
使用:
#include<unistd.h>
#include<fcntl.h>
int fcntl(int fd,int cmd);
int fcntl(int fd,int cmd,long arg);
int fcntl(int fd,int cmd,struct flock *lock);
cmd,系统设定的一些整型常量,用于设置 文件的打开方式:
- F_DUPFD:用来查找大于或等于参数arg的最小且仍未使用的文件编号,并且复制参数fd的文件编号,执行成功返回新复制的文件编号。
- F_GETFD:取得close-on-exec标志。若此标志的参数arg的FD_CLOEXEC位为0,代表在调用exec()相关函数时文件将不会被关闭。
- F_SETFD:设置close-on-exec旗标。该旗标以参数arg的FD_CLOEXEC位决定。
- F_GETFL:取得文件描述词状态旗标,此旗标为open函数的参数返回序号。
- F_SETFL:设置文件描述词状态旗标,参数arg为新旗标,但只允许O_APPEND、O_NONBLOCK、O_ASYNC位的改变,其他位的改变将不受影响。
- F_GETLK:取得文件锁定的状态。
- F_SETLK:设置文件锁定的状态。此时flock结构的l_type值必须是F_RDLCK(建立一个供读取用的锁定)、F_WRLCK(建立一个供写入用的锁定)、F_UNLCK(删除之前建立的锁定方式)。如果无法建立锁定则返回-1,错误代码为EACCESS或EAGAIN。
- F_SETLKW/F_SETLK:这两个参数的作用相同,无法建立锁定时,此调用会一直等到锁定动作成功为止。
参数lock指针为flock类型的结构体指针,用来设置锁定文件的一个区域。flock结构体的定义:
struct flock
{
short int l_type;
short int l_whence;
off_t l_start;
off_t l_len;
pid_t l_pid;
}
l_type:
- F_RDLCK:建立一个供读取用的锁定。
- F_WRLCK:建立一个供写入用的锁定。
- F_UNLCK:删除之前建立的锁定方式。
l_whence:
- SEEK_SET:以文件开头为锁定的起始位置。
- SEEK_CUR:以文件当前的读取位置为锁定的起始位置。
- SEEK_END:以文件结尾为锁定的起始位置。
fcntl函数成功0,失败-1。
可能错误:
- EACCESS或EAGAIN:文件已经被锁定,没有权限再进行锁定操作。
- EAGAIN:文件已经被另外一个进程所占用。
- EBADF:fd参数无效或文件已经关闭。
- EDEADLK:这个文件被死锁。
- EFAULT:文件处于只读分区内。
- EINVAL:函数所给的参数不合法。
- EINTR:操作被其他的程序或信号中断。
- EMFILE:已经超过了最多可以打开的文件数。
- ENOLCK:已经建立了太多的锁定方式。
- EPERM:在以追加方式打开的文件上错误地进行锁定。
文件锁定函数fcntl使用实例
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
int main()
{
int fd;
char path[]="/home/yang/桌面/txt1.txt";
struct flock f1;
char s[]="hello ,Linux!\nhhhhhhhh\n";
extern int errno;
fd=open(path,O_WRONLY|O_CREAT);
if(fd!=-1){
printf("opened file %s.\n",path);
}else{
printf("can't open file %s.\nerrno: %d.\nERR : %s.\n",path,errno,strerror(errno));
}
f1.l_type=F_RDLCK;/*建立一个供读取的锁定*/
f1.l_whence=SEEK_SET;/*以文件开头为锁定的起始位置*/
f1.l_start=2;/*从第二个字节开始*/
f1.l_len=10;/*一共锁定10个字节*/
f1.l_pid=15;/*进程号*/
if(fcntl(fd,F_SETLKW,&f1)==0){
printf("some string of the file was locked.\n");
}else{i
printf("locked error.\nerrno: %d.\nERR : %s.\n",errno,strerror(errno));
}
close(fd);
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim flock.c
yang@yang-virtual-machine:~/桌面$ vim fcntl.c
yang@yang-virtual-machine:~/桌面$ gcc -o fcntl.out fcntl.c
yang@yang-virtual-machine:~/桌面$ ./fcntl.out
opened file /home/yang/桌面/txt1.txt.
locked error.
errno: 9.
ERR : Bad file descriptor.
1.3.8文件的移动和复制
文件的移动函数rename
文件移动:把文件从一个目录转移到另一个目录。
Linux中有两中移动文件方式:
- 在同一个分区中移动文件,相当于把文件重命名。rename函数
- 在不同分区之间移动文件。
rename函数使用:int rename(char *oldpath,char *newpath);
。头文件:#include<stdio.h>
。
可能错误:
EACESS
EBUSY:文件被其他程序占用,系统繁忙。
EFAULT:新or旧文件处于不可访问的目录中。
EINVAL:文件名所指目录不存在。
EISDIR:新or旧文件是一个目录。
ELOOP:新or旧文件有太多的文件or链接相匹配。
EMLINK:文件目录中的文件已经到了最大数目录。
ENAMETOOLONG
ENOENT:旧or新文件不存在。(?新文件怎么不存在)
ENOMEM:内核内存不足。
ENOSPC:磁盘的空间不足。
ENOTDIR:新or旧文件所指的目录不存在。
EXDEV:新文件和旧文件不是在同一类型的文件分区内。
EROFS:新文件位于只读分区。
rename函数使用实例
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
char path1[]="/home/yang/桌面/txt1.txt";
char path2[]="/home/yang/桌面/c/txt11.txt";
char newpath[]="/home/yang/b.txt";
extern int errno;
if(rename(path1,path2)==0){
printf("the file %s was moved to %s.\n",path1,path2);
}else{
printf("can't move the file %s.\n,errno: %d.\nERR : %s.\n",path1,errno,strerror(errno));
}
if(rename(path1,newpath)==0){
printf("the file %s was moved to %s.\n",path1,path2);
}else{
printf("can't move the file %s.\n,errno: %d.\nERR : %s.\n",path1,errno,strerror(errno));
}
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim ydwj.c
yang@yang-virtual-machine:~/桌面$ gcc -o ydwj.out ydwj.c
yang@yang-virtual-machine:~/桌面$ ./ydwj.out
the file /home/yang/桌面/txt1.txt was moved to /home/yang/桌面/c/txt11.txt.
can't move the file /home/yang/桌面/txt1.txt.
,errno: 2.
ERR : No such file or directory.
yang@yang-virtual-machine:~/桌面$ ls
a.out cuowuhao.out flock.c jllswj.out scwj.out ydwj.c
c cuowu.out flock.out jlwj.c sync.c ydwj.out
cjml.c dkwj.c fsync.c jlwj.out sync.out ydwz.c
cjml.cpp dkwj.out fsync.out mytemp-CJnkkD test1.sh ydwz.out
cjml.out dqwj.c gbwj.c mytemp-xGIbO7 test.sh
cuowu.cpp dqwj.out gbwj.out scml.c txt2.txt
cuowuhao.c fcntl.c hello.txt scml.out xrwj.c
cuowuhao.cpp fcntl.out jllswj.c scwj.c xrwj.out
yang@yang-virtual-machine:~/桌面$ cd ./c
yang@yang-virtual-machine:~/桌面/c$ ls
aa.out a.cxx a.o a.s test1.cpp test.c test.out
a.c a.debug a.out b.c test1.out test.debug txt11.txt
文件复制实例
没有直接复制一个文件的函数。只能打开源文件和目标文件,依次从目标文件中读取一定长度的内容,然后写入到目标文件中。
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
int main()
{
int fd1,fd2,size,i=1;
char path1[]="/home/yang/桌面/txt2.txt";
char path2[]="/home/yang/桌面/s1.txt";
char buf[100];
extern int errno;
fd1=open(path1,O_RDONLY);
fd2=open(path2,O_WRONLY|O_CREAT);
if(fd1!=-1)
{
printf("opened the file %s.\n",path1);
}else{
printf("can't open the file %s.\nerrno: %d.\n ERR : %s.\n",path1,errno,strerror(errno));
}
for(;i!=0;){
i=read(fd1,buf,sizeof(buf));
printf("%d:",i);
printf("%s\n",buf);
if(i==-1){
break;
}else{
write(fd2,buf,sizeof(buf));
}
}
close(fd1);
close(fd2);
return 0;
}
yang@yang-virtual-machine:~/桌面$ vim fzwj.c
yang@yang-virtual-machine:~/桌面$ gcc -o fzwj.out fzwj.c
yang@yang-virtual-machine:~/桌面$ ./fzwj.out
opened the file /home/yang/桌面/txt2.txt.
50:hello,Linux.
I've leart C program for two weeks.
0:hello,Linux.
I've leart C program for two weeks.
1.3.9文件实例:电话本程序
程序功能分析
设计程序之前,需要对程序的各种功能进行规划,安排程序的各个模块和实现方式。
一个电话号码管理程序的功能:
- 进行程序以后,需要有一个选择菜单。
- 需要添加电话号码,实现数据的输入。
- 需要删除电话号码,实现数据的管理。
- 需要进行数据列表,显示所有的电话号码信息。
- 需要根据一个姓名进行查找,查出这个用户的电话。
- 需要有文件写入功能,把信息保存到文件上。
- 要有文件读取功能,从文件中读取以前保存的数据。
程序的函数
需要把以上功能写成不同的函数,从而把一个复杂的程序拆分成多个独立的简单模块,然后用主函数把这些程序组织到一起。需要模块:
-
菜单函数,完成菜单的显示和选择。
int menu();
-
显示电话函数,参数是一个结构体,显示结构体的信息。
void shownum(struct telnum t);
-
添加电话函数,完成一个电话号码的添加,返回一个结构体。
struct telnum addnum();
-
查找函数,用户输入一个姓名,查找这个用户的电话。
void selectbyname();
-
删除电话号码函数,用户输入一个姓名,删除对应电话号码。
void delenum();
-
保存信息功能,将所有电话号码保存到文件上。
void savetofile();
-
导入电话号码,从文件中读取以前的文件信息。
void loadfromfile();
-
主函数,完成各个函数的调用。
int main()
包含文件
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<errno.h>
数据的定义
struct telnum
{
char name[10];
char tel[13];
};
struct telnum num[100];
int i;/*当前记录的条数*/
/*两个全局变量*/
菜单函数
每一个菜单有一个编号,用户从键盘输入编号。编号有效则返回这个选项,如果输入的选项为0则退出该程序,编号无效则显示错误要求用户重新选择。
int menu()
{
int j=0;
while(1){
printf("Please select a menu:\n");
printf(" 1: add a number.\n");
printf(" 2: all the number.\n");
printf(" 3: select a number by name.\n");
printf(" 4: delete a number.\n");
printf(" 5: save to file.\n");
printf(" 6: load numbers from file.\n");
printf(" 0: exit.\n");
scanf("%d",&j);
if(j==0){
printf("Byebye.\n");
exit(1);
}else if(j<1||j>6){
printf("error.\n");
continue;
}else{
return(j);/*正常范围返回输入的数值*/
}
}
}
显示电话信息函数
void shownum(struct telnum t)
{
printf("Name :%s\n",t.name);
printf(" tel:%s\n",t.tel);
}
添加电话号码函数
struct telnum addnum()
{
struct telnum numtmp;
char name[10],tel[13];
printf("add a telephone number:\n");
printf("please input the name:\n");
scanf("%s",name);
printf("please input the num:\n");
scanf("%s",tel);
strcpy(numtmp.name,name);
strcpy(numtmp.tel,tel);
return numtmp;
}
按姓名查找函数
void selectbyname()
{
char name[20];
int n,j;
n=0;
printf("select a number by name:\n");
printf("please input a name:\n");
scanf("%s",name);
for(j=0;j<i;j++){
if(strcmp(num[j].name,name)==0){
shownum(num[j]);
n++;
}
}
if(n==0){
printf("no such a name.\n");
}
}
删除电话号码函数
void delenum()
{
char na[20];
int n,j;
n=0;
printf("delete a num by name:\n");
printf("please input a name:\n");
scanf("%s",na);
for(j=0;j<i;j++){
if(strcmp(num[j].name,na)==0){
n++;
for(;j<i;j++){
num[j]=num[j+1];
}
i--;
break;
}
}
if(n==0){
printf("no such a name.\n");
}
}
保存到文件函数
void savetofile()
{
int j,fd;
char file[]="/home/yang/tel.txt";
extern int errno;
fd=open(file,O_WRONLY|O_CREAT);
if(fd!=-1){
printf("opened file %s.\n",file);
}else{
printf("can't open file %s.\nerrno: %d\nERR : %s\n",file,errno,strerror(errno));
}
for(j=0;j<i;j++){
printf(" %d %s\n",j,num[j].name);
write(fd,num[j].name,7);
write(fd,num[j].tel,13);
}
printf("saved.\n");
close(fd);
}
从文件导入信息函数
void loadfromfile()
{
int j=0,fd,t;
i=0;
char na[7];
char tel[13];
char file[]="/home/yang/tel.txt";
extern int errno;
fd=open(file,O_CREAT);
if(fd!=-1){
printf("opened file %s.\n",file);
}else{
printf("can't open the file %s.\nerrno: %d.\nERR : %s.\n",file,errno,strerror(errno));
}
while((t=read(fd,na,7))!=0&&t!=-1){
strcpy(num[i].name,na);
read(fd,tel,13);
strcpy(num[i].tel,tel);
i++;
}
close(fd);
}
主函数
int main()
{
int s,j;
printf("----Telphone Notebook.----\n");
while(1){
s=menu();
switch(s){
case 1:
num[i]=addnum();
i++;
break;
case 2:
for(j=0;j<i;j++){
shownum(num[j]);
}
break;
case 3:
selectbyname();
break;
case 4:
delenum();
break;
case 5:
savetofile();
break;
case 6:
loadfromfile();
break;
}
}
return 0;
}
程序的运行
yang@yang-virtual-machine:~/桌面$ ./dhb.out
----Telphone Notebook.----
Please select a menu:
1: add a number.
2: all the number.
3: select a number by name.
4: delete a number.
5: save to file.
6: load numbers from file.
0: exit.
1
add a telephone number:
please input the name:
linlin
please input the num:
123456789
Please select a menu:
1: add a number.
2: all the number.
3: select a number by name.
4: delete a number.
5: save to file.
6: load numbers from file.
0: exit.
1
add a telephone number:
please input the name:
lina
please input the num:
789456123
Please select a menu:
1: add a number.
2: all the number.
3: select a number by name.
4: delete a number.
5: save to file.
6: load numbers from file.
0: exit.
5
opened file /home/yang/tel.txt.
0 linlin
1 lina
saved.
Please select a menu:
1: add a number.
2: all the number.
3: select a number by name.
4: delete a number.
5: save to file.
6: load numbers from file.
0: exit.
2
Name :linlin
tel:123456789
Name :lina
tel:789456123
Please select a menu:
1: add a number.
2: all the number.
3: select a number by name.
4: delete a number.
5: save to file.
6: load numbers from file.
0: exit.
5
opened file /home/yang/tel.txt.
0 linlin
1 lina
saved.
Please select a menu:
1: add a number.
2: all the number.
3: select a number by name.
4: delete a number.
5: save to file.
6: load numbers from file.
0: exit.
0
Byebye.
yang@yang-virtual-machine:~/桌面$ vim /home/yang/tel.txt
yang@yang-virtual-machine:~/桌面$ ./dhb.out
----Telphone Notebook.----
Please select a menu:
1: add a number.
2: all the number.
3: select a number by name.
4: delete a number.
5: save to file.
6: load numbers from file.
0: exit.
6
opened file /home/yang/tel.txt.
Please select a menu:
1: add a number.
2: all the number.
3: select a number by name.
4: delete a number.
5: save to file.
6: load numbers from file.
0: exit.
2
Please select a menu:
1: add a number.
2: all the number.
3: select a number by name.
4: delete a number.
5: save to file.
6: load numbers from file.
0: exit.
linlin^@123456789^@^@^@^@lina^@n^@789456123^@^FV^@