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

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

文件IO的基本概念

下面的这张图表示了打开文件的内核数据结构:

简单来说,首先是一个文件面向用户的文件描述符,然后文件指针指向一个文件表,用来保存文件相关的信息。接下来可以认为与文件物理存储相关的结构。其中涉及的几个文件IO基本概念解释如下:

文件描述符

当调用open函数打开一个文件时会返回一个非负整数来标志该文件。一方面方便内核的管理,另一方面方便代码基于此标志进行后续的文件操作。在UNIX系统中,stdin与文件描述0相互关联;stdout与文件描述符1相互关联;stderr与文件描述符2相互关联。并使用下面的宏定义来提高程序的可读性:STDIN_FILENOSTDOUT_FILENOSTDERR_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的更多相关文章

  1. 标准io与文件io

    A: 代码重复: 语句块1: while(判断) { 语句块2: 语句块1: } 上面可以改写为: while(1) { 语句块1: if(判断) break: 语句块2: } B: 标准IO和文件I ...

  2. 文件IO函数和标准IO库的区别

    摘自 http://blog.chinaunix.net/uid-26565142-id-3051729.html 1,文件IO函数,在Unix中,有如下5个:open,read,write,lsee ...

  3. 转 漫谈linux文件IO

    在Linux 开发中,有几个关系到性能的东西,技术人员非常关注:进程,CPU,MEM,网络IO,磁盘IO.本篇文件打算详细全面,深入浅出.剖析文件IO的细节.从多个角度探索如何提高IO性能.本文尽量用 ...

  4. (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  5. Java文件IO操作应该抛弃File拥抱Paths和Files

    Java7中文件IO发生了很大的变化,专门引入了很多新的类: import java.nio.file.DirectoryStream;import java.nio.file.FileSystem; ...

  6. Java 文件IO续

    文件IO续 File类    用来将文件和文件夹封装成对象 方便对文件和文件夹的属性信息进行操作    File对象可以作为参数传递给流的构造函数 Demo1 File的构造方法 public cla ...

  7. Java 文件IO

    文件IO Java IO    IO流用来处理设备之间的数据传输 Java对数据的操作是通过流的方式 Java用于操作流的对象都在IO包中    按操作数据分为 字节流和字符流        字符流的 ...

  8. 文件IO和标准IO

    2015.2.26 星期四,阴天 今天的内容主要是文件IO man 手册的分册: man -f open 查看那些分册中有openman 1 -- 普通的命令程序man 2 -- 系统调用man 3 ...

  9. 文件IO操作

    前言 本文介绍使用java进行简单的文件IO操作. 操作步骤 - 读文件 1. 定义一个Scanner对象 2. 调用该对象的input函数族进行文件读取 (参见下面代码) 3. 关闭输入流 说明:其 ...

随机推荐

  1. dd大牛的《背包九讲》

    P01: 01背包问题 题目 有N件物品和一个容量为V的背包.第i件物品的费用是c[i],价值是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大. 基本思路 这是最 ...

  2. Java异步消息平台

    l  JAVA平台异步消息模块 JAVA平台异步消息模块,是一个针对RabbitMQ的消息发送及处理封装,包含消息的配置.发送.接收.失败重试.日志记录等,总共分为4个部分: 1)RabbitMQ访问 ...

  3. java集合TreeMap应用---求一个字符串中,每一个字母出现的次数

    package cn.itcast.p1.map.test; import java.util.Iterator; import java.util.Map; import java.util.Tre ...

  4. Visual Studio 快捷键

    Visual Studio 快捷键 CTRL + DELETE 删除至词尾 CTRL + BACKSPACE 删除至词头Ctrl+Shift+L: 删除当前行 Ctrl+K+Crtr+C: 注释选定内 ...

  5. (1)搭建opencv-android环境

    前言: 本文目的是指导在windows平台搭建一个opencv for android 的开发环境,作者参考了很多网上的教程,本文所使用的各种软件.插件都是截止到写这篇文章的最新版本,作者在实际搭建环 ...

  6. android模拟器(genymotion)+appium+python 框架执行过程中问题解答

    1.case运行过程中中文输入不进去? 答:注意事项 1)需要修改系统编码为utf-8,才能解决中文输入问题,case执行入口文件添加代码如下: import sys reload(sys) sys. ...

  7. 常用搜索引擎的算分,你get了嘛?

    搜索引擎发展至今,已公布了多种算法.作为SEOER的你,还不懂,就out啦.懂了不会用,也是然并卵的一种行为.了解算法知识并不懂得如何把算法实践于SEO工作的你,还是处于学生思维,是时候该升级了.且听 ...

  8. ECharts案例教程1

    原文:http://blog.csdn.net/whqet/article/details/42703973 简介 ECharts,缩写来自Enterprise Charts,商业级数据图表,是百度的 ...

  9. 可运行jar包生成步骤和jar包的生成

    一.可运行jar包生成步骤 1.进入.class文件所在目录,新建一个记事本文件,假设为1.txt,文件内容: 1> Main-Class:可运行类的名字  (  例如:Main-Class:T ...

  10. 添加dubbo xsd的支持

    使用dubbo时遇到问题: org.xml.sax.SAXParseException: schema_reference.4: Failed to read schema document 'htt ...