文件IO
在unix世界中视一切为文件,无论最基本的文本文件还是网络设备或是u盘,在内核看来它们的本质都是一样的。大多数文件IO操作只需要用到5个函数:open 、 read 、 write 、 lseek 以及 close. 这些函数并不是 ISO C 的组成部分,而是unix的系统调用函数。相比较ISO C中带缓冲的IO操作函数,它们是不带缓冲的IO函数。它们之间的区别看上去是这样的:

可以简单的这么来理解,write函数直接将内容写到文件中。而像printf这样的函数将内容先写到缓冲区,然后由缓冲区的方式(行缓冲或者全缓冲)来决定什么时候将缓冲区的内容写到具体的文件中。下面对文件IO操作中的概念还有函数一一介绍。
文件IO的基本概念
下面的这张图表示了打开文件的内核数据结构:

简单来说,首先是一个文件面向用户的文件描述符,然后文件指针指向一个文件表,用来保存文件相关的信息。接下来可以认为与文件物理存储相关的结构。其中涉及的几个文件IO基本概念解释如下:
文件描述符
当调用open函数打开一个文件时会返回一个非负整数来标志该文件。一方面方便内核的管理,另一方面方便代码基于此标志进行后续的文件操作。在UNIX系统中,stdin与文件描述0相互关联;stdout与文件描述符1相互关联;stderr与文件描述符2相互关联。并使用下面的宏定义来提高程序的可读性:STDIN_FILENO 、 STDOUT_FILENO 、 STDERR_FILENO.
文件描述符标志
这个概念容易混淆,文件描述符标志即close-on-exec标志位。如果文件描述符设置了该标志,表示在通过fork函数产生的子进程中调用exec族函数时,该文件描述符会被自动关闭。这样做可以用来保护文件描述符资源,防止泄露。
文件状态标志
文件状态标识用来说明该文件的基本属性。比如是否可读、是否可写、是否阻塞等等。下一小节中会详细的描述。
文件偏移量
当执行读或者写操作时,第一个面临的问题就是在什么位置执行这些操作。文件偏移量这个标志就指明了当前文件相对于起始位置的偏移量。
open函数
open函数的原型如下:
#include <fcntl.h> int open(const char *path, int oflag, .../* mode_t mode */); 返回值:若成功返回文件描述符,若出错返回-1
此函数用 path 参数指定文件的路径,用 oflag 参数指定文件状态标识,说明如下:
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR: 读写打开
以上三种属于互斥的关系,只能选其一。下面的这些标志位可以通过 '|' 操作来进行叠加。
O_APPEND: 每次写时都追加到文件的尾端。
O_CLOEXEC: 它的含义在上文 文件描述法标志位 中已经阐述清楚了。
O_CREAT: 若文件不存在则创建它。使用此选项时,open函数需同时说明第三个参数mode,来指定该文件的访问权限。如果文件已经存在并不会出错,而是重新创建新文件。
O_EXCL: 如果同时指定了OCREATE,且文件存在,则出错。这个标志可以用来测试文件是否存在,也可以保证对现有文件的保护。
O_NOCTTY: 如果path引用的是终端设备,则不将该设备分配作为此进程的控制终端
O_NOBLOCK: 如果path引用的是一个FITO、一个块特殊文件或一个字符特殊文件,则此选项为文件的本次打开操作和后续的IO操作设置非阻塞方式。
O_SYNC: 使每次write等待物理IO操作完成,包括由该write操作引起的文件属性更新所需的IO。
O_TRUNC: 如果文件存在,而且为只写或读-写成功打开,则将其长度截断为0。
常用的标志位为 O_APPEND, O_CLOEXEC, O_NOBLOCK, O_TRUNC。这些标志位可以通过使用 | 操作符来实现多个开启,如
open("./test.txt", O_RDWD | O_APPEND | O_NOBLOCK);
以读写、追加、非阻塞的方式打开当前目录下的test.txt文件。
此外,openat() 与 create() 函数也可以实现打开文件的功能。但一般使用 open 函数即可。
lseek函数
lseek用于设置当前文件偏移量,其原型如下:
#include <unistd.h> off_t lseek(int fd, off_t offset, int whence); 返回值:若成功返回文件的新的偏移量,若出错返回-1
这个函数将fd描述符的文件的偏移量设置到距离 whence 位置 offset 字节数的地方。whence 示意如下:
SEEK_SET: 文件开始处
SEEK_CUR: 文件当前位置
SEEK_END: 文件结尾
若 lseek 成功执行,返回新的文件偏移量。这是一个非常有用的函数。如果想知道文件当前的偏移量,可以使用下面的这个技巧。
off_t offset; offset = lseek(fd, , SEEK_CUR);
read write函数
读写函数放在一起比较,便于记忆。read 函数的原型如下:
#include <unistd.h> ssize_t read(int fd, void *buf, size_t nbytes); 返回值:若成功返回读到的字节数,若已到文件结尾返回0,若出错返回-1
fd为所读取文件的描述符,buf用于存放的缓冲区指针,nbytes为预计要读取的字节数。关于返回值:如果read执行成功,返回实际读到的数据(如已到文件尾端,返回0);如果read出错,返回-1。
有多种的情况下,read 函数多读取的字节数可能会小于所要求的字节数,在网络编程下尤其要注意这一点。
write的函数原型如下:
#include <unistd.h> ssize_t write(int fd, const void *buf, size_t nbytes); 返回值:若成功则返回已写的字节数,若出错返回-1
buf指针指向缓存区,nbytes为预计要写的字节数,若 write 函数执行成功,返回实际写入的字节数;若出错,返回-1。其返回值通常与参数nbytes的值相同,否则表示出错。
文件IO操作举例
下面举一个文件处理的小例子,原来文件的结构如下:
[task]
1. test 1
2. test 2
[End]
写一个函数来实现向task和end之间插入一个新的项。下面是代码实现:
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
void taskAdd(const char *str)
{
int fd;
;
int i;
char c;
off_t offset;
] = {};
] = {};
// copy data
strcpy(line, str);
int len = strlen(line);
line[len] = '\n';
line[len + ] = '\n';
// open the file
)
{
printf("open error\n");
return;
}
// read one line once a time
)) > && nRead < )
{
)) == '\n')
{
// find the [End] flag
)
{
, SEEK_CUR)) < )
{
printf("lseek error\n");
return;
}
) < )
{
printf("read error\n");
return;
}
, SEEK_SET)) < )
{
printf("lseek error\n");
return;
}
)
{
printf("write error\n");
return;
}
)
{
printf("write error\n");
return;
}
close(fd);
return;
}
memset(buf, , );
nRead = ;
}
}
}
文件IO的更多相关文章
- 标准io与文件io
A: 代码重复: 语句块1: while(判断) { 语句块2: 语句块1: } 上面可以改写为: while(1) { 语句块1: if(判断) break: 语句块2: } B: 标准IO和文件I ...
- 文件IO函数和标准IO库的区别
摘自 http://blog.chinaunix.net/uid-26565142-id-3051729.html 1,文件IO函数,在Unix中,有如下5个:open,read,write,lsee ...
- 转 漫谈linux文件IO
在Linux 开发中,有几个关系到性能的东西,技术人员非常关注:进程,CPU,MEM,网络IO,磁盘IO.本篇文件打算详细全面,深入浅出.剖析文件IO的细节.从多个角度探索如何提高IO性能.本文尽量用 ...
- (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO
. . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...
- Java文件IO操作应该抛弃File拥抱Paths和Files
Java7中文件IO发生了很大的变化,专门引入了很多新的类: import java.nio.file.DirectoryStream;import java.nio.file.FileSystem; ...
- Java 文件IO续
文件IO续 File类 用来将文件和文件夹封装成对象 方便对文件和文件夹的属性信息进行操作 File对象可以作为参数传递给流的构造函数 Demo1 File的构造方法 public cla ...
- Java 文件IO
文件IO Java IO IO流用来处理设备之间的数据传输 Java对数据的操作是通过流的方式 Java用于操作流的对象都在IO包中 按操作数据分为 字节流和字符流 字符流的 ...
- 文件IO和标准IO
2015.2.26 星期四,阴天 今天的内容主要是文件IO man 手册的分册: man -f open 查看那些分册中有openman 1 -- 普通的命令程序man 2 -- 系统调用man 3 ...
- 文件IO操作
前言 本文介绍使用java进行简单的文件IO操作. 操作步骤 - 读文件 1. 定义一个Scanner对象 2. 调用该对象的input函数族进行文件读取 (参见下面代码) 3. 关闭输入流 说明:其 ...
随机推荐
- ExtJs之工具栏及菜单栏
先培养一下大概的感觉吧. 基本按书上都弄出来了. <!DOCTYPE html> <html> <head> <title>ExtJs</titl ...
- 面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序
面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序 Ajax 为更好的 Web 应用程序铺平了道路 在 Web 应用程序开发中,页面重载循环是最大的一个使用障碍,对于 Java™ ...
- Outlook与Hotmail的设置
最近研究邮件备份,首先要使用客户端下载邮件,碰到不少问题:1. HOTMAIL GMAIL SINA的POP/IMAP默认居然都是关闭的,必须改成开放才行. GMAIL改成开放以后还是没有成功,好像还 ...
- SQLServer错误代码解释
SQLServer出现错误的代码大全(好用) Code Error Message 0 操作成功完成. 1 功能错误. 2 系统找不到指定的文件. 3 系统找不到指定的路径. 4 系统无法打开文件. ...
- Intellij Idea 15 生成serialVersionUID的方法
默认情况下Intellij IDEA是关闭了继承了Serializable接口的类生成serialVersionUID的警告.如果需要ide提示生成serialVersionUID,那么需要做以下设置 ...
- SQL SERVER ->> Wait Stats
等待状态是隔离SQL SERVER性能问题的一个重要手段,从实例级别入手.可以大概看出SQL SERVER在过去某段时间内是否处于某种资源的吃紧状态.不过取决于你是否有建立有效的性能数据收集机制.sy ...
- highcharts联合jquery ajax 后端取数据
Highcharts是一个制作图表的纯Javascript类库,主要特性如下: 兼容性:兼容当今所有的浏览器,包括iPhone.IE和火狐等等: 对个人用户完全免费: 纯JS,无BS: 支持大部分的图 ...
- hadoop中的ssh无密码登录配置
在配置Hadoop集群分布时,要使用SSH免密码登录,假设现在有两台机器hadoop@Master(192.168.1.101),作为Master机,hadoop@Slave(192.168.1.10 ...
- Java 数据结构之vector
Vector 实现了一个动态数组.是可实现自动增长的对象数组. vector和arraylist的比较: 1.都是采用数组格式存储数据,索引数据块插入数据慢 2.ArrayList会比Vector快, ...
- ava中拦截器 过滤器 监听器都有什么区别
过滤器,是在java web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts2的action进行业务逻辑,比如过滤掉非法u ...