文件IO操作相关系统编程

这里主要说两套IO操作接口,各自是:

POSIX标准

read|write接口。函数定义在#include<unistd.h>

ISO C标准

fread|fwrite接口。函数定义在#include<stdio.h>

有书上说POSIX标准与ISO C标准的差别在于文件读写是否带缓冲区,我则不是非常认同,因此POSIX标准下的IO操作也是带缓冲区的,至于这两个标准下的IO性能谁更加好则不一定。由于这和缓冲区的大小,以及用户逻辑有非常大关系。

POSIX标准

ssize_t read (int __fd, void *__buf, size_t __nbytes)

ssize_t write (int __fd, constvoid *__buf, size_t __n)

读规则:

如预读字节数>文件总字节数,则所有读入文件字节数。返回值为文件字节数

如预读字节数<文件总字节数,则读满__buf(以__nbytes为准)后返回。下回读取会将继续读

如读到文件末尾,则返回值为0

比方:文件长度是100。buf长度是70,那么第一个读取70,读2此会读取剩下的30 ,第三次因为文件游标已经处于文件末尾,则返回0

写操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include
<unistd.h>
#include
<fcntl.h>
#include<stdio.h>
#define
BUFFER_SIZE 200
int main()
{
    int fd
= -1;
    if (access("/tmp/iofile",
F_OK)) {
        fd
= creat(
"/tmp/iofile",
0777);
    else {
        fd
= open(
"/tmp/iofile",
O_WRONLY | O_APPEND);
    }
    if (fd
== -1) {
        perror("文件打开错误!");
        return -1;
    }
    char buf[BUFFER_SIZE];
    int val
= 0, sum = 0;
    do {
        val
= read(0, buf, BUFFER_SIZE);
        if (val
> 0) {
            write(fd,
buf, BUFFER_SIZE);
        else {
            break;
        }
        sum
+= val;
    while (1);
    printf("写入数据总长度是:%d\n",
sum);
    return 1;
}

读操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include
<unistd.h>
#include
<fcntl.h>
#include<stdio.h>
#define
BUFFER_SIZE 400
int main()
{
    int fd
= open(
"/tmp/iofile",
O_RDONLY);
    if (fd
== -1) {
        perror("文件打开错误!

");

        return -1;
    }
    char buf[BUFFER_SIZE];
    int val
= 0, sum = 0;
    do {
        val
= read(fd, buf, BUFFER_SIZE);
        printf("读入数据长度是:%d\n",
val);
        if (val
> 0) {
            write(1,
buf, BUFFER_SIZE);
            printf("\n");
        else {
            sleep(1);
        }
        sum
+= val;
    while (1);
    return 1;
}

运行顺序

1.运行写操作:

tkf@tkf:~/workspace/FileIOWrite/Debug$./FileIOWrite </etc/mtab

写入数据总长度是:990

2.在另外命令行(进程)运行读操作

tkf@tkf:~/workspace/FiloIORead/Debug$./FiloIORead

读入数据长度是:400

读入数据长度是:400

读入数据长度是:200

读入数据长度是:0

……..

è因为此时文件游标已经处于文件末端因此,长度是0

读入数据长度是:0

3.再次运行写操作

tkf@tkf:~/workspace/FileIOWrite/Debug$./FileIOWrite </etc/mtab

写入数据总长度是:990

此时读端进程输出

读入数据长度是:400

读入数据长度是:400

读入数据长度是:200

读入数据长度是:0

è由于再次有数据写入,所以能够读到数据,当数据再次读取完成,则返回0

当然对于第三步骤,我们也能够通过更改读进程游标的位置(lseek)使其能读到数据

IO效率

依据书上效率对照试验,当缓冲区大小(buf)等于文件系统块大小时。性能是最佳的。

文件系统块大小struct stat –>st_blksize 查看

对于IO操作主要步骤能够理解为:

1.内核与系统缓冲区的数据拷贝

2.系统缓冲区与用户缓冲区的拷贝

举例,用户BUF是10字节,系统缓冲区时4096字节,那么到我们写端将用户BUF数据拷贝被系统缓冲区中。因为系统缓冲区没有填满,因此不会运行IO操作,直到写满或者运行同步操作。对于读来说,文件系统会将数据先都预读到系统缓冲区,每次我们请求读都是从系统缓冲区复制到用户缓冲器。

因此在数据存在缓冲区并没有写到磁盘时假设系统出现问题可能数据会丢失。

ISO C标准(标准IO)

标准IO是环绕流的,他与POSIX标准相比能够使用户不用关注分配缓冲区的大小。他会选择适当缓冲区以优化运行IO

冲洗(fflush)

对于标准IO来说,冲洗就是讲缓冲区的数据写入磁盘

缓冲

对于标准IO库提供了三种类型的缓冲

全缓冲:在填满标准IO缓冲区后才进行实际的IO操作

行缓冲:当输入和输出遇到换行符时才运行实际的IO操作

不带缓冲:每次一个都进行实际的IO操作

void setbuf(FILE *__restrict __stream, char *__restrict __buf) ;

int setvbuf (FILE *__restrict __stream, char *__restrict __buf,

int __modes, size_t __n) ;

參数:

__n:缓冲区长度

__buf:缓冲区指针

__stream文件流

__modes:缓冲模式

_IOFBF0:全缓冲

_IOLBF 1:行缓冲

_IONBF 2:无缓冲

函数

mode

buf

缓冲区长度

缓冲类型

setbuf

 

非空

长度为BUFSIZ的BUF

全缓冲,行缓冲

NULL

无缓冲区

不带缓冲

setvbuf

IOFBF

非空

长度为size的buf

全缓冲

NULL

合适长度的系统缓冲区

IOLBF

非空

长度为size的buf

行缓冲

NULL

合适长度的系统缓冲区

IONBF

 

无缓冲区

不带缓冲

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include
<stdio.h>
#include
<stddef.h>
#include
<string.h>
int main()
{
    FILE *
iostream = 
fopen("fopenfile""w+");
    if (iostream
== NULL) {
        perror("流打開錯誤");
        return -1;
    }
    setvbuf(iostream,
NULL, _IOFBF, BUFSIZ); 
//1
    char *buf
"abcde"//2
    int size
fwrite(buf, sizeof(char), strlen(buf)+1
,iostream);
    printf("寫入的數據是:%s",
buf);
    fflush(iostream); //3
    sleep(-1);
    return 1;
}

针对上述代码做例如以下分析:

将3处进行凝视。并运行

fopenfile文件无不论什么内容,因此如今数据都在缓冲区。因为进程SLEEP流未关闭。而且缓冲区也没有写满,因此不会运行IO操作

不凝视不论什么内容,并运行

fopenfile文件内容为abcde,因为fflush冲洗将缓冲区数据写入文件

将1处缓冲模式改为_IOLBF,凝视3处。并运行

fopenfile文件无不论什么内容,尽管指定了行缓冲可是没有行结束符,因此数据在缓冲区内,没有进行IO操作

将1处缓冲模式改为_IOLBF,凝视3处,并将2处数据改为buf=”abcde\n” ,运行

fopenfile文件内容为abcde,因为设置行行缓冲,而且存在结束符。因此进行了IO操作

将1处缓冲模式改为_IONBF,凝视3处,并运行

fopenfile文件内容为abcde,因为设置无缓冲,因此每次写入都会进行IO操作

主要函数

打开关闭流

打开一个指定的文件

FILE *fopen (constchar *__restrict __filename,

constchar *__restrict __modes)

通过文件描写叙述符打开一个指定的文件

FILE *fdopen (int __fd, constchar *__modes)

modes:打开模式

R或rb

为读而打开

W或wb

把文件截断为0长,或为写而创建

A或ab

加入;在文件尾为写而打开,或为写而创建

R+

为读和写而打开

W+

把文件截断为0长,或为为读和写而打开

A+

为在文件尾为写而打开或创建

关闭文件流

intfclose (FILE *__stream);

单字符读写

读函数

int fgetc (FILE *__stream);

int getc (FILE *__stream);

int getchar (void);

fgetc是一个函数,getc可实现为宏,getchar为从标准输出流中获取一个字符,相当于getc(stdin)

返回值:若成功则返回读取到的字符,若失败或读到文件尾则返回-1

因为不管失败或者读到文件尾都返回-1,为了区分哪种原因返回的-1。提供以下2个函数

以读到文件结尾返回

int feof (FILE *__stream)

以产生错误返回返回

int ferror (FILE *__stream)

返回值:条件为真返回非0.条件为假返回0

写函数

int fputc(int __c, FILE *__stream);

int putc (int __c, FILE *__stream);

int putchar (int __c);

返回值:成功返回c,失败返回EOF

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include
<stdio.h>
#include
<stddef.h>
#include
<string.h>
int main()
{
    FILE *
iostream = 
fopen("fopenfile""r");
    if (iostream
== NULL) {
        perror("流打開錯誤");
        return -1;
    }
    char c;
    while ((c
getc(iostream))
!= -1) {
        printf("读取的字符是:");
        putchar(c);
        printf("\n");
    }
    if (feof(iostream))
{
        printf("读取到文件末尾结束\n");
    }
    if (ferror(iostream))
{
        printf("读取出现异常结束\n");
    }
    return 1;
}

读取的字符是:a

读取的字符是:b

读取的字符是:c

读取的字符是:d

读取的字符是:e

读取的字符是:

读取到文件末尾结束

整行读写

写函数

int fputs (constchar *__restrict __s, FILE *__restrict __stream);

int puts (constchar *__s);

返回值:成功返回非负值。错误返回EOF

读函数

char *fgets (char *__restrict __s, int __n, FILE *__restrict __stream)

char *gets (char *__s)

返回值:成功返回buf, 失败或达到文件结尾返回NULL

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include
<stdio.h>
#include
<string.h>
int main()
{
    FILE *iostream
fopen("fopenfile""r+");
    if (iostream
== NULL) {
        perror("文件打开错误!

");

        return -1;
    }
    int reval
fputs("hello
world\n"
,
iostream);
    if (reval
== -1) {
        perror("文件写入失败。");
        return -1;
    }
    fflush(iostream);
    fclose(iostream);
     iostream
fopen("fopenfile""r+");
    if (iostream
== NULL) {
        perror("文件打开错误!

");

        return -1;
    }
    char buf[1000];
    memset(buf, '\0',
1000);
    char *getval
fgets(buf,
1000, iostream);
    printf("读入一行数据是:%s",
buf);
}

二进制IO

前面说到的单字节以及整行读写。假设要写一个结构则须要每个结构项的读写非常麻烦。这就须要用到2进制IO

读操作

size_t read (void *__restrict __ptr, size_t __size,

size_t __n, FILE *__restrict __stream)

__pt:结构体(数据)指针

__size:单个结构体大小

__n:结构体数量

__stream:文件流

返回值:读或写的对象数

写操作

size_t fwrite (constvoid *__restrict __ptr, size_t __size,

size_t __n, FILE *__restrict __s);

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include
<stdio.h>
#include
<string.h>
typedef struct {
    int no;
    char name[100];
    long tel;
}
Info;
int main(int argc, char *argv[])
{
    if (0
== 
strcmp(argv[1], "read"))
{
        FILE *ios
fopen("binaryfile""r");
        Info
info;
        fread(&info, sizeof(Info),
1, ios);
        printf("no=%d\n",
info.no);
        printf("name=%s\n",
info.name);
        printf("tel=%ld\n",
info.tel);
        if (getc(ios)
== -1) {
            if (feof(ios))
{
                printf("读取到文件末尾结束\n");
            }
            if (ferror(ios))
{
                printf("读取出现异常结束\n");
            }
        }
    else if (0
== 
strcmp(argv[1], "write"))
{
        FILE *ios
fopen("binaryfile""w");
        Info
info;
        info.no
= 1;
        info.tel
= 1234567;
        char *name
"hello";
        memcpy(info.name,
name, 
strlen(name)
+ 1);
        fwrite(&info, sizeof(Info),
1, ios);
    }
    return 1;
}

运行结果:

no=1

name=hello

tel=1234567

读取到文件末尾结束

说明:

1.生成的文件为2进制文件,如打开看到的会是乱码

2.最后须要在此尝试读入一个字符,那么流才会结束,才会使用feof等推断

文件流定位

能够设置文件位置指示器来影响一个文件读取,使用方法和sleek一致

获取当前文件位置指示器

long int ftell (FILE *__stream)

返回值:当前文件位置指示器

int fgetpos (FILE *__restrict __stream, fpos_t *__restrict __pos);

返回值:成功返回0,失败返回非0值

当前文件位置指示器

int fseek (FILE *__stream, longint __off, int __whence);

返回值:成功返回0,失败返回非0值

int fsetpos (FILE *__stream, constfpos_t *__pos);

返回值:成功返回0。失败返回非0值

重定位文件位置指示器

void rewind (FILE *__stream);

相当于(void)fseek(stream, 0L,SEEK_SET)

返回值:成功返回0,失败返回非0值

暂时文件

char *tmpnam (char *__s)

char *tempnam (constchar *__dir, constchar *__pfx)

FILE *tmpfile (void) __wur;

int mkstemp (char *__template)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include
<stdio.h>
int main()
{
    char name[1000];
    char *reval
tmpnam(name);
    printf("同意最大随机文件名称个数:%d\n",
TMP_MAX);
    printf("文件名称:%s\n",
reval);
    char *newname
= tempnam(
"/home/tkf/","SDF");
    printf("扩展文件名称:%s\n",
newname);
    FILE *ios=tmpfile();
    sleep(10);
    fclose(ios);
    printf("暂时文件删除成功!\n");
    return 1;
}

Linux学习记录--文件IO操作相关系统编程的更多相关文章

  1. Linux文件IO操作

    来源:微信公众号「编程学习基地」 目录 文件操作 Linux文件类型 Linux文件权限 修改文件权限 Linux error 获取系统调用时的错误描述 打印错误信息 系统IO函数 open/clos ...

  2. 树莓派学习笔记——使用文件IO操作GPIO SysFs方式

    0 前言     本文描写叙述假设通过文件IO sysfs方式控制树莓派 GPIO端口.通过sysfs方式控制GPIO,先訪问/sys/class/gpio文件夹,向export文件写入GPIO编号, ...

  3. linux文件IO操作篇 (一) 非缓冲文件

    文件IO操作分为 2 种 非缓冲文件IO 和 缓冲文件IO 它们的接口区别是 非缓冲 open() close() read() write() 缓冲 fopen() fclose() fread() ...

  4. Linux 学习笔记 1 使用最小的系统,从分区安装系统开始

    我们常用的linux系统在安装过程中大多都省略了对系统进行分区的操作,以至于后期,不了解什么是分区以及分区当中最基本的一些概念, 我们不说最细的知识,只求了解这个过程,那直接步入正题,开始第一节的学习 ...

  5. 9.2 Go 文件IO操作

    9.2 Go 文件IO操作 1.1.1. bufio包 带有缓冲区的IO读写操作,用于读写文件,以及读取键盘输入 func main() { //NewReader返回一个结构体对象指针 reader ...

  6. Delphi关于记录文件的操作

    http://www.cnblogs.com/railgunman/archive/2010/08/16/1801004.html Delphi关于记录文件的操作   本例子几个变量的说明TFileR ...

  7. imx6用文件io操作gpio

    具体请参考: http://blog.csdn.net/u014213012/article/details/53140781 这里要注意的是: 要让linux支持文件io方式操作gpio,首先驱动必 ...

  8. 文件IO操作

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

  9. Linux学习之文件特殊权限详解(SetUID、SetGID、Sticky BIT)(十一)

    Linux学习之文件特殊权限详解(SetUID.SetGID.Sticky BIT) 目录 SetUID SetGID Sticky BIT SetUID SetUID简介 只有可以执行的二进制程序和 ...

随机推荐

  1. Python基础学习参考(三):内置函数

    一:内置函数 在第一篇文章中,我们简单的认识了一下print()函数和input()函数,也就是输入和输出,这些函数我们可以直接的调用,不要自己定义或者引入什么,对吧?想这样的函数就叫做内置函数.这里 ...

  2. css中单位 px、em 的区别【转载】

    原文:http://www.admin10000.com/document/6267.html     在国内网站中,包括三大门户,以及“引领”中国网站设计潮流的蓝色理想,ChinaUI等都是使用了p ...

  3. Spring AOP高级——源码实现(1)动态代理技术

    在正式进入Spring AOP的源码实现前,我们需要准备一定的基础也就是面向切面编程的核心——动态代理. 动态代理实际上也是一种结构型的设计模式,JDK中已经为我们准备好了这种设计模式,不过这种JDK ...

  4. python小小面试题

    一.python是如何进行内存管理的?Python引入了一个机制:引用计数.python内部使用引用计数,来保持追踪内存中的对象,Python内部记录了对象有多少个引用,即引用计数,当对象被创建时就创 ...

  5. WebService--jax-spring集成

    如果使用javax.jws内容编写webservice,则只能通过将程序打成jar包的形式运行,如果要想通过web容器进行发布,则需要使用其他webservice框架.下面介绍jaxws与spring ...

  6. SimpleMembership续

    自上篇SimpleMembership之后,好久不用,也没有研究,最近把以前写的老程序改进下,原有用户系统升级为SimpleMembership,在升级的过程中发现还有许多问题,经过几天的试验,小有收 ...

  7. c#基础知识索引器

    代码 ]);    }} 在这里我们看到,无非是实现了一个泛型算法 等同于 Ontology List<string> lit=new List<string>(); lis. ...

  8. 设计模式的征途—10.装饰(Decorator)模式

    虽然目前房价依旧很高,就连我所在的成都郊区(非中心城区)的房价均价都早已破万,但却还是阻挡不了大家对新房的渴望和买房的热情.如果大家买的是清水房,那么无疑还有一项艰巨的任务在等着大家,那就是装修.对新 ...

  9. Python进阶---面向对象第三弹(进阶篇)

    Python对象中一些方法 一.__str__ class Teacher: def __init__(self,name,age): self.name=name self.age=age self ...

  10. 判断pdf、word文档、图片等文件类型(格式)、大小的简便方法

    判断pdf.word文档.图片等文件类型(格式).大小的简便方法 很久没发文了,今天有时间就写一下吧. 关于上传文件,通常我们都需要对其进行判断,限制上传的类型,如果是上传图片,我们甚至会把图片转化成 ...