Linux文件操作与IO

底层文件访问


open系统调用

#include <fcntl.h> #include <sys/types.h> 
#include <sys/stat.h>  int open(const char *path, int oflags);
int open(const char *path, int oflags, mode_t mode);

在遵循POSIX规范的系统上,使用open系统调用并不需要包含头文件sys/types.h和sys/stat.h,但在某些UNIX系统上,他们可能必不可少。

open系统调用建立了一条从到文件或设备的访问路径,该调用将得到与该文件相关联的文件描述符(file discriptor)
任何一个进程可以同时打开的文件数目有限,通常由limits.h头文件中的常量OPEN_MAX定义,该值与系统有关,且这个限制本身还受到系统全局性限制影响,所以一个程序未必总是可以打开这么多文件。在Linux系统中,这个限制可以随着系统运行而调整,所以OPEN_MAX并不是一个常量。它通常一开始就被设置未256

参数 解释
path 准备打开的文件或者设备的名字
oflags 打开文件所采取的动作
mode 文件访问模式

oflag参数包括下列值的组合(用“按位与”操作)

解释
O_APPEND 把写入数据追加到文件末尾
O_TRUNC 把文件长度设置为0,丢弃已有内容
O_CREAT 如需要就按参数mode给出的访问模式创建文件
O_EXCL 与O_CREAT一起使用,确保调用者创建出文件。open调用是一个原子操作,它只执行一个函数调用。使用这个可选模式可以防止两个程序同时创建一同一个文件。如果文件已经存在,open调用失败

当你使用带有O_CREAT标志的open调用时,你必须使用带3个有参数的open调用。第3个参数mode是几个标志位按位或后得到的,这些标志在头文件sys/stat.h中定义

权限 拥有者
S_IRUSR r user
S_IWUSR w user
S_IXUSR x user
S_IRGRP r group
S_IWGRP w group
S_IXGRP x group
S_IROTH r other
S_IWOTH w other
S_IXOTH x other

用户掩码(由umask命令设定)会影响被创建文件的访问权限open调用中参数mode的值与当前用户掩码的反值做与操作


write系统调用

#include <stdio.h> 
size_t write(int fd, const void *buf, size_t nbytes);

write系统调用把缓冲区buf中的前n个bytes写入与文件描述符fd相关的文件中。

参数 解释
fd 数据目的地的文件描述符
buf 数据来源地的指针
nbytes 写入数据字节数

返回实际写入的字节数,返回值可能小于nbytes。如果返回0,表示未写入数据;如果返回-1,表示write调用出错,错误代码保存在全局变量errno中。


read系统调用

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

read系统调用从与文件描述符fd相关联的文件中读入nbytes字节的数据,并把它们放到buf中。

参数 解释
fd 数据来源文件的文件描述符
buf 数据目的地指针
nbytes 读入数据字节数

返回实际读入的字节数,可能会小于nbytes。如果返回0,表示未读入任何数据,已到达文件尾;返回-1表示出现错误。


close系统调用

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

close调用终止文件描述符fd与其对应文件之间的关联。文件描述符被释放并能够重新使用。close调用成功时候返回0,出错时返回-1。
检查close调用的返回结果非常重要。有的文件系统,特别使网络文件系统,可能不会在关闭文件之前报告文件写操作中出现的错误,这是因为在执行写操作时,数据可能未被确认写入


lseek系统调用

#include <unistd.h> 
#include <sys/types.h> 

off_t lseek(int fd, off_t offset, int whence);

lseek系统调用对文件描述符的读写指针位置进行设置> 参数whence定义该偏移量offset的用法,可取下列值
| 参数 | 解释 |
| ------------- |:-------------:|
|SEEK_SET|绝对位置|
|SEEK_CUR|相对于当前位置|
|SEEK_END|相对于文件尾|


fstat stat lstat系统调用

#include <unistd.h> 
#include <sys/types.h>
#include <sys/stat.h> 
int fstat(int fd, struct stat *buf);
int stat(const char *path, struct stat *buf); 
int lstat(const char *paht, struct stat *buf);

fstat系列调用返回与打开的文件描述符相关联的文件的状态信息,该信息将被写入buf中
stat和lstat返回的使通过文件名查询到的状态信息。它们产生相同效果,但当文件是符号链接时,lstat返回的是该符号链接本身的信息,而stat返回的使该链接指向文件的信息。


标准IO库

在标准IO库中,与底层文件描述符对应的流(stream),它被实现为指向结构体FILE的指针(FILE )> 在启动程序时,有三个文件流被自动打开的。它们是stdin stdout stderr他们与底层文件描述符0 1 2相对应,分别代表标准输入 标准输出 标准错误输出IO函数可能存在缓冲区安全问题,应该避免使用这样的函数,或者十分谨慎地使用有安全问题的函数*

fopen函数

#include <stdio.h> 
FILE *fopen(const char *filename, const char *mode);

fopen打开由参数filename指定的文件,并把它与一个文件流关联起来。> > 参数mode指定文件的打开方式,取下列字符串中的值。

参数 解释
"rb" or "rb" 只读
"w" or "wb" 写方式,并把文件长度截短为0 "a" or "ab" 写方式,新内容追加到文件尾
"r+" or "rb+" 读写
"w+" or "wb+" 读写,文件长度截短为0
"a+" or "ab+" 读写,新内容追加在文件尾

字母b表示文件是一个二进制(binary)文件fopen调用成功将会返回一个非空的FILE指针,失败时返回NULLunix和Linux把所有文件都看成二进制文件,参数mode必须是字符串,所以总是应该使用双引号,而不是单引号*
可用的文件流数量也是有限的。实际限制由头文件stdio.h中的FOPEN_MAX定义,它的值至少为8,在linux系统中通常是16。

fread函数

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

数据从文件流stream读到ptr指向的数据缓冲区中。fread和fwrite都是对数据记录进行操作,size参数指定每个数据记录的长度,计数器ntimes给出要传输的记录个数。
返回值是成功读到数据缓冲区里的记录个数(不是字节数)。当到达文件尾,它的返回值可能会小于ntimes,甚至可以是0。

fwrite函数

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

fwrite与fread有相似的接口。它从指定的数据缓冲区取出数据记录,并把它们写到文件流stream中。
返回值是成功写入的记录个数

fclose函数

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

fclose函数关闭指定的文件流stream,使所有尚未写出的数据写出。如果要确保数据已经全部写出,就应该调用fclose函数。当程序正常结束时,会自动对所有还打开的文件流调用fclose函数。

fflush函数

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

将文件流里所有尚未写出的数据立刻写出。有时在调试程序时,可以用它来确认程序正在写数据而不是被挂起了。调用fclose函数隐含执行了一次flush操作


fseek函数

fseek函数与lseek函数系统调用对应的文件流函数。它在文件流里为下次读写操作指定位置。offset和whence参数的含义和取值与前面的lseek系统调用完全一样。但是fseek返回一个证书:0表示成功,-1表示失败并设置errno支出错误。


fgetc getc getchar函数

 #include <stdio.h> 
 int fgetc(FILE *stream); 
 int getc(FILE *stream); int getchar();

fgetc函数从文件流stream取出下一个字节并把它作为一个字符返回。当它到达文件尾或者出现错误时,它返回EOF(end of file)。可以通过ferror或者feof来区分> getc的作用和fgetc相同,但是它有可能被实现为一个宏(macro),如果这样,stream参数就可能被计算不止一次,所以它不能有副作用。此外,不能保证能够使用getc的地址作为函数指针

getchar函数相当于getc(stdin),它从标准输入里读取下一个字符。


fputc putc putchar函数

 #include<stdio.h> 
 int fputc(int c, FILE *stream);
 int putc(int c, FILE *stream); 
 int putchar(int c);

fputc函数把字符c写到文件流stream中。它返回写入的值,如果失败则返回EOF.> putc函数作用相当于fputc,但它可能被实现为一个宏。
putchar函数相当于putc(c, stdout),它把单个字符写到标准输出。注意,putchar和getchard都是把字符当做int类型而不是插入类型。这就允许文件尾(EOF)取值-1,这是一个超出字符数字编码范围的值。


fgets gets函数

#include <stdio.h> 
char *fgets(char *s, int n, FILE *stream);  
char *gets(char *s);

fgets函数把读到的字符写到s指向的字符串里,知道出现下列情况:

  1. 遇到换行符
  2. 已经传输了n-1个字符
  3. 到达文件尾
    它会把遇到的换行符也传递到接收字符串,再加上一个表示结尾的空字符\0。
    当调用成功时,fgets返回一个指向字符串s的指针。如果文件流已经到达文件尾,fgets会设置这个文件流的EOF标志并返回一个空指针。如果出现读错误,fgets返回一个空指针并设置errno。

gets函数类似于fgets函数,但gets函数存在缓冲区溢出问题,不推荐使用


格式化输入和输出


printf fprintf sprintf snprintf函数

#include <stdio.h> 
int printf(const char *format, ...); 
int sprintf(char *s, const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int snprintf(char *s, size_t size, const char *format, ...);

snprintf修复了缓冲区问题,推荐使用

snprintf将可变个参数(...)按照format格式化成字符串,然后将其复制到str中(1) 如果格式化后的字符串长度 < size,则将此字符串全部复制到str中,并给其后添加一个字符串结束符('\0');
(2) 如果格式化后的字符串长度 >= size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符('\0')返回值为欲写入的字符串长度。

常见格式控制符:

控制符 解释
%d, %i 十进制格式输出整数
%o, %x 八进制或十六进制格式输出一个整数
%c 输出一个字符
%s 输出字符串
%f 单精度浮点数
%e 科学计数法格式输出双精度浮点数
%g 以通用格式输出一个双精度浮点数

-------#### scanf fscanf sscanf函数
#include <stdio.h> int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *s, const char *format, ...);

scanf系列函数实现有漏洞,使用不够灵活,不推荐使用


错误处理

本文中的许多函数和系统调用都可能失败。它们会在失败时候设置外部变量errno来指明失败原因
#include <errno.h>
extern int errno;

你必须在函数调用失败之后立刻对其进行检查,你总是应该在使用它之前将它复制到另一个变量,因为它的值可能被下一个函数调用覆盖,即使下一个函数并没有出错,也可能会覆盖这个变量

错误代码 解释
EPERM 操作不允许
ENOENT 文件或目录不存在
EINTR 系统调用被中断
EIO IO错误
EBUSY 设备或资源忙碌
EEXIST 文件存在
EINVAL 无效参数
EMFILE 打开文件过多
ENODEV 设备不存在
EISDIR 是一个目录
ENOTDIR 不是一个目录

以上错误代码均保存在头文件errno.h中


#include<stdio.h> int ferror(FILE *stream);
int feof(FILE *stream); 
void clearerr(FILE *stream);

ferror函数测试文件流stream的错误标识,如果该标识被设置就返回一个非0值,否则返回0
feof函数测试一个文件流的文件尾标识,如果该标识被设置就返回非0值,否则返回0。
clearerror函数的作用是清除由stream指向的文件流的文件尾标识和错误标识。它无返回值,也未定义任何错误。


 #include <string.h> char *strerror(int errnum);

strerror函数把错误代码映射成一个字符串,该字符串对错误代码进行解释。
#include <stdio.h> void perror(const char *s);

perror函数把errno变量中的当前错误映射成一个字符串,并把它输出到标准错误输出流(stderr)。该字符串的前面加上字符串s(如果不为空),再加上一个冒号和空格。


void *指针类型

void即“无类型”,void *则为“无类型指针”,可以指向任何数据类型。反之则不然,例如:
void *p;
int *a;
p = a; //合法
a = p; //不合法
a = (int *) p; //合法

如果函数的参数可以是任意类型指针,那么应声明其参数为void*。例如内存操作函数:

void * memcpy(void *dest,const void *src,size_t len);

从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。返回指向dest

void * memset(void *buffer,int c,size_t num);

将buffer中前n个字节用c替换并返回buffer 。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 207,113评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,644评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,340评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,449评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,445评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,166评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,442评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,105评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,601评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,066评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,161评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,792评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,351评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,352评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,584评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,618评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,916评论 2 344

推荐阅读更多精彩内容