在Linux的世界里,一切设备皆文件。我们可以系统调用中I/O的函数(I:input,输入;O:output,输出),对文件进行相应的操作(open()、close()、write()、read()等)。
首先使用Linux的文件API,经常看见一个东西,叫做文件描述符。打开现存文件或新建文件时,系统(内核)会返回一个文件描述符,文件描述符用来指定已打开的文件。这个文件描述符相当于这个已打开文件的标号,文件描述符是非负整数,是文件的标识,操作这个文件描述符相当于操作这个描述符所指定的文件。
什么是文件描述符?
(1)文件描述符其实实质是一个数字,这个数字在一个进程中表示一个特定的含义,当我们open打开一个文件时,操作系统在内存中构建了一些数据结构来表示这个动态文件,然后返回给应用程序一个数字作为文件描述符,这个数字就和我们内存中维护这个动态文件的这些数据结构挂钩绑定上了,以后我们应用程序如果要操作这一个动态文件,只需要用这个文件描述符进行区分。
(2)文件描述符就是用来区分一个程序打开的多个文件的。
(3)文件描述符的作用域就是当前进程,出了当前进程这个文件描述符就没有意义了
(4)文件描述符fd的合法范围是0或者一个正数,不可能是一个负数
(5)open返回的fd必须记录好,以后向这个文件的所有操作都要靠这个fd去对应这个文件,最后关闭文件时也需要fd去指定关闭这个文件。如果在我们关闭文件前fd丢了,那么这个文件就没法关闭了也没法读写了
1)打开与读取文件
#include
#include
#include
#include
#include
intmain(intargc,charconst*argv[]){
intfd=-1;//文件描述符
//打开文件
fd=open("ghostwu.txt",O_RDWR);
if(-1==fd){
printf("文件打开失败\n");
}else{
printf("文件打开成功,fd=%d\n",fd);
}
//读取文件
intcount=0;
charbuf[20];
count=read(fd,buf,50);
if(-1==count){
printf("文件读取失败\n");
}else{
printf("文件读取成功,实际读取的字节数目为:%d\n内容为%s\n",count,buf);
}
//关闭文件
close(fd);
return0;
}
需要在当前目录下存在ghostwu.txt这个文件,否则打开的时候失败,这里涉及2个api
intopen(constchar*pathname,intflags);
open非常简单,第一个参数就是文件路径,第二个是文件模式,在man手册中还提供了其他几种方式。
ssize_tread(intfd,void*buf,size_tcount);
第一个参数为文件描述符,就是open返回的那个值
第二个参数buf用来存储从文件中读取的内容
第三个参数,表示希望从文件中读取的内容(注:这个count数字可以随便给,最终以返回的实际数目(read的返回值)为准
2)打开与写入文件
#include
#include
#include
#include
#include
#include
intmain(intargc,charconst*argv[]){
intfd=-1;//文件描述符
//打开文件
fd=open("ghostwu.txt",O_RDWR);
if(-1==fd){
printf("文件打开失败\n");
}else{
printf("文件打开成功,fd=%d\n",fd);
}
//写文件
charbuf[]="IloveLinux,Linuxisveryverygood!!!";
intcount=0;
count=write(fd,buf,strlen(buf));
if(-1==count){
printf("文件写入失败\n");
}else{
printf("文件写入成功,实际写入的字节数目为:%d\n",count);
}
//关闭文件
close(fd);
return0;
}
ssize_twrite(intfd,constvoid*buf,size_tcount);
第一个参数为文件描述符,就是open返回的那个值
第二个参数buf用来存储写入的内容
第三个参数,表示希望写入的文件大小
3)open的一些flag参数
1,只读与只写权限
#include
#include
#include
#include
#include
intmain(intargc,charconst*argv[]){
intfd=-1;//文件描述符
//打开文件,O_RDONLY:只读权限,打开之后的文件只能读取,不能写入
//打开文件,O_WRONLY:只写权限,打开之后的文件只能写入,不能读取
//fd=open("ghostwu.txt",O_RDONLY);
fd=open("ghostwu.txt",O_WRONLY);
if(-1==fd){
printf("文件打开失败\n");
}else{
printf("文件打开成功,fd=%d\n",fd);
}
//读取文件
intcount=0;
charbuf[41];
count=read(fd,buf,38);
if(-1==count){
printf("文件读取失败\n");
}else{
printf("文件读取成功,实际读取的字节数目为:%d\n内容为%s\n",count,buf);
}
//关闭文件
close(fd);
return0;
}
2,清空与追加
#include
#include
#include
#include
#include
#include
intmain(intargc,charconst*argv[]){
intfd=-1;//文件描述符
//打开文件
//在O_RDWR模式下,对于一个已经存在的文件,且有内容,那么写入文件会覆盖对应大小的源文件内容【不是完全覆盖】
//fd=open("ghostwu.txt",O_RDWR);
//在具有写入权限的文件中,使用O_TRUNC会先把原来的内容清除,再写入新的内容
//fd=open("ghostwu.txt",O_RDWR|O_TRUNC);
//在具有写入权限的文件中,使用O_APPEND会把新内容追加到原来内容的后面
//fd=open("ghostwu.txt",O_RDWR|O_APPEND);
//在具有写入权限的文件中,使用O_APPEND和O_TRUNCO_TRUNC起作用,会把原来的内容清除,再写入新的内容
fd=open("ghostwu.txt",O_RDWR|O_APPEND|O_TRUNC);
if(-1==fd){
printf("文件打开失败\n");
return-1;
}else{
printf("文件打开成功,fd=%d\n",fd);
}
//写文件
charbuf[]="newcontent";
intcount=0;
count=write(fd,buf,strlen(buf));
if(-1==count){
printf("文件写入失败\n");
return-1;
}else{
printf("文件写入成功,实际写入的字节数目为:%d\n",count);
}
//关闭文件
close(fd);
return0;
}
3,文件存在已否,创建文件与设置权限
#include
#include
#include
#include
#include
#include
intmain(intargc,charconst*argv[]){
intfd=-1;
//fd=open("ghostwu.txt",O_RDWR|O_CREAT|O_EXCL);
/*
文件不存在:
创建这个文件并打开成功
文件存在:
再次运行时(文件已经创建成功,存在了),这时打开失败
*/
//fd=open("ghostwu.txt",O_RDWR|O_CREAT);
fd=open("ghostwu.txt",O_RDWR|O_CREAT|O_EXCL,666);
if(-1==fd){
printf("文件打开失败,错误号:%d\n",errno);
perror("open");
return-1;
}else{
printf("文件打开成功\n");
}
close(fd);
return0;
}
上面用到了一个函数perror,errno和perror:
1)errno就是errornumber,意思就是错误号码。linux系统中对各种常见错误做了个编号,当函数执行错误时,函数会返回一个特定的errno编号来告诉我们这个函数到底哪里错了
2)errno是由操作系统来维护的一个全局变量,操作系统内部函数都可以通过设置errno来告诉上层调用者究竟刚才发生了一个什么错误
3)errno本身实质是一个int类型的数字,每个数字编号对应一种错误。当我们只看errno时只能得到一个错误编号数字,并不知道具体错在哪里,所以:linux系统提供了一个函数perror(意思printerror),perror函数内部会读取errno并且将这个不好认的数字直接给转成对应的错误信息字符串,然后打印出来
4,lseek用来移动文件内部指针
简单应用:统计文件大小
#include
#include
#include
#include
#include
#include
intmain(intargc,charconst*argv[]){
if(argc!=2){
printf("usage:%s%s\n",argv[0],"filename");
return-1;
}
intfd=-1;
fd=open(argv[1],O_RDWR);
if(-1==fd){
printf("文件打开失败,错误号:%d\n",errno);
perror("open");
return-1;
}else{
printf("文件打开成功\n");
}
//把指针移动到文件末尾,就是文件的大小
intcount=lseek(fd,0,SEEK_END);
printf("文件大小为%d\n",count);
close(fd);
return0;
}
------------------------------------------分割线------------------------------------------
一、同一个进程,多次打开同一个文件,然后读出内容的结果是:分别读【我们使用open两次打开同一个文件时,fd1和fd2所对应的文件指针是不同的2个独立的指针】
#include
#include
#include
#include
#include
#include
intmain(intargc,charconst*argv[]){
intfd1=-1;
intfd2=-1;
charbuf1[20]={0};
charbuf2[20]={0};
intcount1=0;
intcount2=0;
fd1=open("ghostwu.txt",O_RDWR);
if(-1==fd1){
printf("文件打开失败\n");
perror("open");
return-1;
}else{
printf("文件打开成功,fd1=%d\n",fd1);
}
count1=read(fd1,buf1,5);
if(-1==count1){
printf("文件读取失败\n");
perror("read");
}else{
printf("文件读取成功,读取的内容是%s\n",buf1);
}
fd2=open("ghostwu.txt",O_RDWR);
if(-1==fd1){
printf("文件打开失败\n");
perror("open");
return-1;
}else{
printf("文件打开成功,fd2=%d\n",fd1);
}
count2=read(fd2,buf2,10);
if(-1==count2){
printf("文件读取失败\n");
perror("read");
}else{
printf("文件读取成功,读取的内容是%s\n",buf2);
}
close(fd1);
close(fd2);
return0;
}
二、同一个进程,多次打开同一个文件,然后写入内容的结果是:分别写,当使用O_APPEND,就是接着写
#include
#include
#include
#include
#include
#include
#include
intmain(intargc,charconst*argv[]){
intfd1=-1;
intfd2=-1;
charbuf1[]="ghost";
charbuf2[]="wu";
intcount1=0;
intcount2=0;
fd1=open("ghostwu.txt",O_RDWR);
if(-1==fd1){
printf("文件打开失败\n");
perror("open");
return-1;
}else{
printf("文件打开成功,fd1=%d\n",fd1);
}
count1=write(fd1,buf1,strlen(buf1));
if(-1==count1){
printf("文件写入失败\n");
perror("write");
}else{
printf("文件写入成功,写入的内容是%s\n",buf1);
}
fd2=open("ghostwu.txt",O_RDWR);
if(-1==fd1){
printf("文件打开失败\n");
perror("open");
return-1;
}else{
printf("文件打开成功,fd2=%d\n",fd1);
}
count2=write(fd2,buf2,strlen(buf2));
if(-1==count2){
printf("文件写入失败\n");
perror("write");
}else{
printf("文件写入成功,写入的内容是%s\n",buf2);
}
close(fd1);
close(fd2);
return0;
}
上面代码保持不变,再写入的时候加入flag标志:
fd1=open("ghostwu.txt",O_RDWR|O_APPEND);
fd2=open("ghostwu.txt",O_RDWR|O_APPEND);
三、dup后的fd和原来打开文件的fd指向的是同一个文件,同时对这个文件写入时,是接着写
#include
#include
#include
#include
#include
#include
intmain(intargc,charconst*argv[]){
intfd1=-1;
intfd2=-1;
fd1=open("ghostwu.txt",O_RDWR);
if(-1==fd1){
perror("open");
return-1;
}else{
printf("文件打开成功:fd=%d\n",fd1);
}
//dup后的文件,同时write是接着写入
fd2=dup(fd1);
printf("文件dup成功:fd=%d\n",fd2);
//分别向fd1和fd2指向的文件写入
charbuf1[]="ghost";
charbuf2[]="wu";
intcount1=-1,count2=-1;
while(1){
count1=write(fd1,buf1,strlen(buf1));
if(-1==count1){
perror("buf1->write");
return-1;
}else{
printf("buf1->文件写入成功\n");
}
sleep(1);
count2=write(fd2,buf2,strlen(buf2));
if(-1==count2){
perror("buf2->write");
return-1;
}else{
printf("buf2->文件写入成功\n");
}
}
close(fd1);
close(fd2);
return0;
}
在linux系统中,内核占用了0、1、2这三个fd,当我们运行一个程序得到一个进程时,内部就默认已经打开了3个文件,
对应的fd就是0、1、2。分别叫stdin、stdout、stderr。也就是标准输入、标准输出、标准错误。接下来,我们把标准输出关闭,printf就不会输出,如果用dup复制原来的fd,那么新dup出来的fd就是1(对应标准输出)
之后标准输出的内容都会被写入到原来fd对应的那个文件
#include
#include
#include
#include
#include
#include
intmain(intargc,charconst*argv[]){
intfd=-1;
fd=open("ghostwu2.txt",O_RDWR);
if(-1==fd){
perror("open");
return-1;
}else{
printf("文件打开成功fd=%d\n",fd);
}
//fd=0对应stdinfd=1对应stdoutfd=2对应stderror
close(1);//关闭fd=1的标准输出之后,printf输出看不见
intnewFd=-1;
newFd=dup(fd);//newFd一定是1,因为分配后的fd从最小的没被占用的开始
charbuf[3];
sprintf(buf,"%d",newFd);//newFd转字符串型
printf("这是一段输出,由于newFd和fd关联到标准输出(newFd=1),会被写入到文件\n");
write(fd,buf,1);
return0;
}
以上就是关于Linux系统编程之简单文件IO操作的详细介绍,最后想要了解更多关于Linux发展前景趋势,请关注扣丁学堂Linux培训官网、微信等平台,扣丁学堂IT职业在线学习教育平台为您提供权威的Linux视频教程系统,通过千锋扣丁学堂金牌讲师在线录制的第一套自适应Linux在线视频课程系统,让你快速掌握Linux从入门到精通开发实战技能。扣丁学堂Linux技术交流群:659974587。