1.3目录与文件(记录)

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.hsys/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.handunistd.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^@
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值