Linux应用软件编程02-文件IO

Linux应用软件编程02-文件IO
一、文件IO介绍① Linux内核向应用层提供的文件操作方法属于Linux系统调用。② 操作方法打开文件 open读写文件 read/write关闭文件 close③文件描述符file descriptor小的非负的整形数据1024个0-1023操作系统为已打开的文件分配的一个标识符。分配原则最小未被使用原则文件IO操作文件面向文件描述符。④ 操作系统默认已经打开了三个文件标准输入设备、标准输出设备、标准出错设备设备类型文件描述符fd文件流指针FILE*标准输入设备0 (STDIN_FILENO)stdin标准输出设备1 (STDOUT_FILENO)stdout标准错误设备2 (STDERR_FILENO)stderr二、文件IO的API接口函数2.1 open打开文件① 函数原型int open(const char *pathname, int flags);int open(const char *pathname, int flags, mode_t mode);② 功能打开一个文件③ 参数pathname要打开文件的文件名flags打开方式O_RDONLY :只读O_WRONLY 只写O_RDWR 可读可写O_CREAT 创建O_APPEND 追加写O_TRUNC 清空r -- O_RDONLYr -- O_RDWRw -- O_WRONLY | O_CREAT | O_TRUNCw -- O_RDWR | O_CREAT | O_TRUNCa -- O_WRONLY | O_CREAT | O_APPENDa -- O_RDWR | O_CREAT | O_APPENDmode 设置当前用户、同组用户、其他人对文件的读写执行权限创建普通文件时一般0664即可*当flags标志位中存在O_CREAT方式则必须增加mode参数111 111 111 ----- 0777(八进制)文件权限 mode ~umask操作系统中的掩码umask 查看-- 0002④ 返回值成功文件描述符失败-1#include stdio.h #include sys/types.h #include sys/stat.h #include fcntl.h #include unistd.h int main(int argc, const char *argv[]) { int fd open(2.txt, O_WRONLY | O_CREAT | O_TRUNC, 0664); //w r if (fd 0) { printf(open error\n); return -1; } printf(fd %d\n, fd); //fd 3 close(fd); return 0; }2.2 close关闭文件① 函数原型int close(int fd);② 功能关闭文件③ 参数fd文件描述符④ 返回值成功0失败-1⑤ 注意对于打开的文件使用完时要及时关闭。由于文件描述符个数有限光打开文件不关闭会造成文件描述符泄露。2.3 write① 函数原型ssize_t write(int fd, const void *buf, size_t count);② 功能向文件中写入数据③ 参数fd要写的文件的文件描述符buf要写入的数据首地址count要写入的字节数④ 返回值成功返回实际写入的字节数失败-1#include unistd.h #include sys/types.h #include sys/stat.h #include fcntl.h #include stdio.h #include stdlib.h #include string.h int main(int argc, const char *argv[]) { int fd open(1.txt, O_WRONLY | O_CREAT | O_TRUNC, 0664); if (fd 0) { printf(open error\n); return -1; } int cnt 100; char str[] {how are you}; //注意 str 的长度为 11不含末尾自动添加的 \0 ssize_t size write(fd, hello world, strlen(hello world)); printf(size %ld\n, size); //size 11 write(fd, str, strlen(str)); write(fd, cnt, sizeof(int)); close(fd); return 0; } //输出 cat 1.txt hello worldhow are youd2.4 read① 函数原型ssize_t read(int fd, void *buf, size_t count);② 功能从文件中读数据③ 参数fd文件描述符buf存储数据的空间首地址count期望读到的字节数④ 返回值成功返回实际读到的字节数失败-1到达文件末尾0#include unistd.h #include sys/types.h #include sys/stat.h #include fcntl.h #include stdio.h #include stdlib.h #include string.h int main(int argc, const char *argv[]) { int fd open(1.txt, O_RDONLY); if (fd 0) { printf(open error\n); return -1; } char buff[512] {0}; ssize_t size read(fd, buff, sizeof(buff)); printf(size %ld, buff %s\n, size, buff); //第一次 read 会读取全部内容第二次 read 将返回 0 size read(fd, buff, sizeof(buff)); printf(size %ld, buff %s\n, size, buff); close(fd); return 0; } //输出 size 26, buff hello worldhow are youd size 0, buff hello worldhow are youd⑤ 使用文件io实现cat功能#include unistd.h #include sys/types.h #include sys/stat.h #include fcntl.h #include stdio.h #include stdlib.h #include string.h int main(int argc, const char *argv[]) { int fd open(stdio.h, O_RDONLY); if (fd 0) { printf(open error\n); return -1; } char buff[512] {0}; while (1) { ssize_t size read(fd, buff, sizeof(buff)); if (size 0) { break; } //size读多少写多少 write(1, buff, size); //标准输出设备1stdout //printf(%s, buff); } close(fd); return 0; }⑥ 使用文件IO实现文件的拷贝cp#include unistd.h #include sys/types.h #include sys/stat.h #include fcntl.h #include stdio.h #include stdlib.h #include string.h int copy_file(const char *srcname, const char *dstname) { int fdsrc open(srcname, O_RDONLY); int fddst open(dstname, O_WRONLY | O_CREAT | O_TRUNC, 0664); if (fdsrc 0 || fddst 0) { printf(open error\n); return -1; } char buff[1024] {0}; while (1) { ssize_t size read(fdsrc, buff, sizeof(buff)); if (size 0) //0 :文件末尾 0 :出错 { break; } write(fddst, buff, size); } close(fdsrc); close(fddst); return 0; } int main(int argc, const char *argv[]) { if (argc 3) { printf(Usage : ./a.out srcfile dstfile\n); return -1; } copy_file(argv[1], argv[2]); return 0; }2.5 strtok字符串分割① 函数原型char *strtok(char *str, const char *delim);② 功能字符串分割③ 参数str要分割字符串的首地址delim分隔符④ 返回值成功返回分隔到的字符串的首地址失败NULL#include unistd.h #include sys/types.h #include sys/stat.h #include fcntl.h #include stdio.h #include stdlib.h #include string.h int main(int argc, const char *argv[]) { char buff[] {abate v. make or become less\r\n}; char *pword strtok(buff, ); if (pword ! NULL) { printf(pword %s\n, pword); char *pmean strtok(NULL, \r\n); if (pmean ! NULL) { printf(pmean %s\n, pmean); } } return 0; }⑤ 从终端获取用户输入的单词通过查询文件输出对应的意思#include unistd.h #include sys/types.h #include sys/stat.h #include fcntl.h #include stdio.h #include stdlib.h #include string.h int main(int argc, const char *argv[]) { FILE *fp fopen(./dict.txt, r); if (NULL fp) { printf(fopen error ); return -1; } char buff[1024] {0}; char word[64] {0}; char *pw NULL; //单词 char *pm NULL; //释义 while (1) { //将文件位置指针重置到文件开头确保每次查询都从文件开头开始扫描 rewind(fp); //获取用户输入的单词存入 word中 fgets(word, sizeof(word), stdin); //计算 word 长度将最后一个字符应为 \n替换为 \0 word[strlen(word)-1] \0; //比较两个字符串是否相等区分大小写。相等返回 0 if (!strcmp(word, .quit)) { break; } while(1) { //从 fp 指向的文件读取一行最多 1023 字符存入 buff char *p fgets(buff, sizeof(buff), fp); if (NULL p) { printf(No this word\n); break; } //将字符串 buff 按分隔符 空格分割 //strtok 会修改原字符串将空格替换为 \0 pw strtok(buff, ); if (pw ! NULL) { if (0 strcmp(pw, word)) { //继续分割同一个字符串传入 NULL分隔符改为 \r\n遇到 \r 或 \n 停止 pm strtok(NULL, \r\n); if (pm ! NULL) { printf(%s : %s\n, pw, pm); break; } } } } } fclose(fp); return 0; }2.6 lseek文件位置定位函数① 函数原型off_t lseek(int fd, off_t offset, int whence);② 功能文件读写位置偏移和定位③ 参数fd需要重定位的文件offset偏移量字节whence要偏移的起始位置SEEK_SET 文件开头SEEK_CUR 文件当前读写位置SEEK_END 文件末尾④ 返回值成功返回偏移后的位置到文件开头的偏移量失败-1⑤ 求文件大小lenoff_t len lseek(fd, 0, SEEK_END);lseek(fd, 0, SEEK_SET); //复位#include unistd.h #include sys/types.h #include sys/stat.h #include fcntl.h #include stdio.h #include stdlib.h #include string.h int main(int argc, const char *argv[]) { int fd open(1.txt, O_WRONLY | O_CREAT | O_TRUNC, 0664); if (fd 0) { printf(open error\n); return -1; } //将文件偏移量设置为从文件开头向后移动 5 个字节即第 6 个字节位置索引从 0 开始 off_t offset lseek(fd, 5, SEEK_SET); //从当前偏移位置索引 5开始写入 1 个字节字符 A //入后文件偏移量自动前进到 6索引 6 write(fd, A, 1); printf(offset %ld\n, offset); //offset 5 //从当前偏移量6再向后移动 5 个字节返回新偏移量 11 offset lseek(fd, 5, SEEK_CUR); //在偏移 11 处写入字符 B写完后偏移变为 12。 write(fd, B, 1); printf(offset %ld\n, offset); //offset 11 //求文件的大小 offset lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); //将文件偏移量重置到文件开头索引 0 printf(len %ld\n, offset); //len 12 close(fd); return 0; } od -c 1.txt 0000000 \0 \0 \0 \0 \0 A \0 \0 \0 \0 \0 B三、文件IO和标准IO的区别文件IO标准IO面向文件描述符fd面向文件流指针FILE *由Linux内核提供属于系统调用跨平台可移植性弱C标准库函数跨平台可移植性强无缓冲区所以可以用在对硬件的控制上有缓冲区实现数据缓存避免频繁进行用户空间和内核空间切换带来的时间消耗可以操作普通文件也可以操作硬件相关的设备类文件管道套接字等主要使用在对普通文件-的读写上文本文件操作居多四、缓冲区① 高速设备和低速设备进行交互时为了匹配低速设备的速率需要在高速设备和低速设备之间增加一个缓冲区用于数据缓存。比如队列② 行缓冲1KB主要用于终端人机交互如stdout行缓存多是关于终端的一些操作。刷新条件遇到换行符\n刷新缓冲区满刷新程序正常结束刷新调用fflush(stdout)强制刷新。③ 全缓冲4KB主要用于文件读写操作-- 普通文件的操作对普通文件进行标准IO操作建立的缓存一般为全缓存。刷新条件缓冲区满4096字节刷新程序正常结束刷新调用fflush(fp)强制刷新文件关闭时自动刷新④ 无缓冲0KB主要用于出错处理信息的输出如stderr不对数据缓存直接刷新典型应用printf();输出到stdoutfprintf(stderr, fopen error %s, filename)错误提示适用于需要即时显示的交互信息和错误处理int main(int argc, const char *argv[]) { //将格式化字符串输出到指定的输出流 //stderr 默认是无缓冲因此字符串会立即输出到终端 fprintf(stderr, hello world); FILE *fp fopen(1.txt, w); // fflush(fp); // fclose(fp); while (1) { fputs(hello world, fp); usleep(10000); } return 0; }五、出错处理相关函数errno全局的错误码变量程序运行过程中会将对应的函数调用出错信息保存在这个变量中。5.1 strerror① 函数原型char *strerror(int errnum);② 功能将错误码装换成对应的错误信息以字符串方式返回③ 参数errnum错误码④ 返回值返回错误码对应的错误描述信息⑤ 通过循环调用strerror打印系统错误码#include unistd.h #include sys/types.h #include sys/stat.h #include fcntl.h #include stdio.h #include stdlib.h #include string.h int main(int argc, const char *argv[]) { for (int i 0; i 200; i) { printf(%d : %s\n, i, strerror(i)); } return 0; }5.2 perror① 函数原型void perror(const char *s);② 功能打印出错信息和出错原因③ 参数s自定义的错误信息5.3 error① 函数原型void error(int status, int errnum, const char *format, ...);② 功能打印自定义的错误信息error(1, errno, %s : %s : %d :open error: aaa, __FILE__, __func__, __LINE__);③ 参数status状态值0 SUCCESS1FAILerrnum错误码errnoformat格式化后错误信息字符串④ c语言内置宏__FILE__ 表示是那个文件__LINE__ 表示第几行__func__ 表示在那个函数 // __FUNCTION____DATE__表示日期#include unistd.h #include sys/types.h #include sys/stat.h #include fcntl.h #include stdio.h #include stdlib.h #include string.h #include errno.h #include error.h int main(int argc, const char *argv[]) { int fd open(aaa, O_RDONLY); if (fd 0) { error(1, errno, %s : %s : %d :open error: aaa, __FILE__, __func__, __LINE__); //printf(open error : %s\n, strerror(errno)); //perror(open error); // return -1; } printf(Ok!); return 0; }六、目录操作打开目录 opendir读目录 readdir关闭目录 closedir6.1 opendir① 函数原型DIR *opendir(const char *name);② 功能打开一个目录并获得一个目录流指针③ 参数name目录名④ 返回值成功目录流指针失败NULL6.2 closedir① 函数原型int closedir(DIR *dirp);② 功能关闭参数dirp所指向的目录流③ 参数dirp指向要关闭的目录流的DIR*指针④ 返回值成功返回0失败返回-1并设置全局变量errno以指示具体错误6.3 readdir① 函数原型struct dirent *readdir(DIR *dirp);② 功能读取目录中的文件信息③ 参数dirp目录流指针④ 返回值成功返回文件信息的结构体指针失败NULLstruct dirent {ino_t d_ino; /* Inode number */off_t d_off; /* Not an offset; see below */unsigned short d_reclen; /* Length of this record */unsigned char d_type; /* Type of file; not supportedby all filesystem types */char d_name[256]; /* Null-terminated filename */};#include unistd.h #include sys/types.h #include sys/stat.h #include fcntl.h #include stdio.h #include stdlib.h #include string.h #include dirent.h int main(int argc, const char *argv[]) { DIR *pdir opendir(..); //打开当前进程工作目录的上一层目录 if (NULL pdir) { perror(opendir error); return -1; } while (1) { struct dirent *pinf readdir(pdir); if (NULL pinf) { break; } if (. pinf-d_name[0]) { continue; } printf(%ld %s\n, pinf-d_ino, pinf-d_name); } closedir(pdir); return 0; }6.4 mkdir① 函数原型int mkdir(const char *pathname, mode_t mode);② 功能创建一个目录③ 参数pathname目录名mode对目录的读写执行权限 0777④ 返回值成功0失败-1#include unistd.h #include sys/types.h #include sys/stat.h #include fcntl.h #include stdio.h #include stdlib.h #include string.h int main(int argc, const char *argv[]) { int ret mkdir(dir, 0777); if (ret 0) { perror(mkdir error); return -1; } return 0; }6.5 getcwd① 函数原型char *getcwd(char *buf, size_t size);② 功能获取当前工作路径③ 参数buf 存储当前路径的空间size空间大小④ 返回值成功buf的首地址失败NULL6.6 chdir① 函数原型int chdir(const char *path);② 功能修改当前工作路径③ 参数path新的工作路径④ 返回值成功0失败-1⑤ chmod 八进制值 文件名 --- 修改该文件的读写执行权限chmod 0777 1.txt⑥ pwd 获取当前目录对应的绝对路径#include unistd.h #include sys/types.h #include sys/stat.h #include fcntl.h #include stdio.h #include stdlib.h #include string.h int main(int argc, const char *argv[]) { char path[128] {0}; getcwd(path, sizeof(path)); printf(path : %s\n, path); chdir(..); mkdir(ccc, 0777); getcwd(path, sizeof(path)); printf(path : %s\n, path); return 0; }七、时间相关函数timectimelocaltime7.1time① 函数原型time_t time(time_t *tloc);② 功能获取1970-1-1 00:00:00 到现在的秒数③ 参数tloc保存秒数的变量地址④ 返回值返回秒数7.2ctime① 函数原型char *ctime(const time_t *timep);② 功能将秒数转换成字符串时间③ 参数timep秒数的地址④ 返回值返回时间字符串7.3localtime① 函数原型struct tm *localtime(const time_t *timep);② 功能将秒数转换成日历时间③ 参数timep秒数的地址④ 返回值返回具体时间的结构体指针struct tm {int tm_sec; /* Seconds (0-60) */int tm_min; /* Minutes (0-59) */int tm_hour; /* Hours (0-23) */int tm_mday; /* Day of the month (1-31) */int tm_mon; /* Month (0-11) */int tm_year; /* Year - 1900 */int tm_wday; /* Day of the week (0-6, Sunday 0) */int tm_yday; /* Day in the year (0-365, 1 Jan 0) */int tm_isdst; /* Daylight saving time */};#include stdio.h #include time.h int main(int argc, const char *argv[]) { time_t sec; time(sec); printf(sec %ld\n,sec); char *ptime ctime(sec); printf(ptime : %s\n,ptime); struct tm *pt localtime(sec); printf(%d-%02d-%02d %02d:%02d:%02d\n, pt-tm_year1900, pt-tm_mon1, pt-tm_mday, pt-tm_hour,pt-tm_min,pt-tm_sec); return 0; } //输出 sec 1783733363 ptime : Sat Jul 11 09:29:23 2026 2026-07-11 09:29:23