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

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

写操作

#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;
}

读操作

#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.执行写操作:

[email protected]:~/workspace/FileIOWrite/Debug$./FileIOWrite </etc/mtab

写入数据总长度是:990

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

[email protected]:~/workspace/FiloIORead/Debug$./FiloIORead

读入数据长度是:400

读入数据长度是:400

读入数据长度是:200

读入数据长度是:0

……..

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

读入数据长度是:0

3.再次执行写操作

[email protected]:~/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


无缓冲区


不带缓冲

#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

举例:

#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

举例:

#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);

举例:

#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)

#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操作相关系统编程

时间: 2024-10-21 23:50:17

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

linux下的文件IO缓冲区,及其相关操作

linux下的文件IO操作 浅谈文件IO缓冲 Read()和write()函数在操作磁盘文件时不会直接发起磁盘访问,而是仅仅在用户空间缓冲区与内核缓冲区高速缓存之间复制数据. 当调用write()函数的写入3个字节的时候,由于系统调用与磁盘操作并不同步,在write()函数结束后续某个时刻,内核才会将其缓冲区中的数据写入磁盘.如果在此期间,另一个进程试图读取该文件的这几个字节,那么内核将自动从缓冲区高速缓存中提供这些数据,而不是文件中. 与此同理,对输入而言,内核从磁盘中读取数据并存储到内核缓冲

Linux学习记录--文件管理相关系统编程

文件管理相关系统编程 重要文件标识 打开文件标识 O_RDONLY:只读方式打开 O_WRONLY:只写方式打开 O_RDWR:可读写方式打开 打开文件操作副标识 O_CREAT:若路径中文件不存在则创建,使用Open函数时需同时指定文件权限 O_EXCL:若与O_CREAT连用,检查文件是否已经存在,若不存在则建立文件存在则返回错误,这使创建和测试成为一个原子操作 O_APPEND:读写文件从文件尾部开始移动,所有写入数据都加入文件尾部 O_TRUNC:若文件存在并且可以写入,此标识会将源文件

EasyARM i.mx28学习笔记——文件IO方式操作GPIO

0 前言 本文描述如果通过文件IO sysfs方式控制EasyARM GPIO端口.通过sysfs方式控制GPIO,先访问/sys/class/gpio目录,向export文件写入GPIO编号,使得该GPIO的操作接口从内核空间暴露到用户空间,GPIO的操作接口包括direction和value等,direction控制GPIO方向,而value可控制GPIO输出或获得GPIO输入. Linux学习可从应用出发,先不纠结Linux驱动编写,先把Linux给玩起来. [相关博文] [EasyARM

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

0 前言 本文描述如果通过文件IO sysfs方式控制树莓派 GPIO端口.通过sysfs方式控制GPIO,先访问/sys/class/gpio目录,向export文件写入GPIO编号,使得该GPIO的操作接口从内核空间暴露到用户空间,GPIO的操作接口包括direction和value等,direction控制GPIO方向,而value可控制GPIO输出或获得GPIO输入. Linux学习可从应用出发,先不纠结Linux驱动编写,先把Linux给玩起来. [相同与不同] 本文和[EasyARM

linux学习(2)文件操作

pwd 显示当前目录 touch 创建空白文件或者更新已有文件的时间 .开头的文件为隐藏文件 列出目录内容 ls -a 显示所有文件 包括隐藏文件 ls -r 显示详细信息 ls -R 显示子目录的结构 ls -ld显示目录和链接的信息 file查看文件的类型 cd 切换目录 复制 cp 源文件 目标文件(目录) 复制文件夹 cp -r 源文件夹 目标文件夹 显示复制的详细信息 cp -v 移动 mv 文件 目标目录 移动并且修改文件名 mv 文件 目标目录/文件名 重命名 mv 文件 文件名

Linux学习笔记——例说makefile 增加系统共享库

0.前言 从学习C语言开始就慢慢开始接触makefile,查阅了很多的makefile的资料但总感觉没有真正掌握makefile,如果自己动手写一个makefile总觉得非常吃力.所以特意借助博客总结makefile的相关知识,通过例子说明makefile的具体用法. 例说makefile大致分为4个部分 1.只有单个C文件 2.含有多个C文件 3.需要包括头文件路径 4.增加宏定义 5.增加系统共享库 6.增加自定义共享库 7.一个实际的例子 [代码仓库]--makefile-example

linux 学习记录- 用户切换

一.用户切换:      缘由:1.使用一般账号操作系统,这是平日操作的好习惯.2.用较低权限启动系统服务:如apache软件,可以建立        apache用户来启动apache软件,如果这个程序被攻破,系统也不至于损毁.3.软件本身的限制. a.su  最简单的切换用户身份的方法       使用方法:  su [-lm][-c 指令][username]            详解: -:代表使用login-shell的变量档案读取方式来登入系统,若后面没有用户名,代表切换到root

imx6用文件io操作gpio

具体请参考: http://blog.csdn.net/u014213012/article/details/53140781 这里要注意的是: 要让linux支持文件io方式操作gpio,首先驱动必须得支持,也就是说设备树上必须先配置好gpio模式,然后参照以上链接去实现gpio操作 这里举例来说:hud项目中(imx6dl平台),有一个蓝牙电源的使能受GPIO1_IO30的控制,所以我们必须得在设备树上配置这个pad为GPIO模式 1.配置gpio模式 现在需要在设备树上配置GPIO1_IO

Delphi关于记录文件的操作

http://www.cnblogs.com/railgunman/archive/2010/08/16/1801004.html Delphi关于记录文件的操作 本例子几个变量的说明TFileRec = record //记录定义Day : Integer;... //其他定义end;f : File of TFileRec;  //标准的输入/输出文件FilRec : TFileRec;    //记录数据FileName ;             //记录文件的名称关于记录文件的相关操作