一、IO基础
1.1 IO的概念
1> i:input o:output
2> 所谓IO就是程序对外部设备完成信息的交换过程
1.2 已经接触的IO
1> #include<sdtio.h> 该头文件中封装了有关标准的输入输出相关的操作
2> 接触的IO函数:printf\scanf、getchar\putchar、gets\puts
3> 以上函数的特点:输入输出的目标都是终端文件
1.3 分类
1> 标准IO:依赖于库函数,程序提供的函数,该函数中封装了一个缓冲区,调用函数时先将要操作的数据放入缓冲区,等待缓冲区刷新时机到了后,该函数在调用系统调用函数将数据同一刷入到内核空间,效率较高
2> 文件IO:依赖于系统调用,内核提供的函数,每次进行该调用,都会执行一次系统调用,效率较低
3> 区别:标准IO = 文件IO + 缓冲区
4> 每次调用系统调用函数,进程进入阻塞状态,程序就会从用户空间向内核空间进行一次切换,当IO事件完成后,进程会重新进入就绪状态
1.4 常用接口函数
1> 标准IO:printf\scanf、getchar\putchar、gets\puts 、fopen\fclose、fprintf\fscanf、fgetc\fputc、fgets\fputs、fread\fwrite、fseek\ftell\rewind
2> 文件IO:open、close、read、write、lseek
二、标准IO
2.1 FILE文件结构体类型
1> 该类型是在stdio.h中已经定义了的用于描述一个文件的所有信息的结构体类型
2> 由fopen函数打开某个文件,所返回的操作句柄就是文件指针,后期可以通过该指针操作文件
2> 该结构体中的内容为
如何找到FILE结构体
1、到根目录下的usr目录中的include目录中:ctags -R 创建索引
2、在任意地方输入指令:vi -t 要查询的名字
3、找到对应的文件数字后,输入数字并回车,查看对应文件信息
4、使用 ctrl + ] 继续向前追 使用ctrl + [ :回退
1 2 3 4 5 6 7 8 9 10 11 245 struct _IO_FILE { 257 char * _IO_buf_base; 缓冲区的起始地址258 char * _IO_buf_end; 缓冲区的结束地址 267 268 int _fileno; }
3> 特殊的文件指针有三个,这三个指针,主要是针对于终端操作的
stdin:标准输入流指针
stdout:标准输出流指针
stderr:标准错误输出流指针
2.2 fopen函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <stdio.h> FILE *fopen (const char *pathname, const char *mode) ; 功能:通过指定的模式,打开指定的文件,并返回该文件的地址 参数1 :是一个字符串,表示要打开的文件,是要打开的文件的路径。 例如: "/usr/include/stdio.h" 参数2 :也是一个字符串,表示要打开的文件模式,以下面的字符开头,表示不同的打开方式 r Open text file for reading. The stream is positioned at the beginning of the file. r+ Open for reading and writing. The stream is positioned at the beginning of the file. w Truncate file to zero length or create text file for writing. The stream is positioned at the beginning of the file. w+ Open for reading and writing. The file is created if it does not exist, otherwise it is truncated. The stream is positioned at the beginning of the file. a Open for appending (writing at end of file) . The file is created if it does not exist. The stream is positioned at the end of the file. a+ Open for reading and appending (writing at end of file) . The file is created if it does not exist. The initial file position for reading is at the beginning of the file, but output is always appended to the end of the file. 返回值:成功返回打开的文件的指针,失败返回NULL 并置位错误码
2.3 fclose函数
1 2 3 4 5 6 #include <stdio.h> int fclose (FILE *stream) ; 功能:关闭指定的文件 参数:由open函数打开的文件指针 返回值:成功返回0 , 失败返回EOF(-1 ),并置位错误码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <stdio.h> int main (int argc, const char *argv[]) { FILE *fp; fp = fopen("./test.txt" , "r" ); if (NULL == fp) { printf ("open file error\n" ); return -1 ; } printf ("文件打开成功\n" ); fclose(fp); return 0 ; }
2.4 错误码
1> 错误码:当调用内核提供的函数出错时,内核空间向用户空间返回一个错误信息,每个错误信息会对应一个编号,内核空间向用户空间返回的就是该编号,这个编号就是错误码
2> 查看错误码:vi -t EIO
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 5 #define EPERM 1 6 #define ENOENT 2 7 #define ESRCH 3 8 #define EINTR 4 9 #define EIO 5 10 #define ENXIO 6 11 #define E2BIG 7 12 #define ENOEXEC 8 13 #define EBADF 9 14 #define ECHILD 10 15 #define EAGAIN 11 16 #define ENOMEM 12 17 #define EACCES 13 18 #define EFAULT 14 19 #define ENOTBLK 15 20 #define EBUSY 16 21 #define EEXIST 17 22 #define EXDEV 18 23 #define ENODEV 19 24 #define ENOTDIR 20 25 #define EISDIR 21
3> 处理错误码和错误信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <errno.h> int error;错误码定义在当前头文件中,如果想要使用错误码变量,需要加上该头文件 #include <string.h> char *strerror (int errnum) ; 功能:将给定的错误码,转换成错误信息 参数:错误码 返回值:错误信息 #include <stdio.h> void perror (const char *s) ; 功能:输出最新的错误码对应的错误信息 参数:字符串,是一个提示信息,原样输出,并在后面加上一个冒号,冒号后是错误信息 返回值:无
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <stdio.h> #include <errno.h> #include <string.h> int main (int argc, const char *argv[]) { FILE * fp = NULL ; fp = fopen("./test.txt" , "r" ); if (NULL == fp) { perror("fopen error" ); return -1 ; } fclose(fp); return 0 ; }
2.5 fputc、fgetc函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <stdio.h> int fputc (int c, FILE *stream) ; 功能:向指定的文件中输出一个字符 参数1 :要输出的字符数据 参数2 :要输出的文件指针,由fopen函数打开后指向的某个文件 返回值:成功返回输出的字符,失败返回EOF #include <stdio.h> int fgetc (FILE *stream) ; 功能:从指定的文件中读取一个字符 参数:文件指针 返回值:读取光标所在位置的一个字符,失败返回EOF
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 #include <stdio.h> int main (int argc, const char *argv[]) { FILE *fp = NULL ; if ((fp=fopen("./test.txt" , "w" )) == NULL ) { perror("fopen error" ); return -1 ; } fputc('H' , fp); fputc('e' , fp); fputc('l' , fp); fputc('l' , fp); fputc('o' , fp); fputc('\n' , fp); fclose(fp); if ( (fp = fopen("./test.txt" , "r" )) == NULL ) { perror("fopen r error" ); return -1 ; } char buf = 0 ; while (1 ) { buf = fgetc(fp); if (buf == EOF) { break ; } printf ("%c" , buf); } fclose(fp); return 0 ; }
练习:
1> 使用fgetc统计给定的某个文件的行号
2> 使用fgetc和fputc完成两个文件的拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <stdio.h> int main (int argc, const char *argv[]) { if (argc != 3 ) { printf ("input file error\n" ); printf ("usage:./a.out srcfile dstfile\n" ); return 0 ; } FILE *srcfp, *dstfp; if ((srcfp = fopen(argv[1 ], "r" )) == NULL ) { perror("srcfile open error" ); return -1 ; } if ((dstfp = fopen(argv[2 ], "w" )) == NULL ) { perror("dstfile open error" ); return -1 ; } char buf = 0 ; while ((buf = fgetc(srcfp)) != EOF) { fputc(buf, dstfp); } fclose(srcfp); fclose(dstfp); printf ("拷贝成功\n" ); return 0 ; }
2.6 fputs、fgets函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> int fputs (const char *s, FILE *stream) ; 功能:将指定的字符串输出到指定的文件中去 参数1 :要输出的字符串的起始地址 参数2 :要被输出的文件 返回值:成功返回输出字符的个数,失败返回-1 #include <stdio.h> char *fgets (char *s, int size, FILE *stream) ; 功能:从指定的文件中读取最多size-1 个字符到s数组中 参数1 :存放字符串的容器起始地址 参数2 :要读取的字符个数,最大是size-1 个字符,如果文件中的字符个数小于size-1 , 则有多少读多少,最后也会补个'\0' ,读取过程中,如果遇到'\n' 也会结束一次读取 并且将'\n' 放入字符串中,后面再补个'\0' 参数3 :要被读取的文件 返回值:成功返回读取字符的个数,失败返回EOF
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <stdio.h> int main (int argc, const char *argv[]) { FILE *fp = NULL ; if ((fp = fopen("./test.txt" , "w" )) == NULL ) { perror("fopen error" ); return -1 ; } fputs ("hello world\n" , fp); fputs ("hello world\n" , fp); fputs ("hello world\n" , fp); fputs ("hello world\n" , fp); fclose(fp); if ((fp = fopen("./test.txt" , "r" )) == NULL ) { perror("fopen error" ); return -1 ; } char buf[8 ] = "" ; while (1 ) { char * res = fgets(buf, sizeof (buf), fp); if (res == NULL ) { break ; } printf ("%s\n" , buf); } fclose(fp); return 0 ; }
2.7 关于时间的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <time.h> time_t time (time_t *tloc) ;功能:获取系统当前的时间,从1970 年1 月1 日0 时0 分0 秒开始累计的秒数 参数:时间变量的指针 返回值:返回系统累计的时间 使用方式1 : time_t sys_time;time(&sys_time); 使用方式2 : time_t sys_time = time(NULL ); struct tm *localtime (const time_t *timep) ; 功能:将给定的秒数,转变成时间类型的结构体指针 参数:由time函数获取的秒数的地址 返回值:时间结构体类型的指针 struct tm { int tm_sec; 秒数 int tm_min; 分钟 int tm_hour; 小时 int tm_mday; 月中天数 int tm_mon; 月份+1 int tm_year; 年份 + 1900 int tm_wday; 周中天数 int tm_yday; 年中天数 int tm_isdst; 夏令时 };
2.8 sprintf函数
1 2 3 4 5 6 int sprintf (char *str, const char *format, ...) ;功能:将后面的格式串转换为字符串放入一个给定的字符数组中 参数1 :存放转换格式串的字符数组首地址 参数2 :格式串 参数3 :不定参数,个数由参数2 中的格式控制符决定 返回值:成功返回转换的字符个数,失败返回-1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <stdio.h> #include <time.h> int main (int argc, const char *argv[]) { time_t sys_time = 0 ; time(&sys_time); struct tm * t = localtime(&sys_time); char buf[128 ] ="" ; sprintf (buf,"%4d-%02d-%02d %02d:%02d:%02d\n" ,t->tm_year+1900 , t->tm_mon+1 ,\ t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); printf ("buf = %s\n" , buf); return 0 ; }
作业:
1> 使用fgets统计一个文件的行号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <stdio.h> int main (int argc, const char *argv[]) { FILE *fp=NULL ; if (argc!=2 ){ puts ("输入有误" ); puts ("eg:/a.out test.txt" ); return -1 ; } if ((fp=fopen(argv[1 ],"r" ))==NULL ){ perror("fopen error:" ); return -1 ; } int line=0 ; char buff[128 ]; while (NULL !=fgets(buff,sizeof (buff),fp)){ line++; } printf ("此文件有%d行\n" ,line); fclose(fp); return 0 ; }
2> 使用fgets、fputs完成两个文件的拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <stdio.h> int main (int argc, const char *argv[]) { if (argc!=3 ){ puts ("输入有误!" ); return -1 ; } FILE* fp=NULL ; FILE*fp_cp=NULL ; if ((fp=fopen(argv[1 ],"r" ))==NULL ){ perror("打开1文件出错:" ); return -1 ; } if ((fp_cp=fopen(argv[2 ],"w" ))==NULL ){ perror("打开2文件出错:" ); return -1 ; } char buff[1024 ]; while (fgets(buff,sizeof (buff),fp)!=NULL ){ fputs (buff,fp_cp); } puts ("拷贝成功!" ); fclose(fp); fclose(fp_cp); return 0 ; }
3> 向文件中输出当前的系统时间
1 2 3 4 5 6 7 8 9 10 1、16:42:50 2、16:42:51 3、16:42:52 。。。 ctrl + C ./a.out 6、17:10:03 7、17:10:04 8、17:10:05 。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 #include <stdio.h> #include <time.h> #include <unistd.h> time_t sys_time = 0 ;char time_buff[128 ] = "" ; struct tm *t ;int line = 1 ;int get_time () { time(&sys_time); t = localtime(&sys_time); sprintf (time_buff, "%4d[%4d/%02d/%02d-%02d:%02d:%02d]\n" , line, t->tm_year + 1900 , t->tm_mon + 1 , t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); return t->tm_sec; } int main (int argc, const char *argv[]) { FILE *fp = NULL ; int last_sec = -1 ; if ((fp = fopen("./time.txt" , "r" )) == NULL ) { perror("打开文件失败:" ); } char buff[128 ]; while (NULL != fgets(buff, sizeof (buff), fp)) { line++; } while (1 ) { if ((fp = fopen("./time.txt" , "a" )) == NULL ) { perror("打开文件失败:" ); return -1 ; } sleep(1 ); if (last_sec != get_time()) { last_sec = t->tm_sec; fputs (time_buff, fp); line++; } printf ("%d\n" , line); fclose(fp); } return 0 ; }
2.9 snprintf函数
1> 对于sprintf而言,如果字符数组的长度,小于字符串的长度,那么存储字符串时,就没有把字符串的结束标志存储起来,使用时会很危险
2> 基于此,引入了snprintf,多提供一个参数,size,用于控制读取的个数
3> snprintf比sprintf使用更加安全
1 2 3 4 5 6 7 int snprintf (char *str, size_t size, const char *format, ...) ;功能:将指定的格式串中的最多size-1 个字符写入到str字符数组中 参数1 :要存储字符串的字符数组起始地址 参数2 :要读取的字符个数,最大是size-1 参数3 :格式串 参数4 :可变参数 返回值:成功返回转换的字符串的长度,失败返回-1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <stdio.h> #include <string.h> int main (int argc, const char *argv[]) { char buf[12 ]; snprintf (buf,sizeof (buf), "%s-%s-%c-%d\n" , "zhangsan" , "23111班" , 'M' , 99 ); printf ("buf = %s\n" , buf); return 0 ; }
2.10 关于缓冲区问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int _flags;char * _IO_read_ptr;char * _IO_read_end;char * _IO_read_base;char * _IO_write_base;char * _IO_write_ptr;char * _IO_write_end;char * _IO_buf_base;char * _IO_buf_end;char * _IO_save_base;char * _IO_backup_base;char * _IO_save_end;
缓冲区本质上就是一段内存 在源码中,和文件有关的结构体中有很多的指针变量,如上所示,这些指针就是在维护缓冲区。
此时我们就可以知道,缓冲区是由要打卡文件的进程申请的,也是由这个进程来维护的,缓冲区存在于FILE结构体中。
1> 缓冲区的分类
行缓存:和终端相关的读写操作指针对应的缓冲区称为行缓存(stdin、stdout),大小为1024字节
全缓存:和文件相关的读写操作指针对应的缓冲区称为全缓存(fp),大小为4096字节
不缓存:和标准出错输出流相关的文件指针对应的缓冲区(stderr),大小为0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <stdio.h> int main (int argc, const char *argv[]) { printf ("%ld\n" , stdout ->_IO_buf_end - stdout ->_IO_buf_base); printf ("%ld\n" , stdout ->_IO_buf_end - stdout ->_IO_buf_base); char ch = getchar(); printf ("%ld\n" , stdin ->_IO_buf_end - stdin ->_IO_buf_base); perror("hello" ); printf ("%ld\n" , stderr ->_IO_buf_end - stderr ->_IO_buf_base); FILE *fp = NULL ; if ((fp = fopen("./time.txt" , "r" )) == NULL ) { perror("fopen error" ); return -1 ; } fgetc(fp); printf ("%ld\n" , fp->_IO_buf_end - fp->_IO_buf_base); fclose(fp); return 0 ; }
2> 缓冲区的刷新时机
1、行缓存刷新时机
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include <stdio.h> int main (int argc, const char *argv[]) { for (int i=0 ; i<1025 ; i++) { putchar ('A' ); } while (1 ); return 0 ; }
fflush
函数,用于强制刷新缓冲区。
1 2 3 4 5 6 #include <stdio.h> int fflush (FILE *stream) ; 功能:刷新指定的文件指针的缓冲区 参数:要刷新的缓冲区文件指针 返回值:成功返回0 ,失败返回EOF,并置位错误码
2> 全缓存刷新时机
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 #include <stdio.h> int main (int argc, const char *argv[]) { FILE *fp = NULL ; if ((fp = fopen("./time.txt" , "w+" )) == NULL ) { perror("fopen error" ); return -1 ; } for (int i=0 ; i<4097 ; i++) { fputc('A' , fp); } while (1 ); return 0 ; }
3> 不缓存的刷新时机
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <stdio.h> int main (int argc, const char *argv[]) { fputs ("hello world" , stderr ); while (1 ); return 0 ; }
2.11 fprintf、fscanf函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int fprintf (FILE *stream, const char *format, ...) ;功能:向指定的文件中,输出指定的格式串 参数1 :要被输出的文件 参数2 :格式串,可以包含格式控制符 参数3 :不定参数,根据参数2 中的格式控制符而定 返回值:成功返回输出的字符个数,失败返回EOF int fscanf (FILE *stream, const char *format, ...) ;功能:从给定的文件中,以指定的格式,读取数据到程序中来 参数1 :指定的文件指针 参数2 :格式串,可以包含格式控制符 参数3 :不定参数,根据参数2 中的格式控制符而定 返回值:成功返回写入数据的个数,失败返回一个负数,并置位错误码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> int main (int argc, const char *argv[]) { fprintf (stderr , "%s-%s-%d\n" , "zhangpp" , "23111班" , 90 ); int num; fscanf (stdin , "%d" , &num); printf ("num = %d\n" , num); return 0 ; }
练习:使用fprintf和fsacnf完成注册登录功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 #include <stdio.h> #include <stdlib.h> #include <string.h> int do_register () { FILE * wfp = NULL ; char userName_reg[20 ]; char pwd_reg[20 ]; printf ("请输入注册账号:" ); fgets(userName_reg, sizeof (userName_reg), stdin ); printf ("请输入注册密码:" ); fgets(pwd_reg, sizeof (pwd_reg), stdin ); userName_reg[strlen (userName_reg)-1 ] = '\0' ; pwd_reg[strlen (pwd_reg)-1 ] = '\0' ; if ((wfp = fopen("./usr.txt" , "a+" )) == NULL ) { perror("fopen error" ); return -1 ; } fprintf (wfp, "%s %s\n" , userName_reg, pwd_reg); fclose(wfp); printf ("注册成功\n" ); return 0 ; } int do_login () { char userName_log[20 ]; char pwd_log[20 ]; char userName[20 ]; char pwd[20 ]; printf ("请输入登录账号:" ); fgets(userName_log, sizeof (userName_log), stdin ); printf ("请输入登录密码:" ); fgets(pwd_log, sizeof (pwd_log), stdin ); userName_log[strlen (userName_log)-1 ] = '\0' ; pwd_log[strlen (pwd_log)-1 ] = '\0' ; FILE *rfp = NULL ; if ((rfp = fopen("./usr.txt" , "r" )) == NULL ) { perror("fopen error" ); return -1 ; } while (1 ) { int res = fscanf (rfp, "%s %s" , userName, pwd); if (res < 0 ) { printf ("登录失败\n" ); fclose(rfp); return 1 ; } if (strcmp (userName_log,userName)==0 && strcmp (pwd_log, pwd)==0 ) { printf ("登录成功\n" ); fclose(rfp); return 0 ; } } } int main (int argc, const char *argv[]) { char menu; while (1 ) { printf ("\t\t=====1、注册=====\n" ); printf ("\t\t=====2、登录=====\n" ); printf ("\t\t=====0、退出=====\n" ); printf ("请输入功能选项:" ); scanf ("%c" , &menu); while (getchar() != '\n' ); switch (menu) { case '1' : { do_register(); } break ; case '2' : { do_login(); } break ; case '0' : exit (EXIT_SUCCESS); default :printf ("您输入的功能有误请重新输入!!!\n" ); } printf ("请输入任意键按回车清屏!!!\n" ); while (getchar() != '\n' ); system("clear" ); } return 0 ; }
2.12 fwrite、fread函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <stdio.h> size_t fread (void *ptr, size_t size, size_t nmemb, FILE *stream) ; 功能:从stream指向的文件中读取nmemb项数据,每一项的大小为size,将数据存储在ptr指向的空间中 参数1 :存放数据的起始地址 参数2 :每一项的大小 参数3 :读取数据的总项数 参数4 :要被读取的文件 返回值:成功返回读取的项数,失败返回一个小于项数的值 注意:fread出错时,并不能区分是因为文件读取结束还是程序调用出错,需要使用feof或ferror来判断 size_t fwrite (const void *ptr, size_t size, size_t nmemb, FILE *stream) ;功能:将ptr指针指向的空间中,以每项大小为size字节,的nmemb项数据,写入到stream指向的文件中 参数1 :要写入的数据起始地址 参数2 :每一项的大小 参数3 :写入数据的总项数 参数4 :要被写入的文件 返回值:成功返回写入的项数,失败返回返回一个小于项数的值,或者是0
1> 使用fread、fwrite读写一个字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <stdio.h> int main (int argc, const char *argv[]) { FILE *fp = NULL ; if ((fp = fopen("./time.txt" , "w" )) == NULL ) { perror("fopen error" ); return -1 ; } char wbuf[128 ] = "hello world" ; fwrite(wbuf, 1 , sizeof (wbuf), fp); fclose(fp); if ((fp = fopen("./time.txt" , "r" )) == NULL ) { perror("fopen error" ); return -1 ; } char rbuf[5 ] = "" ; fread(rbuf, 1 , sizeof (rbuf), fp); fclose(fp); fwrite(rbuf, 1 , sizeof (rbuf), stdout ); return 0 ; }
2> 使用fread、fwrite读写一个整数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <stdio.h> int main (int argc, const char *argv[]) { FILE *fp = NULL ; if ((fp = fopen("./time.txt" , "w" )) == NULL ) { perror("fopen error" ); return -1 ; } int num = 12 ; fwrite(&num, sizeof (int ), 1 , fp); fclose(fp); if ((fp = fopen("./time.txt" , "r" )) == NULL ) { perror("fopen error" ); return -1 ; } int key = 0 ; fread(&key, sizeof (int ), 1 , fp); fclose(fp); printf ("key = %d\n" , key); return 0 ; }
3> 使用fread、fwrite读写一个结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <stdio.h> struct stu_t { int numb; char name[20 ]; double score; }; int main (int argc, const char *argv[]) { FILE *fp = NULL ; if ((fp = fopen("./time.txt" , "w" )) == NULL ) { perror("fopen error" ); return -1 ; } struct stu_t s [3] = {{1001 , "张三" , 90 }\ ,{1002 , "李四" , 95 }\ ,{1003 , "二狗" , 80 }}; fwrite(s, sizeof (struct stu_t ), 3 , fp); fclose(fp); if ((fp = fopen("./time.txt" , "r" )) == NULL ) { perror("fopen error" ); return -1 ; } struct stu_t t ; fread(&t, sizeof (t), 1 , fp); fread(&t, sizeof (t), 1 , fp); fclose(fp); printf ("numb = %d, name = %s, score = %.2lf\n" , t.numb, t.name, t.score); return 0 ; }
2.13 feof、ferror函数
1> 由于fread出错时,不区分到低是文件结束函数调用失败,需要使用对应函数判断
2> 函数原型
1 2 3 4 5 6 7 8 9 10 11 12 #include <stdio.h> int feof (FILE *stream) ;功能:判断给定的文件指针是否已经到文件末尾 参数:文件指针 返回值:如果到达文件末尾返回值真,否则返回假 int ferror (FILE *stream) ;功能:判断文件指针使用是否出错 参数:文件指针 返回值:如果出错返回值真,否则返回假
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <stdio.h> int main (int argc, const char *argv[]) { FILE *fp = NULL ; if ((fp = fopen("./usr.txt" , "r" )) == NULL ) { perror("fopen error" ); return -1 ; } char buf[12 ] = "" ; while (!feof(fp)) { int res = fread(buf, 1 , sizeof (buf), fp); fwrite(buf, 1 , res, stderr ); if (ferror(fp)) { printf ("读取失败\n" ); break ; } } fclose(fp); return 0 ; }
2.14 关于光标的函数(fseek、ftell、rewind)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <stdio.h> int fseek (FILE *stream, long offset, int whence) ;功能:将指定的文件指针从指定的位置处开始偏移 参数1 :文件指针 参数2 :偏移量 >0 :表示向后偏移 =0 :表示不偏移 <0 :表示下向前偏移 参数3 :偏移的起始位置 SEEK_SET:文件起始位置 SEEK_CUR:文件当前位置 SEEK_END:文件结尾位置 返回值:成功返回0 ,失败返回-1 并置位错误码 long ftell(FILE *stream);功能:返回当前光标所在位置 参数:文件指针 返回值:当前文件指针所在位置,失败返回-1 ,并置位错误码 eg: fseek(fp, 0 , SEEK_END); ftell(fp); void rewind (FILE *stream) ;功能:将光标重新定位在开头 : fseek(fp,0 ,SEEK_SET) 参数:文件指针 返回值:无
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <stdio.h> struct stu_t { int numb; char name[20 ]; double score; }; int main (int argc, const char *argv[]) { FILE *fp = NULL ; if ((fp = fopen("./time.txt" , "w+" )) == NULL ) { perror("fopen error" ); return -1 ; } struct stu_t s [3] = {{1001 , "张三" , 90 }\ ,{1002 , "李四" , 95 }\ ,{1003 , "二狗" , 80 }}; fwrite(s, sizeof (struct stu_t ), 3 , fp); fseek(fp,-2 *sizeof (struct stu_t ) ,SEEK_CUR); struct stu_t t ; fread(&t, sizeof (t), 1 , fp); fclose(fp); printf ("numb = %d, name = %s, score = %.2lf\n" , t.numb, t.name, t.score); return 0 ; }
图像数据处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <stdio.h> int main (int argc, const char *argv[]) { FILE *fp = NULL ; if ((fp = fopen("./jntm.bmp" , "r+" )) == NULL ) { perror("fopen error" ); return -1 ; } fseek(fp, 2 , SEEK_SET); unsigned int size; fread(&size, sizeof (size), 1 , fp); printf ("size = %d\n" , size); unsigned char color[3 ] = {255 , 0 , 0 }; fseek(fp, 54 , SEEK_SET); for (int i=0 ; i<100 ; i++) { for (int j=0 ; j<640 ; j++) { fwrite(color, sizeof (color), 1 , fp); } } fclose(fp); return 0 ; }
作业
1> 使用fread、fwrite完成两个文件的拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <stdio.h> int main (int argc, const char *argv[]) { if (argc!=3 ) { puts ("输入错误!" ); return -1 ; } FILE* fp=NULL ; FILE* fp_cp=NULL ; if ((fp=fopen(argv[1 ],"r" ))==NULL ) { perror("fopen error:" ); return -1 ; } if ((fp_cp=fopen(argv[2 ],"w" ))==NULL ) { perror("fopen error:" ); return -1 ; } char buff; while (1 ) { fread(&buff,1 ,sizeof (buff),fp); fwrite(&buff,1 ,sizeof (buff),fp_cp); if (feof(fp)) { break ; } } fclose(fp); fclose(fp_cp); return 0 ; }
2> 将注册登录框架重新实现一遍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 #include <stdio.h> #include <stdlib.h> #include <string.h> enum Operate { REGISTER, LOGIN, EXIT }; char *op_msg[] = {"注册" , "登录" , "退出" };enum Operate menu ;int do_register () { FILE *wfp =NULL ; char username_reg[20 ]; char pwd_reg[20 ]; printf ("请输入账号:" ); fgets(username_reg,sizeof (username_reg),stdin ); printf ("请输入密码:" ); fgets(pwd_reg,sizeof (pwd_reg),stdin ); username_reg[strlen (username_reg)-1 ]='\0' ; pwd_reg[strlen (pwd_reg)-1 ]='\0' ; if ((wfp=fopen("./user.txt" ,"a+" ))==NULL ) { perror("open user.txt error:" ); return -1 ; } fprintf (wfp,"%s %s\n" ,username_reg,pwd_reg); fclose(wfp); printf ("注册成功!\n" ); return 0 ; } int do_login () { FILE *wfp =NULL ; char input_username[20 ]; char input_pwd[20 ]; char username_reg[20 ]; char pwd_reg[20 ]; printf ("请输入账号:" ); fgets(input_username,sizeof (input_username),stdin ); printf ("请输入密码:" ); fgets(input_pwd,sizeof (input_pwd),stdin ); input_username[strlen (input_username)-1 ]='\0' ; input_pwd[strlen (input_pwd)-1 ]='\0' ; if ((wfp=fopen("./user.txt" ,"r" ))==NULL ) { perror("open user.txt error:" ); return -1 ; } while (1 ) { int res = fscanf (wfp,"%s %s" ,username_reg,pwd_reg); if (res<0 ) { printf ("没有这个用户\n" ); fclose(wfp); return 1 ; } if (strcmp (username_reg,input_username)==0 && strcmp (pwd_reg,input_pwd)==0 ){ printf ("登录成功!\n" ); fclose(wfp); return 0 ; } } } int main (int argc, const char *argv[]) { while (1 ) { for (int i = 0 ; i <= EXIT; i++) { printf ("【%d】:%s\t" , i, op_msg[i]); if ((i + 1 ) % 2 == 0 ) { puts ("" ); } } puts ("" ); printf ("请输入要执行的操作:" ); scanf ("%d" , &menu); while (getchar() != '\n' ); switch (menu) { case REGISTER: do_register(); break ; case LOGIN: do_login(); break ; case EXIT: exit (EXIT_SUCCESS); default : printf ("输入序号有误,请重新输入:" ); } printf ("按任意键清屏\n" ); while (getchar() != '\n' ); system("clear" ); } return 0 ; }
3> 完成图像文件信息的读写操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <stdio.h> int main (int argc, const char *argv[]) { FILE *fp=NULL ; if (NULL ==(fp=fopen("./output.bmp" ,"r+" ))) { perror("fopen error:" ); return -1 ; } fseek(fp, 0x02 , SEEK_SET); unsigned int size; fread(&size, sizeof (size), 1 , fp); printf ("size = %d\n" , size); unsigned int x,y; fseek(fp, 0x12 , SEEK_SET); fread(&x, sizeof (size), 1 , fp); fread(&y, sizeof (size), 1 , fp); printf ("x = %d\ny = %d\n" , x,y); fseek(fp, 54 , SEEK_SET); unsigned char color[3 ] = {255 , 0 , 0 }; for (int i=0 ; i<800 ; i++) { for (int j=0 ; j<33 ; j++) { fwrite(color, sizeof (color), 1 , fp); } } fclose(fp); return 0 ; }
三、文件IO
文件IO是基于系统调用
程序每进行一次系统调用,就会从用户空间向内核空间进行一次切换,执行效率较慢
目的:由于后期进程间通信,如管道、套接字通信,都使用的是文件IO,所以引入文件IO操作的概念
3.1 文件描述符
件描述符本质上是一个非负整数,每个打开的文件,都会对应一个整数用于系统调用
每个程序打开文件的个数是有上限的,默认是1024个,可以通过ulimit -a进行查看
文件描述符使用原则:最小未分配原则
当使用open函数打开一个文件时,系统会给该文件分配一个文件描述符作为句柄
当一个程序运行时,默认会打开三个文件描述符,分别对应标准输入、标准输出、标准出错
1 2 3 4 5 6 7 8 9 10 11 #include <stdio.h> int main (int argc, const char *argv[]) { printf ("%d\n" , stdin ->_fileno); printf ("%d\n" , stdout ->_fileno); printf ("%d\n" , stderr ->_fileno); return 0 ; }
3.2 open函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open (const char *pathname, int flags) ;int open (const char *pathname, int flags, mode_t mode) ; 功能:打开或可能创建一个文件 参数1 :文件路径,是一个字符串表示要打开的文件 参数2 :打开标识 三个必须选一个:O_RDONLY(只读)、O_WRONLY(只写)、O_RDWR(读写) 后面的可以以位或的方式连接,表示拥有该属性 O_CREAT:表示创建一个文件, 当第二个参数中有O_CREAT时,第三个参数必须要加上 O_APPEND:追加方式打开文件 O_TRUNC:清空文件内容 O_NONBLOCK:以非阻塞形式打开文件 O_EXCL:确保本次操作一定创建文件,如果文件已经存在,则open函数会报错,错误码为EEXIST eg: "w" : O_WRONLY | O_CREAT | O_TRUNC "r" :O_RDONLY "a" :O_WRONLY | O_APPEND | O_CREAT "w+" :O_RDWR | O_CREAT | O_TRUNC "r+" :O_RDWR "a+" :O_RDWR | O_CREAT | O_APPEND ┌──────────────┬───────────────────────────────┐ │ fopen() mode │ open() flags │ ├──────────────┼───────────────────────────────┤ │ r │ O_RDONLY │ ├──────────────┼───────────────────────────────┤ │ w │ O_WRONLY | O_CREAT | O_TRUNC │ ├──────────────┼───────────────────────────────┤ │ a │ O_WRONLY | O_CREAT | O_APPEND │ ├──────────────┼───────────────────────────────┤ │ r+ │ O_RDWR │ ├──────────────┼───────────────────────────────┤ │ w+ │ O_RDWR | O_CREAT | O_TRUNC │ ├──────────────┼───────────────────────────────┤ │ a+ │ O_RDWR | O_CREAT | O_APPEND │ └──────────────┴───────────────────────────────┘ 参数3 :如果第二个参数中有O_CREAT,该参数必须设置,表示文件的权限,如果不设置,该文件的权限是一个随机权限 文件最终的权限是由给定的权限 & ~umask 得到 终端的umask的值,可以通过指令 umask查看,可以通过指令 umask -n更改当前终端的umask的值 一般创建普通文件最大权限为:664 目录文件权限最大权限为:775 返回值:成功返回一个新的文件描述符,失败返回-1 并置位错误码。
3.3 close函数
1 2 3 4 5 6 #include <unistd.h> int close (int fd) ; 功能:关闭指定的文件描述符 参数:要关闭的文件描述符,关闭后,该文件描述符可以分配给其他文件使用 返回值:成功返回0 ,失败返回-1 并置位错误码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <myhead.h> int main (int argc, const char *argv[]) { int fd = -1 ; fd = open("./test.txt" , O_WRONLY|O_CREAT|O_TRUNC, 0664 ); if (fd == -1 ) { perror("open error" ); return -1 ; } printf ("fd = %d\n" , fd); close(fd); return 0 ; }
3.4 write\read函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <unistd.h> ssize_t write (int fd, const void *buf, size_t count) ; 功能:将buf指向的地址中count个字节,写入到fd指向的文件中 参数1 :文件描述符 参数2 :容器起始地址,void *类型,表明可以写入任何类型的数据 参数3 :要写入数据的个数 返回值:成功返回写入的字符个数,失败返回-1 并置位错误码 #include <unistd.h> ssize_t read (int fd, void *buf, size_t count) ; 功能:从fd文件中,将count个字节读取到buf对应的容器中 参数1 :文件描述符 参数2 :容器起始地址,void *类型,表明可以读取任何类型的数据 参数3 :要读取数据的个数 返回值:成功返回读取字节的个数,失败返回-1 并置位错误码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <myhead.h> int main (int argc, const char *argv[]) { int fd = -1 ; if ((fd = open("./test.txt" , O_WRONLY|O_CREAT|O_TRUNC, 0664 )) ==-1 ) { perror("open error" ); return -1 ; } printf ("fd = %d\n" , fd); char buf[128 ] = "hello world ni hao I love China\n" ; int res = write(fd, buf, strlen (buf)); printf ("res = %d\n" , res); close(fd); if ((fd = open("./test.txt" , O_RDONLY)) ==-1 ) { perror("open error" ); return -1 ; } printf ("fd = %d\n" , fd); char rbuf[128 ] = "" ; res = read(fd, rbuf, sizeof (rbuf)); printf ("res = %d\n" , res); write(stdout ->_fileno, rbuf, res); close(fd); return 0 ; }
练习:使用read、wriet完成两个文件的拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <myhead.h> int main (int argc, const char *argv[]) { if (argc != 3 ) { printf ("input file error\n" ); printf ("usage:./a.out srcfile dstfile\n" ); return -1 ; } int srcfd, dstfd; if ((srcfd = open(argv[1 ], O_RDONLY)) ==-1 ) { perror("open srcfile error" ); return -1 ; } if ((dstfd = open(argv[2 ], O_WRONLY|O_CREAT|O_TRUNC, 0664 )) ==-1 ) { perror("open dstfile error" ); return -1 ; } char buf[128 ] = "" ; while (1 ) { memset (buf, 0 , sizeof (buf)); int res = read(srcfd, buf, sizeof (buf)); write(dstfd, buf, res); if (res == 0 ) { break ; } } close(srcfd); close(dstfd); printf ("拷贝成功\n" ); return 0 ; }
3.5 光标移动(lseek)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <sys/types.h> #include <unistd.h> off_t lseek (int fd, off_t offset, int whence) ; 功能:移动光标位置 参数1 :要移动光标的文件描述符 参数2 :偏移量 >0 :表示向后偏移 =0 :表示不偏移 <0 :表示向前偏移 参数3 :偏移的起始位置 SEEK_SET:从文件开头偏移 SEEK_CUR:从文件光标当前位置偏移 SEEK_END:从文件末尾开始偏移 返回值:成功返回光标当前位置,失败返回(off_t )-1 并置位错误码 lseek = fseek + ftell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <myhead.h> int main (int argc, const char *argv[]) { int fd = -1 ; if ((fd = open("./bb.bmp" , O_RDONLY)) == -1 ) { perror("open error" ); return -1 ; } lseek(fd, 2 , SEEK_SET); unsigned int size; read(fd, &size, sizeof (unsigned int )); printf ("size = %d\n" , size); size = lseek(fd, 0 , SEEK_END); printf ("size = %d\n" , size); close(fd); return 0 ; }
3.6 关于文件描述符的拷贝问题
1> 使用赋值的情况直接拷贝,多个文件描述符变量,共享同一个文件光标,通过一个文件描述符进行移动,所有的文件描述符的光标都会改变
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <myhead.h> int main (int argc, const char *argv[]) { int fd1 = -1 ; if ((fd1 = open("./test.txt" , O_RDONLY)) == -1 ) { perror("open error" ); return -1 ; } printf ("fd1 = %d\n" , fd1); int fd2 = fd1; lseek(fd1, 12 , SEEK_SET); char buf[10 ] = "" ; read(fd2, buf, sizeof (buf)-1 ); printf ("buf = %s\n" , buf); return 0 ; }
2> 使用dup函数,完成文件描述符的拷贝
1 2 3 4 5 6 7 #include <unistd.h> int dup (int oldfd) ; 功能:通过旧的文件描述符,拷贝出一个新的文件描述符,新文件描述符遵循最小未分配原则 参数:旧文件描述符 返回值:新文件描述符,失败返回-1 并置位错误码 执行后,新旧两个文件描述符都指向同一个打开的文件,共享文件光标位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <myhead.h> int main (int argc, const char *argv[]) { int fd1 = -1 ; if ((fd1 = open("./test.txt" , O_RDONLY)) == -1 ) { perror("open error" ); return -1 ; } printf ("fd1 = %d\n" , fd1); int fd2 = dup(fd1); printf ("fd2 = %d\n" , fd2); lseek(3 , 12 , SEEK_SET); char buf[10 ] = "" ; read(4 , buf, sizeof (buf)-1 ); printf ("buf = %s\n" , buf); return 0 ; }
3> 使用dup2函数完成两个文件描述符的拷贝
1 2 3 4 5 6 7 int dup2 (int oldfd, int newfd) ; 功能:通过拷贝旧的文件描述符到新的文件描述符中 参数1 :旧文件描述符 参数2 :新文件描述符,如果newfd已经指向了某个已经打开的文件,则在进行拷贝之前,先将其关闭 返回值:成功返回新的文件描述符,失败返回-1 并置位错误码 注意:newfd不是使用最小为分配原则,因为newfd在调用之前可能已经指向某个文件,调用后,无论是newfd函数oldfd都指向oldfd指向的文件 通过该方式复制的文件描述符,依然共享同一个文件的光标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <myhead.h> int main (int argc, const char *argv[]) { int fd1 = -1 ; if ((fd1 = open("./test.txt" , O_RDONLY)) == -1 ) { perror("open error" ); return -1 ; } int fd2 = -1 ; if ((fd2 = open("./bb.bmp" , O_RDONLY)) == -1 ) { perror("open error" ); return -1 ; } printf ("fd1 = %d\n" , fd1); printf ("fd2 = %d\n" , fd2); dup2(fd1, fd2); lseek(3 , 12 , SEEK_SET); char buf[10 ] = "" ; read(4 , buf, sizeof (buf)-1 ); printf ("buf = %s\n" , buf); return 0 ; }
4> 多次使用open函数完成对同一个文件的打开时,不同的文件描述符使用的是独立的光标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <myhead.h> int main (int argc, const char *argv[]) { int fd1 = -1 ; if ((fd1 = open("./test.txt" , O_RDONLY)) == -1 ) { perror("open error" ); return -1 ; } int fd2 = -1 ; if ((fd2 = open("./test.txt" , O_RDONLY)) == -1 ) { perror("open error" ); return -1 ; } printf ("fd1 = %d\n" , fd1); printf ("fd2 = %d\n" , fd2); lseek(fd1, 12 , SEEK_SET); char buf[10 ] = "" ; read(fd2, buf, sizeof (buf)-1 ); printf ("buf = %s\n" , buf); return 0 ; }
四、文件属性获取(stat)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int stat (const char *pathname, struct stat *statbuf) ; 功能:将给定的文件的相关属性,通过statbuf返回出来 参数1 :要获取属性的文件路径是一个字符串 参数2 :文件属性结构体指针,需要传递一个文件属性类型的结构体变量 文件属性结构体类型: struct stat { dev_t st_dev; 设备id号 ino_t st_ino; 文件的inode号 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; 总大小,以字节为单位 blksize_t st_blksize; 块的大小 blkcnt_t st_blocks; 块的个数 }; 关于文件类型和文件权限的介绍 使用 st_mode & S_IFMT后的到文件的类型 S_IFSOCK 0140000 socket 套接字文件类型 S_IFLNK 0120000 symbolic link 链接文件类型 S_IFREG 0100000 regular file 普通文件类型 S_IFBLK 0060000 block device 块设备文件类型 S_IFDIR 0040000 directory 目录文件类型 S_IFCHR 0020000 character device 字符设备文件类型 S_IFIFO 0010000 FIFO 管道文件类型 使用 st_mode & 0777 后得到文件的权限 返回值:成功返回0 ,失败返回-1 并置位错误码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <myhead.h> int main (int argc, const char *argv[]) { struct stat sb ; stat(argv[1 ], &sb); switch (sb.st_mode&S_IFMT) { case S_IFSOCK: { printf ("这是套接字文件\t" ); } break ; case S_IFLNK: { printf ("这是链接文件\t" ); } break ; case S_IFREG: { printf ("这是普通文件\t" ); } break ; case S_IFBLK: { printf ("这是块设备文件\t" ); } break ; case S_IFDIR: { printf ("这是目录文件\t" ); } break ; case S_IFCHR: { printf ("这是字符设备文件\t" ); } break ; case S_IFIFO: { printf ("这是管道文件\t" ); } break ; } printf ("%#o\t%ld\t%ld\n" , sb.st_mode&0777 , sb.st_size, sb.st_ino); return 0 ; }
五、目录相关操作
5.1 opendir函数
1 2 3 4 5 6 7 #include <sys/types.h> #include <dirent.h> DIR *opendir (const char *name) ; 功能:打开一个指定的目录,并返回该目录的目录指针 参数:要打开的目录,是一个字符串 返回值:成功返回目录指针,失败返回NULL 并置位错误码
5.2 closedir函数
1 2 3 4 5 6 7 8 #include <sys/types.h> #include <dirent.h> int closedir(DIR *dirp); 功能:关闭一个已经打开的目录指针 参数:目录指针 返回值:成功返回0,失败返回-1并置位错误码
5.3 readdir函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <dirent.h> struct dirent *readdir (DIR *dirp) ; 功能:读取指定目录中的下一个文件或目录的信息 参数:目录指针 返回值:成功返回当前文件或目录的信息,失败返回NULL 并置位错误码 struct dirent { ino_t d_ino; off_t d_off; 光标偏移量 8 字节 unsigned short d_reclen; 当前记录的大小 2 字节 unsigned char d_type; char d_name[256 ]; 文件名 }; 关于文件或目录类型 DT_BLK This is a block device. DT_CHR This is a character device. DT_DIR This is a directory. DT_FIFO This is a named pipe (FIFO) . DT_LNK This is a symbolic link. DT_REG This is a regular file. DT_SOCK This is a UNIX domain socket. DT_UNKNOWN The file type could not be determined.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 #include <myhead.h> int main (int argc, const char *argv[]) { if (argc != 2 ) { printf ("input error\n" ); printf ("usage:./a.out name\n" ); return -1 ; } DIR *dp = NULL ; if ((dp = opendir(argv[1 ])) == NULL ) { perror("opendir error" ); return -1 ; } struct dirent *sdp = NULL ; while ((sdp = readdir(dp)) != NULL ) { printf ("inode:%10ld, size:%10d, %10s, " ,\ sdp->d_ino, sdp->d_reclen, sdp->d_name); switch (sdp->d_type) { case DT_BLK: { printf ("b\n" ); } break ; case DT_CHR: { printf ("c\n" ); } break ; case DT_DIR: { printf ("d\n" ); } break ; case DT_FIFO: { printf ("p\n" ); } break ; case DT_LNK: { printf ("l\n" ); } break ; case DT_REG: { printf ("-\n" ); } break ; case DT_SOCK: { printf ("s\n" ); } break ; } } closedir(dp); return 0 ; }