ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linux C语言文件操作:从标准IO到文件IO的全面解析

Linux C语言文件操作:从标准IO到文件IO的全面解析 一、Linux文件操作概述在Linux系统编程中文件操作是核心技能之一。理解文件操作的本质需要先明确内存与外存的区别内存变量、数组、数据结构读写数据速度快程序执行结束、掉电后数据丢失空间小价格昂贵文件外存/硬盘读写速度比内存慢掉电后数据不丢失空间大相对内存价格便宜文件的主要作用有两个一是长期保存掉电不丢失的数据二是作为Linux提供给应用层的硬件操作接口体现了“一切皆文件”的设计哲学。二、Linux下的文件类型Linux系统将一切资源都抽象为文件通过ls -l 文件名命令可以查看文件的详细信息。文件类型由输出行首的第一个字符标识-普通文件regular file如main.c、xxx.txt、a.outd目录文件directory如文件夹l软连接文件link相当于快捷方式符号链接b块设备文件block如硬盘等存储设备c字符设备文件character如鼠标、键盘、显示器s套接字文件socket用于网络编程p管道文件pipe用于进程间通信三、Linux文件操作方法Linux下的文件操作主要分为两类标准IO标准C库提供的文件操作接口属于库函数文件IOLinux内核提供的文件操作接口属于系统调用无论使用哪种方法文件操作的基本思想都是相同的打开文件读文件、写文件关闭文件四、标准IO操作详解4.1 打开文件fopen()#include stdio.h FILE *fopen(const char *pathname, const char *mode);功能打开一个文件并获得一个文件流指针参数pathname要打开的文件的文件名mode打开方式r以只读方式打开文件必须要存在r以读写方式打开文件必须要存在w以只写方式打开文件不存在则创建文件存在则清空w以读写方式打开文件不存在则创建文件存在则清空a以只写方式打开文件不存在则创建写时追加写a以读写方式打开文件不存在则创建读从开头读写从末尾写返回值成功FILE*文件流指针失败NULL4.2 关闭文件fclose()int fclose(FILE *stream);功能关闭文件参数stream已打开的文件的文件流指针返回值成功0失败EOF-1注意只打开文件而使用完不关闭文件会造成文件描述符泄露。只有fclose或fflush之后数据才会从“用户态缓冲区”强制输出到“内核缓冲区”即操作系统管理的页缓存从而让其他打开的文件流能够看到它。4.3 标准流操作系统默认已打开的三个标准流stdin标准输入流对应标准输入设备键盘stdout标准输出流对应标准输出设备显示屏/终端stderr标准出错流对应标准出错设备显示屏/终端4.4 字符读写fputc()和fgetc()int fputc(int c, FILE *stream);功能向文件中写入一个字符参数c要写入的字符或者字符对应的ASCII值stream要写入的文件的文件流指针返回值成功写入的字符失败EOF-1注意putchar(a); fputc(a, stdout)int fgetc(FILE *stream);功能从文件中读取一个字符参数stream要读取的文件的文件流指针返回值成功返回读到的字符失败EOF-1读到文件末尾EOF-14.5 字符串读写fputs()和fgets()int fputs(const char *s, FILE *stream);功能将一个字符串写入到文件不包括\0字符(0)参数s要写入的字符串的首地址stream要写入的文件的文件流指针返回值成功0失败EOF-1char *fgets(char *s, int size, FILE *stream);功能从文件中读取一行遇到新行结束参数s存储读取到的数据的空间首地址size期待读到的字节数最多size-1stream要读的文件的文件流指针返回值成功:返回s存储数据的空间首地址失败NULL到达文件末尾NULL4.6fwrite与freadsize_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);功能向文件中写入大小是size的nmemb个元素参数ptr要写入的元素的首地址size每个元素的字节数nmemb要写入的元素的个数stream要写入的文件流指针返回值成功实际写入的元素个数失败 nmembsize_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);功能从文件中读取大小是size的nmemb个元素到ptr指针指向的内存空间参数ptr用来存储读到数据的空间首地址size每个元素的字节数nmemb希望读取的元素个数stream要读的文件流指针返回值成功返回实际从文件中读到的元素个数当文件中不足一个元素时返回0size与nmemb不可交换否则可能出现当读取数据不足一个元素时返回04.7流定位函数fseekint fseek(FILE *stream, long offset, int whence);功能重新定位流的位置参数stream要定位的文件流指针offset偏移量字节whence要偏移的相对位置SEEK_SET : 从文件开头偏移SEEK_CUR 从当前位置偏移SEEK_END 从文件末尾偏移返回值成功0失败-1抢占磁盘空间创建空洞文件提前抢占磁盘空间加\0才可不然只移动光标不会预分配空间ftelllong ftell(FILE *stream);功能获取流所在位置到文件开头的偏移量rewindvoid rewind(FILE *stream);功能将流复位到开头求文件大小fseek(fp, 0, SEEK_END); len ftell(fp); rewind(fp);五、时间相关函数time#include time.htime_t time(time_t *tloc);功能获取1970-1-1 000000到现在的秒数参数tloc保存秒数的变量的地址返回值获得的秒数ctimechar *ctime(const time_t *timep);功能将秒数转换成字符串时间参数timep 秒数的变量的地址返回值字符串时间localtiome()struct tm *localtime(const time_t *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 */};六、文件IO文件IO是Linux内核提供的文件操作接口属于系统调用。与标准IO相比文件IO直接使用文件描述符进行操作。6.1 打开文件open()#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);功能打开一个文件并获得一个文件描述符参数pathname要打开的文件的文件名flags打开方式O_RDONLY只读O_WRONLY只写O_RDWR读写O_CREAT创建O_TRUNC清空O_APPEND追加mode用户自己、同组用户、其他用户对该文件的读写执行权限普通文件0664权限给满0777返回值成功文件描述符失败-16.2 关闭文件close()#include unistd.h int close(int fd);功能关闭文件参数fd文件描述符返回值成功0失败-1注意只打开文件使用完不关闭文件会造成文件描述符泄露。6.3 文件描述符当打开一个文件时系统会为已打开的文件分配一个文件描述符用来标记该文件。文件描述符是小的、非负的整型数据。操作系统默认可分配出的文件描述符0-1023总共1024个。文件描述符的分配原则最小未被使用。系统默认已打开的文件标准IO文件IOFILE*intstdin0STDIN_FILENOstdout1STDOUT_FILENOstderr2STDERR_FILENO6.4 读写文件write()和read()ssize_t write(int fd, const void *buf, size_t count); ssize_t read(int fd, void *buf, size_t count);write()功能向文件中写入数据read()功能从文件中读取数据返回值成功实际写入/读取的字节数失败-1到达文件末尾read06.5 文件定位lseek()off_t lseek(int fd, off_t offset, int whence);功能重新定位文件的读写位置参数fd文件描述符offset偏移量whence要偏移的起始位置SEEK_SET从文件开头偏移SEEK_CUR从当前读写位置偏移SEEK_END从文件末尾偏移返回值成功返回当前读写位置到文件开头的偏移量失败-16.6 文件拷贝示例#include stdio.h #include sys/types.h #include sys/stat.h #include fcntl.h #include unistd.h int file_copy(const char *srcfile, const char *dstfile) { int fdsrc open(srcfile, O_RDONLY); int fddst open(dstfile, O_WRONLY | O_CREAT | O_TRUNC, 0664); if (-1 fdsrc || -1 fddst) { printf(open error\n); return -1; } char buff[1024] {0}; ssize_t size 0; while ((size read(fdsrc, buff, sizeof(buff))) 0) { write(fddst, buff, size); } close(fdsrc); close(fddst); return 0; }七、文件IO和标准IO的区别文件IO和标准IO在Linux文件操作中各有特点主要区别如下特性标准IO文件IO操作对象FILE*文件流指针fd文件描述符来源标准C库函数Linux系统调用可移植性强跨平台弱Linux特有缓冲区有缓冲区无缓冲区效率减少用户层和内核层反复切换提高效率实时性好适用场景主要用在对普通文件操作普通文件、硬件设备类相关的文件7.1 缓冲区类型标准IO使用缓冲区机制主要分为三种类型1. 行缓冲默认大小1024字节1K主要使用在人机交互终端上刷新条件遇到\n刷新程序结束刷新使用fflush()强制刷新缓冲区满时自动刷新2. 全缓冲默认大小4096字节4K主要用于文件交互刷新条件程序结束刷新文件关闭自动刷新全缓冲区满时自动刷新使用fflush()强制刷新3. 无缓冲0K主要用于出错信息输出标准出错设备stderr7.2 打开方式对应关系标准IO与文件IO的打开方式对应关系r→O_RDONLYr→O_RDWRw→O_WRONLY | O_TRUNC | O_CREAT0664w→O_RDWR | O_TRUNC | O_CREAT0664a→O_WRONLY | O_CREAT | O_APPEND0664a→O_RDWR | O_CREAT | O_APPEND0664八、其他1、输出流int fprintf(FILE *stream, const char *format, ...);功能将格式化后的字符串写入到stream文件流代表的文件中。int dprintf(int fd, const char *format, ...);功能将格式化后的字符串写入到fd所代表的文件中。*int sprintf(char *str, const char *format, ...);功能将格式化后的字符串写入到字符型数组中字符串的拼接格式化可以将整形数据、浮点型数据等转换成字符串函数目标对象数据类型核心场景printf标准输出屏幕FILE*stdout默认终端打印fprintf任何文件流如fopen返回的指针FILE*如fp、stderr写入文件、写入错误输出dprinf文件描述符如open返回的整数int fd如STDOUT_FILENO、fd系统调用级别的直接写入无缓冲区卡顿sprinf内存缓冲区char[]数组char*拼字符串做网络包存到变量里char *strtok(char *str, const char *delim);功能字符串截取函数参数str要截取的原始字符串delim要截断的分隔符返回值成功截取到的子字符串的首地址失败NULL已经到字符串末尾2.实战示例1 使用fgetc读取文件内容#include stdio.h int main(int argc, const char *argv[]) { if (argc 2) { printf(Usage : ./a.out filename\n); return -1; } FILE *fp fopen(argv[1], r); if (NULL fp) { printf(fopen error\n); return -1; } int ch; while ((ch fgetc(fp)) ! EOF) { putchar(ch); } fclose(fp); return 0; }2 实现cp命令功能#include stdio.h int main(int argc, const char *argv[]) { if (argc 3) { printf(Usage: ./a.out srcfile dstfile\n); return -1; } FILE *fpsrc fopen(argv[1], r); FILE *fpdst fopen(argv[2], w); if (NULL fpsrc || NULL fpdst) { printf(fopen error\n); return -1; } int ret 0; while (1) { ret fgetc(fpsrc); if (EOF ret) { break; } fputc(ret, fpdst); } fclose(fpsrc); fclose(fpdst); return 0; }3、主函数传参可以在程序运行时利用命令行参数向主函数传参int main(int argc, const char *argv[]) { // argc命令行传递的参数个数 // argv保存多个char*类型指针的数组 // argv[0] ---- ./mycat // argv[1] ---- 1.txt // argv[2] ---- aaa // ... }示例执行./mycat 1.txt aaa bbb ccc时argc 4argv[0] ./mycatargv[1] 1.txtargv[2] aaaargv[3] bbbargv[4] ccc4、man手册使用Linux提供了完善的man手册系统帮助开发者查阅文档Linux命令man ls或man 1 ls系统调用man open或man 2 open库函数man fopen、man fread、man fclose
返回列表