文件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. 在ASP.NET Core中使用AOP来简化缓存操作

    前言 关于缓存的使用,相信大家都是熟悉的不能再熟悉了,简单来说就是下面一句话. 优先从缓存中取数据,缓存中取不到再去数据库中取,取到了在扔进缓存中去. 然后我们就会看到项目中有类似这样的代码了. pu ...

  2. iOS 中的 Delayed Transition

    Android 的动画体系中,存在一类由 TransitionManager. beginDelayedTransition 管理的动画.这个方法,很特殊.执行此方法后,其后续的 UI 变化,不会立即 ...

  3. ⑦bootstrap按钮 图片 辅助使用基础案例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. 最大流——Dinic算法

    前面花了很长时间弄明白了压入-重标记的各种方法,结果号称是O(V3)的算法测demo的时候居然TLE了一个点,看了题解发现所有人都是用Dinic算法写的,但它的复杂度O(V2E)明显高于前者,具体是怎 ...

  5. 将Excel文件数据导入到SqlServer数据库的三种方案

    方案一: 通过OleDB方式获取Excel文件的数据,然后通过DataSet中转到SQL Server,这种方法的优点是非常的灵活,可以对Excel表中的各个单元格进行用户所需的操作. openFil ...

  6. gcc调试 学习1

    gdb进入调试 b 6 在第6行设置断点 d 2 删除num为2的断点 info b 查看断点 run 运行 n 执行到断点1 s 如果下一条是函数就进入函数 n 继续执行 print i  输出i的 ...

  7. java 分页导出百万级数据到excel

    最近修改了一个导出员工培训课程的历史记录(一年数据),导出功能本来就有的,不过前台做了时间限制(只能选择一个月时间内的),还有一些必选条件, 导出的数据非常有局限性.心想:为什么要做出这么多条件限制呢 ...

  8. [转载] java多线程学习-java.util.concurrent详解(三)ScheduledThreadPoolExecutor

    转载自http://janeky.iteye.com/blog/770441 ------------------------------------------------------------- ...

  9. 非关系型数据库redis-java基本操作

    概述 redis是一个key-value的nosql数据库(非关系型数据库).支持存储的value类型包括string(字符串).list(链表).set(集合).zset(sorted set -- ...

  10. Struts2-046验证脚本

    下面分享一下Struts2-046验证的python脚本 #encoding:utf-8 import urllib2 from poster.encode import multipart_enco ...