使用Linux的文件API,经常看见一个东西,叫做文件描述符.

什么是文件描述符?

(1)文件描述符其实实质是一个数字,这个数字在一个进程中表示一个特定的含义,当我们open打开一个文件时,操作系统在内存中构建了一些数据结构来表示这个动态文件,然后返回给应用程序一个数字作为文件描述符,这个数字就和我们内存中维护这个动态文件的这些数据结构挂钩绑定上了,以后我们应用程序如果要操作这一个动态文件,只需要用这个文件描述符进行区分。

(2)文件描述符就是用来区分一个程序打开的多个文件的。

(3)文件描述符的作用域就是当前进程,出了当前进程这个文件描述符就没有意义了

(4)文件描述符fd的合法范围是0或者一个正数,不可能是一个负数

(5)open返回的fd必须记录好,以后向这个文件的所有操作都要靠这个fd去对应这个文件,最后关闭文件时也需要fd去指定关闭这个文件。如果在我们关闭文件前fd丢了,那么这个文件就没法关闭了也没法读写了

1)打开与读取文件

 #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h> int main(int argc, char const *argv[]) { int fd = -; //文件描述符 //打开文件
fd = open( "ghostwu.txt", O_RDWR ); if ( - == fd ) {
printf("文件打开失败\n");
}else {
printf("文件打开成功,fd=%d\n", fd );
} //读取文件
int count = ;
char buf[];
count = read( fd, buf, );
if ( - == count ) {
printf("文件读取失败\n");
}else {
printf("文件读取成功,实际读取的字节数目为:%d\n内容为%s\n", count, buf );
} //关闭文件
close( fd ); return ;
}

需要在当前目录下存在ghostwu.txt这个文件,否则打开的时候失败,这里涉及2个api

int open(const char *pathname, int flags);

open非常简单,第一个参数就是文件路径, 第二个是文件模式,在man手册中还提供了其他几种方式。

ssize_t read(int fd, void *buf, size_t count);

第一个参数为文件描述符,就是open返回的那个值

第二个参数buf用来存储从文件中读取的内容

第三个参数,表示希望从文件中读取的内容( 注:这个count数字可以随便给,最终以返回的实际数目(read的返回值)为准

2)打开与写入文件

 #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h> int main(int argc, char const *argv[]) { int fd = -; //文件描述符 //打开文件
fd = open( "ghostwu.txt", O_RDWR ); if ( - == fd ) {
printf("文件打开失败\n");
}else {
printf("文件打开成功,fd=%d\n", fd );
} //写文件
char buf[] = "I love Linux, Linux is very very good!!!";
int count = ;
count = write( fd, buf, strlen( buf ) );
if ( - == count ) {
printf("文件写入失败\n");
}else {
printf("文件写入成功,实际写入的字节数目为:%d\n", count);
} //关闭文件
close( fd ); return ;
}

ssize_t write(int fd, const void *buf, size_t count);

第一个参数为文件描述符,就是open返回的那个值

第二个参数buf用来存储写入的内容

第三个参数,表示希望写入的文件大小

3)open的一些flag参数

1,只读与只写权限

 #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h> int main(int argc, char const *argv[]) { int fd = -; //文件描述符 //打开文件, O_RDONLY:只读权限,打开之后的文件只能读取,不能写入
//打开文件, O_WRONLY:只写权限,打开之后的文件只能写入,不能读取
// fd = open( "ghostwu.txt", O_RDONLY );
fd = open( "ghostwu.txt", O_WRONLY ); if ( - == fd ) {
printf("文件打开失败\n");
}else {
printf("文件打开成功,fd=%d\n", fd );
} //读取文件
int count = ;
char buf[];
count = read( fd, buf, );
if ( - == count ) {
printf("文件读取失败\n");
}else {
printf("文件读取成功,实际读取的字节数目为:%d\n内容为%s\n", count, buf );
} //关闭文件
close( fd ); return ;
}

2,清空与追加

 #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h> int main(int argc, char const *argv[]) { int fd = -; //文件描述符 //打开文件
//在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_TRUNC O_TRUNC起作用,会把原来的内容清除,再写入新的内容
fd = open( "ghostwu.txt", O_RDWR | O_APPEND | O_TRUNC ); if ( - == fd ) {
printf("文件打开失败\n");
return -;
}else {
printf("文件打开成功,fd=%d\n", fd );
} //写文件
char buf[] = "new content";
int count = ;
count = write( fd, buf, strlen( buf ) );
if ( - == count ) {
printf("文件写入失败\n");
return -;
}else {
printf("文件写入成功,实际写入的字节数目为:%d\n", count);
} //关闭文件
close( fd ); return ;
}

3,文件存在已否,创建文件与设置权限

 #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h> int main(int argc, char const *argv[]) { int fd = -; // 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, ); if( - == fd ) {
printf("文件打开失败,错误号:%d\n", errno );
perror( "open" );
return -;
}else {
printf("文件打开成功\n");
} close( fd ); return ;
}

上面用到了一个函数perror,errno和perror:

1)errno就是error number,意思就是错误号码。linux系统中对各种常见错误做了个编号,当函数执行错误时,函数会返回一个特定的errno编号来告诉我们这个函数到底哪里错了

2)errno是由操作系统来维护的一个全局变量,操作系统内部函数都可以通过设置errno来告诉上层调用者究竟刚才发生了一个什么错误

3)errno本身实质是一个int类型的数字,每个数字编号对应一种错误。当我们只看errno时只能得到一个错误编号数字,并不知道具体错在哪里,所以:linux系统提供了一个函数perror(意思print error),perror函数内部会读取errno并且将这个不好认的数字直接给转成对应的错误信息字符串,然后打印出来

4,lseek用来移动文件内部指针

简单应用:统计文件大小

 #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h> int main(int argc, char const *argv[]) { if ( argc != ) {
printf("usage:%s %s\n", argv[], "filename");
return -;
} int fd = -; fd = open( argv[], O_RDWR ); if( - == fd ) {
printf("文件打开失败,错误号:%d\n", errno );
perror( "open" );
return -;
}else {
printf("文件打开成功\n");
} //把指针移动到文件末尾,就是文件的大小
int count = lseek( fd, , SEEK_END ); printf("文件大小为%d\n", count); close( fd );
return ;
}

------------------------------------------分割线------------------------------------------

一、同一个进程,多次打开同一个文件,然后读出内容的结果是: 分别读【我们使用open两次打开同一个文件时,fd1和fd2所对应的文件指针是不同的2个独立的指针】

 #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h> int main(int argc, char const *argv[]) { int fd1 = -;
int fd2 = -;
char buf1[] = {};
char buf2[] = {};
int count1 = ;
int count2 = ; fd1 = open( "ghostwu.txt", O_RDWR ); if ( - == fd1 ) {
printf("文件打开失败\n");
perror( "open" );
return -;
}else {
printf("文件打开成功,fd1=%d\n", fd1);
} count1 = read( fd1, buf1, );
if ( - == count1 ) {
printf( "文件读取失败\n" );
perror( "read" );
}else {
printf( "文件读取成功,读取的内容是%s\n", buf1 );
} fd2 = open( "ghostwu.txt", O_RDWR ); if ( - == fd1 ) {
printf("文件打开失败\n");
perror( "open" );
return -;
}else {
printf("文件打开成功,fd2=%d\n", fd1);
} count2 = read( fd2, buf2, );
if ( - == count2 ) {
printf( "文件读取失败\n" );
perror( "read" );
}else {
printf( "文件读取成功,读取的内容是%s\n", buf2 );
} close( fd1 );
close( fd2 ); return ;
}

二、同一个进程,多次打开同一个文件,然后写入内容的结果是: 分别写,当使用O_APPEND,就是接着写

 #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <string.h> int main(int argc, char const *argv[]) { int fd1 = -;
int fd2 = -;
char buf1[] = "ghost";
char buf2[] = "wu";
int count1 = ;
int count2 = ; fd1 = open( "ghostwu.txt", O_RDWR ); if ( - == fd1 ) {
printf("文件打开失败\n");
perror( "open" );
return -;
}else {
printf("文件打开成功,fd1=%d\n", fd1);
} count1 = write( fd1, buf1, strlen( buf1 ) );
if ( - == count1 ) {
printf( "文件写入失败\n" );
perror( "write" );
}else {
printf( "文件写入成功,写入的内容是%s\n", buf1 );
} fd2 = open( "ghostwu.txt", O_RDWR ); if ( - == fd1 ) {
printf("文件打开失败\n");
perror( "open" );
return -;
}else {
printf("文件打开成功,fd2=%d\n", fd1);
} count2 = write( fd2, buf2, strlen( buf2 ) );
if ( - == count2 ) {
printf( "文件写入失败\n" );
perror( "write" );
}else {
printf( "文件写入成功,写入的内容是%s\n", buf2 );
} close( fd1 );
close( fd2 ); return ;
}

上面代码保持不变,再写入的时候加入flag标志:

fd1 = open( "ghostwu.txt", O_RDWR | O_APPEND );

fd2 = open( "ghostwu.txt", O_RDWR | O_APPEND );

三、 dup后的fd和原来打开文件的fd指向的是同一个文件,同时对这个文件写入时,是接着写

 #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h> int main(int argc, char const *argv[]) { int fd1 = -;
int fd2 = -; fd1 = open( "ghostwu.txt", O_RDWR ); if ( - == fd1 ) {
perror( "open" );
return -;
}else {
printf("文件打开成功:fd=%d\n", fd1);
} //dup后的文件,同时write 是接着写入
fd2 = dup( fd1 );
printf("文件dup成功:fd=%d\n", fd2); //分别向fd1和fd2指向的文件写入 char buf1[] = "ghost";
char buf2[] = "wu"; int count1 = -, count2 = -; while ( ) {
count1 = write( fd1, buf1, strlen( buf1 ) );
if ( - == count1 ) {
perror( "buf1->write" );
return -;
}else {
printf("buf1->文件写入成功\n");
} sleep( ); count2 = write( fd2, buf2, strlen( buf2 ) );
if ( - == count2 ) {
perror( "buf2->write" );
return -;
}else {
printf("buf2->文件写入成功\n");
}
} close( fd1 );
close( fd2 );
return ;
}

在linux系统中,内核占用了0、1、2这三个fd,当我们运行一个程序得到一个进程时,内部就默认已经打开了3个文件,

对应的fd就是0、1、2。分别叫stdin、stdout、stderr。也就是标准输入、标准输出、标准错误。接下来,我们把标准输出关闭,printf就不会输出,如果用dup复制原来的fd,那么新dup出来的fd就是1(对应标准输出)

之后标准输出的内容都会被写入到原来fd对应的那个文件

 #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h> int main(int argc, char const *argv[]) { int fd = -; fd = open( "ghostwu2.txt", O_RDWR );
if ( - == fd ) {
perror( "open" );
return -;
}else {
printf( "文件打开成功fd=%d\n", fd );
} //fd=0 对应stdin fd=1 对应 stdout fd=2 对应stderror
close( ); //关闭fd=1的标准输出之后,printf输出看不见 int newFd = -; newFd = dup( fd ); //newFd一定是1, 因为分配后的fd从最小的没被占用的开始
char buf[];
sprintf( buf, "%d", newFd ); //newFd转字符串型
printf( "这是一段输出,由于newFd和fd关联到标准输出(newFd=1),会被写入到文件\n" );
write( fd, buf, ); return ;
}

Linux系统编程:简单文件IO操作的更多相关文章

  1. 【Linux 应用编程】文件IO操作 - 常用函数

    Linux 系统中的各种输入输出,设计为"一切皆文件".各种各样的IO统一用文件形式访问. 文件类型及基本操作 Linux 系统的大部分系统资源都以文件形式提供给用户读写.这些文件 ...

  2. linux系统编程之文件IO

    1.打开文件的函数open,第一个参数表示文件路径名,第二个为打开标记,第三个为文件权限 代码: #include <sys/types.h> #include <sys/stat. ...

  3. Linux系统编程001--系统IO

    1. 文件系统:用来存储.组织.管理文件的一套方式.协议 2. 文件 文件的属性:i-node唯一表示一个文件的存在与否 文件的内容 3. Linux系统如何实现文件的操作? 点击查看代码 硬件层: ...

  4. linux系统编程之文件与io(五)

    上一节中已经学习了文件描述符的复制,复制方法有三种,其中最后一种fcntl还并未使用到,关于这个函数,不光只有复制文件描述符的功能,还有其它一些用法,本节就对其进行一一剖析: fcntl常用操作: 这 ...

  5. linux系统编程之文件与io(一)

    经过了漫长的学习,C语言相关的的基础知识算是告一段落了,这也是尝试用写博客的形式来学习c语言,回过头来看,虽说可能写的内容有些比较简单,但是个人感觉是有史起来学习最踏实的一次,因为里面的每个实验都是自 ...

  6. UNIX系统编程:文件IO(I)

    1.标准C库中访问文件用的是文件指针FILE *(stdin,stdout,stderr):对于linux系统编程而言,所有对设备或文件的操作都是通过文件描述符进行的 2.当打开或者创建一个文件的时候 ...

  7. linux系统编程之文件与IO(一):文件描述符、open,close

    什么是IO? 输入/输出是主存和外部设备之间拷贝数据的过程 设备->内存(输入操作) 内存->设备(输出操作) 高级I/O ANSI C提供的标准I/O库称为高级I/O,通常也称为带缓冲的 ...

  8. Linux系统编程:文件I/O编程

    文件I/O操作在Linux编程时是经常会使用到的一个内容,通常可以把比较大的数据写到一个文件中来进行存储或者与其他进程进行数据传输,这样比写到一个全局数组或指针参数来实现还要方便好多. 一.文件常用操 ...

  9. linux c编程:文件的操作

    在Linux系统中,系统是通过inode来获得这个文件的信息.在Linux系统中,inode的信息都是封装在stat这个结构体中.可以通过man 2 stat来查看stat的具体结构.从中可以看到包含 ...

随机推荐

  1. swiper3插件无缝滚动配置

    <html> <head> <link rel="stylesheet" href="https://cdn.bootcss.com/Swi ...

  2. html5的结构

    目录 一.新增的主体结构元素 1.1.article元素 1.2.section元素 1.3.nav元素 1.4.aside元素 1.5.time元素 1.6.pubdate元素 二.新增的非主体结构 ...

  3. System.ServiceModel.CommunicationException: 已超过传入消息(65536)的最大消息大小配额。若要增加配额,请使用相应绑定元素上的 MaxReceivedMessageSize 属性。

  4. nginx利用反向代理调试后台接口

    1.location 支持配置项目的绝对路径 2.假设我们的后台API地址是以API开头,location ^~ /api/ 代表nginx将会拦截请求地址中包含"/api/"字样 ...

  5. git实用攻略(二)

    最近团队的版本控制从svn切换到了git,虽说已经使用git有2年多了,也写了一个实用攻略,但是github上的项目使用经验和公司内部团队协作的使用经验还有很多不同.补充下新的使用体会. 首先还是看一 ...

  6. 《重构--改善既有代码的设计》总结or读后感:重构是程序员的本能

    此文写得有点晚,记得去年7月读完的这本书,只是那时没有写文章的意识,也无所谓总结了,现在稍微聊一下吧. 想起写这篇感想,还是前几天看了这么一篇文章 研究发现重构软件并不会改善代码质量 先从一个大家都有 ...

  7. 单源最短路径 dijkstra算法实现

    本文记录一下dijkstra算法的实现,图用邻接矩阵表示,假设图为无向图.而且连通,有向图,不连通图的做法相似. 算法简述: 首先确定"单源"的源.假设是第0个顶点. 维护三个数组 ...

  8. C++ 虚指针、成员变量与类对象的偏移地址

    先给出一段代码实现 #include <iostream> using namespace std; class animal { protected: int age; public: ...

  9. JSP具体篇——response对象

    response对象 response对象用于响应client请求,向客户输出信息. 他封装了JSP产生的响应,并发送到client以响应client请求. 1.重定向网页 使用response对象的 ...

  10. Bootstrap入门Demo——制作路径导航栏

    今天在在群里聊天的时候看到一仅仅程序猿发了一张用Bootstrap做的界面.感觉挺好看.然后去官网看了下组件.发现都挺美丽的,然后看到了路径导航栏,刚好要做这个东西,然后就下了Bootstrap的源代 ...