Linux基础文件编程篇 Linux一切皆是文件文件系统(文件夹文件)硬件设备,管道,数据库,Socket等一、文件编程概述: 1。应用中比如:账单,游戏进度,配置文件等。2。用代码操作文件:实现文件创建,打开,编辑等自动化执行。二、计算机如何帮我们自动化完成以上操作呢? 操作系统提供了一系列的API如Linux系统: 打开open 读写writeread 光标定位lseek 关闭close三、文件打开及创建 打开创建文件 头文件: includeincludeincludefcntl。h intopen(constcharpathname,intflags);(参数1:字符指针指向文件路径,参数2:整型数权限) intopen(constcharpathname,intflags,modetmode);open返回值是文件描述符没有文件返回值就无法用write();和read();参数说明: pathname:要打开的文件名(含路径,缺省为当前路径)flags: ORDONLY只读打开 OWRONLY只写打开 ORDWR可读可写可打卡 当我们附带了权限后,打开的文件就只能按照这种权限来操作。以上这三个常数中应当只指定一个。intcreat(constcharfilename,modetmode)filename:要创建的文件名(包含路径,缺省为当前路径)mode:创建模式可读可写可执行 常见创建模式:宏表示数字SIRUSR4可读SIWUSR2可写SIXUSR1可执行SIRWXU7可读,写,执行示例代码:(在路径HomeCLC下创建了file1可读可写可执行文件)includesystypesincludesysstat。hincludefcntl。hincludestringincludeunistd。hintmain(){fdcreat(homeCLCfile,SIRWXU);SIRWXU可读可写可执行return0;} 下列常数是可选择的:1。OCREA若文件不存在则创建它。 使用此选项时,需要同时说明第三个参数mode,用其说明该新文件的存取许可权限。 mode:一定是在flags中使用了OCREAT标志,mode记录待创建的文件的访问权限。 OCREAT若文件不存在则创建它示例代码:includesystypes。hincludesysstat。hincludefcntl。hincludestdio。hintmain(){定义整型返回值fdopen(。file,ORDWR);open指令打开可读可写file文件返回值printf(fdd,fd);if(fd1){若返回值为1没有file文件printf(openfilefailed);fdopen(。file,ORDWROCREAT,0600);open指令若没有file文件OCREAT创建file文件权限0600642所以是rw可读可写if(fd0){printf(creatfilesuccess);}}printf(fdd,fd);return0;} 结果: 注意: fdopen(。file,ORDWR);open指令打开可读可写file文件返回值 fdopen(。file,ORDWROCREAT,0600); open指令若没有file文件OCREAT创建file文件权限0600642所以是rw可读可写权限0600的含义: 表示普通文件r(4)可读w(2)可写rw表示可读可写x(1)执行0600:642所以是rw可读可写0是同组0其他组2。OEXCL如果同时指定了OCREAT,而文件已经存在,则打开失败或者返回1 OEXCL如果同时指定了OCREAT,而文件已经存在,则打开失败或者返回1示例代码:includesystypes。hincludesysstat。hincludefcnt。hincludestdio。hintmain(){fdopen(。file,ORDWROCREATOEXCL,0600);if(fd1){若返回值为1没有file文件printf(fileexist);return0;}} 注意: fdopen(。file,ORDWROCREATOEXCL,0600);3。OAPPEND每次写时都加到文件的尾端。 OAPPEND每次写时都加到文件的尾端示例代码:includesystypes。hincludesysstat。hincludefcntl。hincludestdio。hintmain(){charbuf19990330;fdopen(。file,ORDWROAPPEND);printf(opensusceess:fdd,fd);intnwritewrite(fd,buf,strlen(buf));if(nwrite!1){printf(writedbytetofile,nwrite);}close(fd);return0;} 注意: fdopen(。file,ORDWROAPPEND);4。OTRUNC属性去打开文件时,如果这个文件中本来是有内容的,而且为只读或者只写成功打开,则将其长度截断为0。 OTRUNC属性去打开文件时,如果这个文件中本来是有内容的,把原先内容全部覆盖掉,示例代码:includesystypes。hincludesysstat。hincludefcntl。hincludestdio。hintmain(){charbuf19990330;fdopen(。file,ORDWROTRUNC);printf(opensusceess:fdd,fd);intnwritewrite(fd,buf,strlen(buf));if(nwrite!1){printf(writedbutetofile,nwrite);}close(fd);return0;} 注意: fdopen(。file,ORDWROTRUNC); lsl是把文件所有内容列出来 rwxrxrx1CLCbook8478Fed912:36moderwrr1CLCbook385Fed912:36mode。crw1CLCbook0Fed912:44file 表示普通文件r(4)可读w(2)可写rw表示可读可写x(1)执行四、文件写入操作编程 写入文件:write头文件:includeunistd。h ssizetwrite(intfd,constvoidbuf,sizetcount); 将缓冲区buf指针指向内存数据,写conuct的大小到fd里。 (intfd,constvoidbuf,sizetcount)参数:(文件描述符,【无类型指针是一个缓冲区】,写入文件的大小)文件写入操作示例代码:includesystypes。hincludesysstat。hincludefcntl。hincludestdio。hincludestring。hincludeunistd。hintmain(){charbufRefuel。CONG;fdopen(。file,ORDWR);printf(fdd,fd);if(fd1){printf(openfilefailed);fdopen(。file,ORDWROCREAT,0600);if(fd0){printf(creatfilesuccess);}}ssizetwrite(intfd,constvoidbuf,sizetcount);将缓冲区buf指针指向内存数据,写conut大小到fd。write(fd,buf,strlen(buf));printf(fdd,fd);close(fd);关闭fd文件return0;} strlen();函数是用来真正计算有效字符长度用strlen头文件是:string。h五、文件读取操作 读取文件:read头文件:includeunistd。h ssizetread(intfd,constvoidbuf,sizetcount);从fd个文件读取count个字节数据放到buf里 返回值:若读取成功,读多少个字节返回多少个字节,若读到尾什么都没读到返回0,读取失败返回1文件读取示例代码:includesystypes。hincludesysstat。hincludefcntl。hincludestdio。hincludestring。hincludestdlib。hintmain(){charbufRefuel。CONG;fdopen(。file,ORDWR);printf(fdd,fd);if(fd1){printf(openfilefailed);fdopen(。file,ORDWROCREAT,0600);if(fd0){printf(createfilesuccess);}}intnwritewrite(fd,buf,strlen(buf));if(nwrite!1){printf(writedbytetofile,nwrite);若读取成功,返回读到的ite}charreadBreadBuf(char)malloc(sizeof(char)nwrite1);intnreadread(fd,reafBuf,nwrite);printf(readd,context:s,nread,readBuf);close(fd);return0;} 结果什么都没读取到:是因为光标的原因,光标在写入数据后,光标停在写完的位置,读取的时候光会变后面,什么也没有所以读取不到。 解决办法:把光标移动到头。重新打开文件。 重新打开文件的方式解决光标的问题:includesystypes。hincludesysstat。hincludefcntl。hincludestdio。hincludestring。hincludestdlib。hintmain(){charbufRefuel。CONG;fdopen(。file,ORDWR);printf(fdd,fd);if(fd1){printf(openfilefailed);fdopen(。file,ORDWROCREAT,0600);if(fd0){printf(createfilesuccess);}}intnwritewrite(fd,buf,strlen(buf));if(nwrite!1){printf(writedbytetofile,nwrite);若读取成功,返回读到的ite}close(fd);写完数据后关闭文件fdopen(。file,ORDWR);重新打开charreadBreadBuf(char)malloc(sizeof(char)nwrite1);intnreadread(fd,reafBuf,nwrite);printf(readd,context:s,nread,readBuf);close(fd);return0;} 结果为: 六、文件光标移动操作 光标移动:lseek头文件:includeincludeunistd。h 宏:SEEKSET指向文件的头SEEKCUR指向当前光标位置SEEKEND指向文件的尾 offtlseek(intfd,offtoffset,intwhence);作用:将文件读写指针相对whence移动offset个字节参数说明:(文件描述符,偏移值,固定的位置)includesystypes。hincludesysstat。hincludefcntl。hincludestdio。hincludestring。hincludestdlib。hintmain(){charbufRefuel。CONG;fdopen(。file,ORDWR);printf(fdd,fd);if(fd1){printf(openfilefailed);fdopen(。file,ORDWROCREAT,0600);if(fd0){printf(createfilesuccess);}}intnwritewrite(fd,buf,strlen(buf));if(nwrite!1){printf(writedbytetofilenwrite);}charreadBreadBuf(char)malloc(sizeof(char)nwrite1);lseek(fd,0,SEEKSET);参数:(文件描述,偏移值,固定的位置)lseek(fd,11,SEEKCUR);所在光标位置往前偏移11个intnreadread(fd,readBuf,nwrite);printf(readd,context:s,nwrite,readBuf);close(fd);return0; lseek(fd,0,SEEKSET);参数:(文件描述,偏移值,固定的位置)lseek(fd,11,SEEKCUR);所在光标位置往前偏移11个七、文件操作原理简述1。文件描述符: 对于内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或者创建一个新文件时,内核向进程返回一个文件描述符。当读写一个文件时,用open和creat返回的文件描述符标识该文件,将其作为参数传递给read和write。按照惯例,UNIXshell使用文件描述符0与进程的标准输入相结合,文件描述符1与标准输出相结合,文件描述符2与标准错误输出相结合。STDINFILENO、STDOUTFILENO、STDERRFILENO这几个宏代替了0,1,2这几个数。 文件描述符,这个数字在一个进程中表示一个特定含义,当我们open一个文件时,操作系统在内存中构建了一些数据结构来表示这个动态文件,然后返回给应用程序一个数字作为文件描述符,这个数字就和我们内存中维护的这个动态文件的这些数据结构绑定上了,以后我们应用程序如果要操作这个动态文件,只需要用这个文件描述符区分。 文件描述符的作用域就是当前进程,除了这个进程文件描述符就没有意义了,open函数打开文件,打开成功返回一个文件描述符,打开失败,返回1。2。linux系统默认: 标准描述符:标准输入(0)标准输出(1)标准错误(3)includesystypes。hincludesysstat。hincludefcntl。hincludestdio。hincludestring。hincludestdlib。hintmain(){charreadBuf〔128〕;intnreadread(0,readBuf,5);0标准输入intnwritewrite(1,readBuf,strlen(readBuf));1标准输出printf(end!);return0;} 在这里插入图片描述3。操作文件时候: 打开创建文件读取文件写入文件关闭文件 1、在Linux中要操作一个文件,一般是先open打开一个文件,得到文件描述符,然后对文件进行读写操作(或其他操作),最后是close关闭文件即可。 2、强调一点:我们对文件进行操作时,一定要先打开文件,打开成功之后才能操作,如果打开失败,就不用进行后边的操作了,最后读写完成后,一定要关闭文件,否则会造成文件损坏。 3、文件平时是存放在块设备中的文件系统文件中的,我们把这种文件叫静态文件,当我们去open打开一个文件时,linux内核做的操作包括:内核在进程中建立一个打开文件的数据结构,记录下我们打开的这个文件;内核在内存中申请一段内存,并且将静态文件的内容从块设备中读取到内核中特定地址管理存放(叫动态文件)。readwrite都是对动态文件进行操作 4、打开文件以后,以后对这个文件的读写操作,都是针对内存中的这一份动态文件的,而并不是针对静态文件的。当然我们对动态文件进行读写以后,此时内存中动态文件和快设备文件中的静态文件就不同步了,当我们close关闭动态文件名,close内部内核将内存中的动态文件的内容去更新(同步)块设备中的静态文件。 5、为什么这么设计,不直接对块设备直接操作。块(假设有100字节)设备本身读写非常不灵活,是按块读写的,最小只读100字节,而内存是挤字节单位操作的可而具可以随机操作,很灵活。 静态文件放入磁盘中的文件是静态文件,如:桌面上的文件。jpg 动态文件open静态文件后,会在linux内核产生结构体记录文件如:fd信息节点,buf(内容,内存) 调用close时候,会把所有信息缓存到磁盘中八、文件操作编程小练习实现cp指令代码: cp(src。c源文件,dest。c复制到的目标文件) 实现cp操作: 1。c语言参数:。a。out2。实现思路:打开src。c读src到buf创建打开dest。c将buf写入dest。cclose两个文件 c语言参数:参数(argc是数组)(argv是二级指针是包含数组的数组:是argv里的每一项都是一个数组)includestdio。hintmain(intargc,charagrv)参数(argc是数组)(argv是包含数组的数组:是argv里的每一项都是一个数组){printf(totolparams:d,argc);printf(No。1params:d,argv〔0〕);printf(No。2params:d,argv〔1〕);printf(No。3params:d,argv〔2〕);return0;} 输入:。a。outdessrc结果:totolparams:3No。1params:。a。outNo。2params:desNo。3params:src 实现mycp操作代码:includesystypes。hincludesysstat。hincludestdio。hincludestring。hincludestdlib。hincludeunistd。hintmain(intargc,charargv){intfdSintfdDcharreadBufNULL;if(argc!3){printf(paramerror);exit(1);}fdSrcopen(argv〔1〕,ORDWR);1。打开fdSrc。c(源文件)intsizelseek(fdSrc,0,SEEKEND);光标记录文件大小lseek(fdSrc,0,SEEKSET);!!千万记得把光标回到头readBuf(char)malloc(sizeof(char)size8);开辟readBuf空间大小intnreadread(fdSrc,readBuf,size);2。读取fdSrc(源文件)到readBuf缓冲区fdDestopen(argv〔2〕,ORDWROCREATOTRUNC,0600);3。打开创建fdDest。cintnwritewrite(fdDest,readBuf,strlen(readBuf));4。将readbuf写入到fdDest。cclose(fdSrc);5。关闭两个文件close(fdDest);return0;}mycp容易出现的小问题实现优化:用lseek来光标计算size数组charreadBufNULL;intsizelseek(fdSrc,0,SEEKEND);lseek(fdSrc,0,SEEKSET);readBuf(char)malloc(sizeof(char)size8);若要是拷贝大于1024的文件就不行intnreadread(fdSrc,readBuf,size);读取大小用lseek查出的大小。 3。目标文件存在并且存在一些数据,拷贝就会覆盖了原来数据的一部分 解决方法:OTRUNC属性去打开文件时,如果这个文件中本来是有内容的,而且为只读或者只写成功打开,则将其长度截断为0fdDesopen(argv〔2〕,ORDWROCREATOTRUNC,0600);九、文件编程小应用之修改程序的配置文件 (工作中常用)配置文件的修改 例如:SPEED5LENG100SCORE90LEVEL95 修改指定内容的思路:找到(要修改的)位置(修改的位置)往后移动到(要改的值) 3。修改要改的值 找寻修改位置时候,用到strstr()函数;功能:用来检索子串在字符串中首次出现的位置 修改LENG的值示例代码:includesystypes。hincludesysstat。hincludefcntl。hincludeunistd。hincludestdio。hincludestring。hincludestdlib。hintmain(intargc,charargv){intfdScharreadBufNULL;if(argc!2){printf(pararmerror);exit(1);}fdSrcopen(argv〔1〕,ORDWR);intsizelseek(fdSrc,0,SEEKEND);lseek(fdSrc,0,SEEKSET);readBuf(char)malloc(sizeof(char)size8);intnreadread(fdSrc,readBuf,size);charpstrstr(readBuf,LENG);找到(要修改的)位置参数1要找的源文件2。要找的字符串if(pNULL){printf(notfound);exit(1);}ppstrlen(LENG);移动字符串个字节p0;p取内容lseek(fdSrc,0,SEEKSET);intnwritewrite(fdSrc,readBuf,strlen(readBuf));close(fdSrc);return0;} 可以把封装成一个函数:voidchangefile(intfd,charreadbuf,charf,chart){charpstrstr(readbuf,f);if(pNULL){printf(nofound);exit(1);}ppstrlen(f);}intmain(intargc,charargv){changefile(fdSrc,readBuf,LENG,6);}十、写一个整数到文件 ssizetwrite(intfd,constvoidbuf,sizetcount); 将缓冲区buf指针指向内存数据,写conut大小到fd。 ssizetread(intfd,constvoidbuf,sizetcount); 从fd个文件读取count个字节数据放到buf里1。写入整型数代码:includesystypes。hincludesysstat。hincludefcntl。hincludeunistd。hincludestdio。hincludestring。hincludestdlib。hintmain(){intdata100;intdata20;fdopen(。flie1,ORDWR);intnwritewrite(fd,data,sizeof(int));lseek(fd,0,SEEKSET);intnreadread(fd,data2,sizeof(int));printf(readd,data2);close(fd);return0;} 在这里插入图片描述2。写入结构体代码(1):structTest{};intmain(){intTestdata{100,a};intTestdata2;fdopen(。flie1,ORDWR);intnwritewrite(fd,data,sizeof(structTest));lseek(fd,0,SEEKSET);intnreadread(fd,data2,sizeof(structTest));printf(readds,data2。a,data2。c);close(fd);return0;}3。写入结构体代码(2):structTest{};intmain(){intTestdata〔2〕{{100,a},{101,b}};intTestdata2〔2〕;fdopen(。flie1,ORDWR);intnwritewrite(fd,data,sizeof(structTest)2);lseek(fd,0,SEEKSET);intnreadread(fd,data2,sizeof(structTest)2);printf(readds,data2〔0〕。a,data2〔0〕。c);printf(readds,data2〔1〕。a,data2〔1〕。c);close(fd);return0;} 注意写入读入的大小:sizeof(structTest)乘2 缓冲区可以写入:整数,字符,结构体等十一、标准C库对文件操作引入1。open和fopen的区别 1。来源open是UNIX系统调用函数,返回的是文件描述符,它是文件在文件描述符表里的索引。fopen是ANSIC标准中的c语言库函数,在不同的系统中应该调用不同的内核API,返回值是一个指向文件结构的指针。 2。移植性 这一点从上面的来源就可以推断出来,fopen是C标准函数,因此用有良好的移植性;而oprn是UNIX系统调用,移植性有限。如windows下相似的功能使用API函数。 3。适用范围 open返回文件描述符,而文件描述符是UNIX系统下的一个重要概念,UNIX下的一切设备都是文件的形式操作,如网络套件字,硬件设备(驱动)等。当然包括操作普通正规文件。fopen是用来操纵普通正规文件的。 4。缓冲缓冲文件系统缓冲文件系统的特点是:在内存开辟一个缓冲区,为程序中的每一个文件使用;当执行读文件的操作时,从磁盘文件将数据先读入内存缓冲区,装满后再从内存缓冲区依此读出需要的数据。执行写文件的操作时,先将数据写入内存缓冲区,待内存缓冲区装满后再写入文件。由此可以看出,内存缓冲区的大小,影响着实际操作外存的次数,内存缓冲区越大,则操作外存的次数就少,执行速度就快、效率高。一般来说,文件缓冲区的大小随机器而定。fopen,fclose,fread,fwrite,fgetc,fgets。fputc,fputs,freopen,fseek。ftell,rewind等。非缓冲文件系统缓冲文件系统是借助文件结构体指针来对文件进行管理,通过文件指针来对文件进行访问,既可以读写字符、字符电、格式化数据,也可以读写二进制数据。非缓冲文件系统依赖于操作系统,通过操作系统的功能对文件进行读写,是系统级的输入输出,它不设文件结构体指针,只能读写二进制文件,但效率高、速度快,由于ANSI标准不再包括非缓冲文件系统,因此建议大家最好不要选择它。open,close,read,write,getc,getchar,putc,putchar等。一句话总结一下,就是open无缓冲,fopen有缓冲。前者与read,write等配合使用,后者与freadfwrite等配合使用使用fopen函数,由于在用户态下就有了缓冲,因此进行文件读写操作的时候就减少了用户态和内核态的切换(切换到内核态调用还是需要调用系统调用API:read,write);而使用open函数,在文件读写时则每次都需要进行内核态和用户态的切换;表现为,如果顺序访问文件,fopen系列的函数要比直接调用open系列的函数快;如果随机访问文件则相反。这样一总结梳理,相信大家对于两个函数及系列函数有了一个更全面清晰的认识,也应该知道在什么场合下使用什么样的函数更合适效率更高。2。fopen();fwrite();fread();方式写入数据 FILEfopen(constcharpath,constcharmode); 参数说明:path:路径mode:用什么方式打开返回值:FILE类型 mode打开模式: 模式指令功能说明r只读方式打开一个文本文件rb只读方式打开一个二进制文件w只写方式打开一个文本文件wb只写方式打开一个二进制文件a追加方式打开一个文本文件ab追加方式打开一个二进制文件r可读可写方式打开一个文本文件rb可读可写方式打开一个二进制文件w可读可写方式创建一个文本文件wb读可写方式生成一个二进制文件a可读可写追加方式打开一个文本文件ab可读可写方式追加一个二进制文件 写入:sizetfwrite(constvoidptr,sizetsize,sizetnmemb,FILEstream);ptr缓冲区等同于(buf)size一个字符大小(sizeofchar)3。nmemb个数 4。stream(哪个文件)whichfile 读取:sizetfread(constvoidptr,sizetsize,sizetnmemb,FILEstream); 光标问题:intfseek(FILEstream,longoffset,intwhence); 示例代码:includestdio。hincludestring。hintmain(){FILEcharstrRefuel。CONG;charreadBuf〔128〕{0};fpfopen(。CONG。txt,w);可读可写方式创建一个文本文件fwrite(strsizeof(char),strlen(str),fp);一次性写一个char写str个字节,到fp里fseek(fp,0,SEEKSET);fread(readBuf,sizeof(char),strlen(str),fp);从fp里一次读一个char读str个读到readBuf里去printf(readdata:s,readBuf);fclose(fp);return0;} 也可改写:fread(readBuf,sizeof(char),strlen(str),1,fp); 读strlen(str)个读1次3。nread和nwrite的返回值 nread和nwrite的返回值取决于第三个参数intnfwritefwrite(strsizeof(char)strlen(str),1,fp);intnfreadfread(strsizeof(char)strlen(str),1,fp);printf(readd,writed,nread,nwrite); 结果:nread1nwrite14。nfread和nfwrite返回值区别intnfreadfread(strsizeof(char)strlen(str),100,fp);printf(nreadd,nread); 结果为:nread1 但是如果写100结果就会不同intnfwritefwrite(strsizeof(char)strlen(str),100,fp);printf(nwrited,nwrite); 结果为:nwrite1005。标准c库写入结构体到文件 fwrite()写入结构体代码:includestdio。hincludestring。hstructTest{};intmain(){FILEstructTestdata1{1100,a};structTestdata2;fpfopen(。CONG。txt,w);可读可写方式创建一个文本文件intnfwritefwrite(data,sizeof(stsuctTest),1,fp);fseek(fp,0,SEEKSET);intnfreadfread(data,sizeof(stsuctTest),1,fp);printf(readd,s,data2。a,data2。c);fclose(fp);return0;} 结果:read1100,a6。fgetc();fputc();feof()的使用方法; 读字符函数:fgetc()函数的用法作用:从指定的文件中读一个字符,函数调用的形式为:charch字符变量fgetc(fp文件指针);我们可以将读取到的数据给到一个字符变量存储。面对要读取的数据繁多的情况,为了减少程序运行的时间复杂度,要用到fgets();fgets();功能是从指定的文件读取一个字符串到字符数组中。函数的调用形式为:fgets(字符数组名,n,文件指针);参数:n为一个正整数。表示从文件中读出的字符串不超过n1个字符,在读入的最后一个字符后加上字符串结束标志0,说通俗易懂点就是读多少?printf(s,str);循环读取所有数据while(fgets(str,100,fp)){ 循环读取所有数据,直到fets读到’‘ printf(s,str);}fclose(fp);return0; fputc()函数的用法 intfputc(intc,FILEstream);功能:把c写入文件stream里 feof()函数的用法 intfeof(FILEstream);作用:(是否到尾巴的位置):测试在没到达文件尾巴返回0,到达尾巴返回非0 feof()函数使用代码:includestdio。hincludestring。hintmain(){FILEfpfopen(。test。txt,r);没到达文件尾巴返回0,到达尾巴返回非0while(!feof(fp)){没到尾巴时返回0,取反进入while当到达尾巴返回非0取反0退出whilecfgetc(fp);printf(c,c);}fclose(fp);return0;} fputc写入文件代码:includestdio。hincludestring。hintmain(){FILEcharstrRefuel。CONG;intlensizeof(str);fpfopen(。test。txt,w);if(fpNULL){printf(打开文件出现错误);exit(1);}for(i0;i){fputc(str,fp);}fclose(fp);return0;} 文件编程就到这里结束了!!!