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

文件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


比方:文件长度是100buf长度是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 ", 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 ", val);
        if (val > 0) {
            write(1, buf, BUFFER_SIZE);
            printf(" ");
        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.系统缓冲区与用户缓冲区的拷贝


举例,用户BUF10字节,系统缓冲区时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

 

非空

长度为BUFSIZBUF

全缓冲,行缓冲

NULL

无缓冲区

不带缓冲

setvbuf

IOFBF

非空

长度为sizebuf

全缓冲

NULL

合适长度的系统缓冲区

IOLBF

非空

长度为sizebuf

行缓冲

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 ” ,运行

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


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

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


主要函数


打开关闭流

打开一个指定的文件

FILE *fopen (constchar *__restrict __filename,

constchar *__restrict __modes) 


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

FILE *fdopen (int __fd, constchar *__modes) 


modes:打开模式

Rrb

为读而打开

Wwb

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

Aab

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

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(" ");
    }
    if (feof(iostream)) {
        printf("读取到文件末尾结束 ");
    }
    if (ferror(iostream)) {
        printf("读取出现异常结束 ");
    }
    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 ", 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, '', 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 ", info.no);
        printf("name=%s ", info.name);
        printf("tel=%ld ", info.tel);
        if (getc(ios) == -1) {
            if (feof(ios)) {
                printf("读取到文件末尾结束 ");
            }
            if (ferror(ios)) {
                printf("读取出现异常结束 ");
            }
        }
    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 ", TMP_MAX);
    printf("文件名称:%s ", reval);
    char *newname = tempnam("/home/tkf/","SDF");
    printf("扩展文件名称:%s ", newname);
    FILE *ios=tmpfile();
    sleep(10);
    fclose(ios);
    printf("暂时文件删除成功! ");
    return 1;
}



原文地址:https://www.cnblogs.com/zsychanpin/p/7379703.html