文件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. Android 开发笔记___复选框__checkbox

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout ...

  2. web前端免费资源集

    web前端免费资源集 https://github.com/vhf/free-programming-books/blob/master/free-programming-books-zh.md

  3. ③bootstrap文本使用基础案例

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

  4. Javascript数组求和的方法总结 以及由斐波那契数列得到的启发

    一次面试中,面试官要求用三种不同的Javascript方法进行一个数字数组的求和,当时思来想去只想到了使用循环这一种笨方法,因此面试比较失败,在这里总结了六种Javascript进行数组求和的方法,以 ...

  5. 微信小程序入门(一)

    想必当你对官方文档了解地差不多的时候,一颗跃跃欲试的心就开始骚动了吧. 开发小程序之前的准备工作: 1).准备一个域名 2).准备一台云服务器 3).搭建小程序的后台,博主的小程序后台请求的的是自己写 ...

  6. CSS和文档

    1. 块级元素: p,div,ul,ol,h1,h2 . . . h6等.块级元素独占一行,旁边不能有其他元素. 2. 行内元素:span,a,strong,em等. display属性可以使块级元素 ...

  7. python变量命名规则

    在Python中使用变量时,需要遵守一些规则和指南.违反这些规则将引发错误,而指南旨在让你编写的代码更容易阅读和理解.请务必牢记下述有关变量的规则.  变量名只能包含字母.数字和下划线.变量名可以字 ...

  8. python基础教程——dict和set

    dict python内置字典:dict,全称dictionary,在其他语言中称为map,使用键值对存储. ex: d = {'xiaoli' : 95 , 'xiaoming' : 98 , 'x ...

  9. overflow-x: scroll;横向滑动详细讲解

    overflow-x: scroll;横向滑动(移动端使用详解) css3 , ie8以上 <!DOCTYPE html> <html lang="en"> ...

  10. Ani动态光标格式解析

    数据结构: Ani文件中的数据是按区段存放的,区段数据结构如下: 标识符(4字节ASCII),数据长度(一个DWORD),数据 按照此规则来看Ani文件,文件起始12字节可以理解为标准文件头,除数据长 ...