Linux系统编程:文件I/O编程

文件I/O操作在Linux编程时是经常会使用到的一个内容,通常可以把比较大的数据写到一个文件中来进行存储或者与其他进程进行数据传输,这样比写到一个全局数组或指针参数来实现还要方便好多。

一、文件常用操作与相关函数

1.打开或创建一个文件:编写程序时涉及内容——打开或创建文件的路径名称、打开文件的方式(读|写|执行)、访问权限(用户|组|其他)、实现方式(系统调用|库)。

①系统调用方式:

1 #include <fcntl.h>      //函数原型声明、flags定义
2 #include<sys/stat.h>    //mode定义
3 int open( const char * pathname,int flags, mode_t mode);//当需要创建新的文件时才需要使用第3个参数mode

pathname:不带路径时默认在当前目录下

flags:打开方式,常用选项——O_RDWR(读写打开)、O_RDONLY(只读打开)、O_WRONLY(只写打开)、O_CREAT(文件不存在时创建)、

mode:使用O_CREAT时设置访问权限,常用选项——S_IRUSR(用户可读)、S_IWUSR(用户可写)、S_IXUSR(用户可执行)

返回值:成功——文件描述符(int),失败——(-1)

打开一个文件操作结束之后要记得关闭文件,与其对应的函数:

#include<unistd.h> 
int close(int fd);

②库函数方式:

#include<stdio.h>
FILE * fopen(const char * path,const char * mode);//FILE是文件流结构

path:文件路径名,包括文件名

mode:打开文件的方式。注意!这个形参是指针。编程时要使用字符串的形式。常用设置——b:以二进制格式打开或创建文件、t:打开文本文件、r:只读、w:只写、+:只要有加号出现就表示可读可写、a:append追加的意思,往文件末尾插入,这样就不会覆盖掉原来的内容。根据需要进行组合设置。

返回值:成功——指向FILE文件流的指针,失败——NULL。

与其对应的关闭函数:

#include<stdio.h> 
int fclose(FILE * stream);

该函数会让缓冲区内的数据写入文件中,并释放系统所提供的文件资源。成功执行返回0。

2.设置文件指针的位置:文件指针是读写文件操作时的起始位置,直接关系到读写的结果。进行读写操作时需要明确当前文件指针所处的位置。

①系统调用方式:

#include<unistd.h>
#include<sys/types.h>
off_t lseek(int fildes,off_t offset ,int whence);

whence:设置文件位置时的参考点——SEEK_SET 参数offset即为新的读写位置、SEEK_CUR 以目前的读写位置往后增加offset个位移量、SEEK_END 将读写位置指向文件尾后再增加offset个位移量

返回值:成功——设置成功之后的文件位置(距离文件起始位置),失败——(-1)

②库函数方式:

#include<stdio.h>
int fseek(FILE * stream,long offset,int whence);

参数和lseek()函数一模一样

返回值:成功——(0),失败——(-1)

3.写内容到文件:涉及内容——写的位置、内容、大小等,写完之后文件“指针”位置会跟着向后移动。

①系统调用方式:

#include<unistd.h>
ssize_t write (int fd,const void * buf,size_t count);

返回值:成功——写入的字节数,失败——(-1)

②库函数方式:

#include<stdio.h>
size_t fwrite(const void * ptr,size_t size,size_t nmemb,FILE * stream);

返回值:成功——写入的字节数

 4.读出文件内容:从文件指针处,读出count个字节数到buf指针指向的存储区

 ①系统调用方式:

#include<unistd.h>
ssize_t read(int fd,void * buf ,size_t count);

返回值:成功——读出的字节数,失败——(-1)

 ②库函数方式:

#include<stdio.h>
size_t fread(void * ptr,size_t size,size_t nmemb,FILE * stream);

返回值:成功——读出的字节数

二、编程演示

 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 #include <unistd.h>
 6 
 7 int main()
 8 {
 9     int fd,ret,rdlen;
10     char tmp[10];
11     fd = open("./myfile",O_RDWR|O_CREAT,S_IRUSR|S_IWUSR);
12     ret = chown(file_name,1000,1000);
13     if( ret<0 )
14         printf("chown() error.
");
15     lseek(fd,0,SEEK_SET);
16     rdlen = read(fd,tmp,10);
17     if(rdlen<0)
18     {
19         printf("read local file error.
");
20         return -1;
21     }
22     return 0;
23 }

三、问题记录

问1.为什么有系统调用方式来实现文件I/O操作还要库函数方式?

答1.系统调用需要进行用户空间和内核空间之间的切换,这种切换对CPU的开销大,如果大量使用系统调用会大大降低CPU的工作效率,为了解决这个问题于是有了库函数方式,他是一种带缓冲区的操作,当用户空间缓冲区满或者写操作结束时,才将用户缓冲区的内容写到内核缓冲区,同样的道理,当内核缓冲区满或写结束时才将内核缓冲区内容写到文件对应的硬件平台,如此一来就大大减少了系统调用的次数,大大提高CPU工作效率。

问2.write和fwrite函数把内容写到文件中是以哪种格式写入的?为什么用软件打开自己写的文件时会乱码?vim中如何以16进制格式显示打开的二进制文件?

答2.在linux下这两个函数都是以二进制的格式把内容写到文件里边的。像Uedit软件打开文件时他是会根据文件的后缀名来对文件进行转格式显示出来的,也就是说,大多情况下我们看到的内容和文件中实际的存储格式是不一样的。vim中如果要打开一个二进制文件要加“-b”选项,打开后在命令模式下输入“:%!xxd”即可让二进制文件内容以16进制格式来显示,16进制更容易让我们从中获取数据的信息。

问3.如何创建一批文件,要求文件名具有某种格式?

答3.使用sprint()函数对文件名进行格式化处理。

问4.如何修改文件的所有者?

答4.发现程序执行第一遍程序时可以成功执行,第二次运行出错,原来是文件权限问题,不知为毛默认创建的文件的所有者是“root”,为了下一次在用户模式下程序可以正常执行就得改变文件的所有者,使用chown()函数,在上边的那个demo里边就使用了这个函数。

原文地址:https://www.cnblogs.com/lubiao/p/4728723.html