Linux程序设计——文件操作

底层文件访问

每个运行中的程序被称为进程(process),它有一些与之关联的文件描述符,你可以通过它们访问打开的文件和设备。
当一个程序开始运行时,它一般会有3个已经打开的文件描述符。

  • 0:标准输入
  • 1:标准输出
  • 2:标准错误

write系统调用

将缓冲区buf的前nbytes个字节写入与文件描述符fildes关联的文件中。

原型

size_t write( int fildes, const void *buf, size_t nbytes);

返回(实际写入的字节数)

0 表示未写入任何数据
-1 write调用过程中出现了错误

示例

#include <unistd.h>
#include <stdlib.h>

int main()
{
    if ((write(1, "Here is some data\n", 18)) != 18)
        write(2, "A write error has occurred on file descriptor 1\n",46);

    exit(0);
}

read系统调用

从与文件描述符fildes相关联的文件里读取nbytes个字节的数据,并将其放到数据区buf中。

原型

size_t read( int fildes, void *buf, size_t nbytes);

返回(实际读入的字节数)

0 表示未读入任何数据,到达文件末尾。
-1 read调用过程中出现了错误

示例

#include <unistd.h>
#include <stdlib.h>

int main()
{
    char buffer[128];
    int nread;

    nread = read(0, buffer, 128);
    if (nread == -1)
        write(2, "A read error has occurred\n", 26);

    if ((write(1,buffer,nread)) != nread)
        write(2, "A write error has occurred\n",27);

    exit(0);
}

运行

使用echo通过管道为程序提供输入

$ echo hello world | ./simple_read

使用文件重定向为程序提供输入

$ ./simple_read < simple_read.c

open系统调用

创建一个新的文件描述符,与对应文件进行关联。

原型

int open( const char *path, int oflags);
int open( const char *path, int oflags,  mode_t mode);

返回

如果调用成功,它将返回一个可以被read、write和其他系统调用使用的文件描述符。
如果调用失败,返回-1。
这个文件描述符是唯一的,它不会与任何其他运行的进程共享。如果两个进程同时打开同一个文件,它们会分别得到两个不同的文件描述符。

访问模式oflags

  • O_RDONLY 以只读方式打开
  • O_WRONLY 以只写方式打开
  • O_RDWR 以读写方式打开

close系统调用

终止文件描述符fildes与对应文件之间的关联。

原型

int close( int fildes);

底层系统调用为什么效率非常低

执行系统调用时,Linux必须从运行用户代码切换到执行内核代码,然后再返回用户代码。减少这种开销的一个好方法是,在程序中尽可能减少系统调用的次数,并且让每次系统调用完成尽可能多的工作。例如,每次读写大量的数据而不是每次仅读写一个字符。

文件复制程序(逐字复制)

源码

#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

int main()
{
    char c; 
    int in, out;

    in = open("file.in", O_RDONLY); 
    out = open("file.out", O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
    while(read(in,&c,1) == 1)//每次读取一个字节写入一个字节
        write(out,&c,1);

    exit(0);
}

运行

$ gcc -o copy_system copy_system.c
$ TIMEFORMAT="" time ./copy_system

输出

0.02user 5.21system 0:05.73elapsed 91%CPU (0avgtext+0avgdata 892maxresident)k
0inputs+2048outputs (0major+48minor)pagefaults 0swaps

文件复制程序(块复制)

#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

int main()
{
    char block[1024]; 
    int in, out; 
    int nread;

    in = open("file.in", O_RDONLY); 
    out = open("file.out", O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
    while((nread = read(in,block,sizeof(block))) > 0)
        write(out,block,nread);

    exit(0);
}

运行

$ gcc -o copy_system copy_block.c
$ TIMEFORMAT="" time ./copy_block

输出

0.00user 0.01system 0:00.03elapsed 32%CPU (0avgtext+0avgdata 884maxresident)k
0inputs+2048outputs (0major+47minor)pagefaults 0swaps

标准I/O库

标准I/O库提供输出缓冲功能,你可以高效地写任意长度的数据块,库函数则在满足数据块长度时安排执行底层系统调用,这就极大降低了系统调用的开销。
标准I/O库(stdio)及其头文件stdio.h为底层I/O系统调动提供了一个通用的接口。

fopen函数

fopen打开filename参数指定的文件,并把它与一个文件流关联起来。

原型

FILE * fopen(const char * path,const char * mode);

返回

成功时,返回一个非空的FILE *指针。
失败时,返回NULL。

fread函数

fread库函数用于从一个文件流里读取数据。数据从文件流stream读到由ptr指向的数据缓冲区里。size参数指定每个数据记录的长度,计数器nitems给出要传输的记录个数。

原型

size_t fread(void *ptr, size_t size, size_t nitems, FILE *stream);

返回

返回值是成功读到数据缓冲区里的记录个数(不是字节数)。当到达文件末尾时,它的返回值可能会小于nitems,甚至可以是零。

fwrite函数

从指定的数据缓冲区ptr里取出数据记录,并把它们写到输出流stream中。

原型

size_t fwrite(const void *ptr, size_t size, size_t nitems, FILE *stream);

fclose函数

关闭指定的文件流stream,使所有尚未写出的数据都写出。因为stdio库会对数据进行缓冲,所有使用fclose是很重要的。

原型

int fclose(FILE *stream);

fflush函数

把文件流里的所有未写出数据立刻写出。注意,调用fclose函数隐含执行了一次fflush操作。

原型

int fflush(FILE *stream);

fgetc函数

fgetc函数从文件流里取出下一个字节并把它作为一个字符返回。当它到达文件末尾或者出现错误时,它返回EOF。你必须通过ferror或feof来区分这两种情况。

原型

int fgetc(FILE *stream);

fputc函数

fgetc函数把一个字符写到一个输出文件流中。它返回写入的值,如果失败,则返回EOF。

原型

int fputc(int c, FILE *stream);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值