文件IO与标准库IO
一、文件(系统)IO与标准库IO
1、无缓存IO系统调用
函数:open close write read 等系统调用
操作数据流向路径:数据——内核缓存区——磁盘
2、标准IO库函数调用
函数:fopen fclose fwrite fread fgets fputs fgetc fputc 等标准库函数调用函数
操作数据流向路径:数据——流缓存区——内核缓存区——磁盘
文件IO是低级IO不带有缓存、而标准io带有缓存在open时会在用户层开启一个缓存区域提高效率,防止频繁调用系统io
标准io —> 系统io (标准io是在系统io之上)
二、标准IO 相关的API
头文件:#include<stdio.h>
1.fopen / fcolse
FILE * fopen(const char * path,const char * mode)
返回值:文件顺利打开后,指向该流的文件指针就会被返回。若果文件打开失败则返回NULL,并把错误代码存在errno 中。
参数:
path:字符串包含欲打开的文件路径及文件名。
mode:有下列几种流形态字符串:
r 打开只读文件,该文件必须存在。
r+ 打开可读写的文件,该文件必须存在。
w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。
a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。
上述的形态字符串都可以再加一个b字符,如rb、w+b或ab+等组合,加入b 字符用来告诉函数库打开的文件为二进制文件,而非纯文字文件。
int fclose(FILE * stream);
此动作会让缓冲区内的数据写入文件中,并释放系统所提供的文件资源。
返回值:若关文件动作成功则返回0,有错误发生时则返回EOF并把错误代码存到errno
2.fread/fwrite/fseek/ftell
size_t fread(void * ptr,size_t size,size_t nmemb,FILE * stream);
ptr: 存放buffer
size: 一个单元大小
nmemb:单元个数
stream:已打开的文件指针
理论接受的字节大小 size*nmemb
返回值:
返回实际读取的单元个数。如果小于count,则可能文件结束或读取出错;
可以用ferror()检测是否读取出错,用feof()函数检测是否到达文件结尾。如果size或nmemb为0,则返回0。
size_t fwrite(const void * ptr,size_t size,size_t nmemb,FILE * stream);
ptr: 传输送buffer
size: 一个单元大小
nmemb:单元个数
stream:已打开的文件指针
理论写的字节大小 size*nmemb
返回值:
返回实际写入的nmemb 数目,如果小于nmemb ,则说明发生了错误,文件流错误标志位将被设置,随后可以通过ferror()函数判断。
int fseek(FILE * stream,long offset,int whence);
stream:已打开的文件指针
offset:根据参数whence来移动读写位置的位移数
whence:
SEEK_SET从距文件开头offset位移量为新的读写位置。
SEEK_CUR 以目前的读写位置往后增加offset(可负)个位移量。
SEEK_END将读写位置指向文件尾后再增加offset(可负)个位移量。
返回值:
成功:0 错误: -1
long ftell(FILE * stream);
取得文件流目前的读写位置
返回值:当调用成功时则返回目前的读写位置,若有错误则返回-1
注意:读写指针是同一个 按情况使用fseek进行指针的偏移
3.fgetc/fgets fputc/fputs
int fgetc(FILE * stream);
函数说明:fgetc()从参数stream所指的文件中读取一个字符。若读到文件尾而无数据时便返回EOF。
char * fgets(char * s,int size,FILE * stream);
从参数stream所指的文件内读入字符并存到参数s所指的内存空间,直到出现换行字符、
读到文件尾或是已读了size-1个字符为止,最后会加上NULL作为字符串结束 包括最后的空字符
size:通常是使用以 s传递的数组长度
返回为一个指针
int fputc(int c,FILE * stream);
会将参数c 转为unsigned char 后写入参数stream 指定的文件中
会返回写入成功的字符,即参数c
int fputs(const char * s,FILE * stream);
将参数s所指的字符串写入到参数stream所指的文件内
若成功则返回写出的字符个数,返回EOF则表示有错误发生。
三、文件IO相关API
头文件:
#include <sys/types.h>
#include <sys/stat.h>
#include<unistd.h>
#include <fcntl.h>
1.open/close
open:
int open( const char * pathname, int flags);
int open( const char * pathname,int flags, mode_t mode);
成功:返回文件描述符 失败: -1
参数flags:
flag | 功能 |
---|---|
O_WRONLY | 以只写方式打开文件 |
O_RDWR | 以可读写方式打开文件 |
O_RDONLY | 以只读方式打开文件 |
O_CREAT | 若欲打开的文件不存在则自动建立该文件 |
O_EXCL | 如果O_CREAT 也被设置,此指令会去检查文件是否存在。文件若不存在则建立该文件,否则将导致打开文件错误。此外,若O_CREAT与O_EXCL同时设置,并且欲打开的文件为符号连接,则会打开文件失败 |
O_NOCTTY | 如果欲打开的文件为终端机设备时,则不会将该终端机当成进程控制终端机 |
O_TRUNC | 若文件存在并且以可写的方式打开时,此旗标会令文件长度清为0,而原来存于该文件的资料也会消失 |
O_APPEND | 当读写文件时会从文件尾开始移动,也就是所写入的数据会以附加的方式加入到文件后面 |
O_NONBLOCK | 以不可阻断的方式打开文件,也就是无论有无数据读取或等待,都会立即返回进程之中 |
O_NDELAY | 同O_NONBLOCK |
O_SYNC | 以同步的方式打开文件 |
O_NOFOLLOW | 如果参数pathname 所指的文件为一符号连接,则会令打开文件失败 |
O_DIRECTORY | 如果参数pathname 所指的文件并非为一目录,则会令打开文件失败 |
参数mode:
只有在建立新文件时才会生效,此外真正建文件时的权限会受到umask值所影响,因此该文件权限应该为(mode-umaks)。
mode | 说明 |
---|---|
S_IRWXU ,00700 权限 | 代表该文件所有者具有可读、可写及可执行的权限。 |
S_IRUSR 或S_IREAD,00400权限, | 代表该文件所有者具有可读取的权限。 |
S_IWUSR 或S_IWRITE,00200 权限, | 代表该文件所有者具有可写入的权限。 |
S_IXUSR 或S_IEXEC,00100 权限, | 代表该文件所有者具有可执行的权限。 |
S_IRWXG 00070权限, | 代表该文件用户组具有可读、可写及可执行的权限。 |
S_IRGRP 00040 权限, | 代表该文件用户组具有可写入的权限。 |
S_IXGRP 00010 权限, | 代表该文件用户组具有可执行的权限。 |
S_IRWXO 00007权限, | 代表其他用户具有可读、可写及可执行的权限。 |
S_IROTH 00004 权限, | 代表其他用户具有可读的权限 |
S_IWOTH 00002权限, | 代表其他用户具有可写入的权限。 |
S_IXOTH 00001 权限, | 代表其他用户具有可执行的权限。 |
错误代码:
代码 | 说明 |
---|---|
EEXIST | 参数pathname 所指的文件已存在,却使用了O_CREAT和O_EXCL旗标。 |
EACCESS | 参数pathname所指的文件不符合所要求测试的权限。 |
EROFS | 欲测试写入权限的文件存在于只读文件系统内。 |
EFAULT | 参数pathname指针超出可存取内存空间。 |
EINVAL | 参数mode 不正确。 |
ENAMETOOLONG | 参数pathname太长。 |
ENOTDIR | 参数pathname不是目录。 |
ENOMEM | 核心内存不足。 |
ELOOP | 参数pathname有过多符号连接问题。 |
EIO I/O | 存取错误。 |
close:
int close(int fd);
0:成功 -1:失败
EBADF 参数fd 非有效的文件描述词或该文件已关闭
2.read/write
size_t : unsigned int
ssize : int
read:
ssize_t read(int fd,void * buf ,size_t count);
返回实际读到大小 返回0 :表示已到达文件尾或是无可读取的数据,此外文件读写位置会随读取到的字节移动
返回-1: 错误代码存于errno
EINTR 此调用被信号所中断。
EAGAIN 当使用不可阻断I/O 时(O_NONBLOCK),若无数据可读取则返回此值。
EBADF 参数fd 非有效的文件描述词,或该文件已关闭。
write:
ssize_t write (int fd,const void * buf,size_t count);
返回实际写入大小
错误返回-1:
EINTR 此调用被信号所中断。
EAGAIN 当使用不可阻断I/O 时(O_NONBLOCK),若无数据可读取则返回此值。
EADF 参数fd非有效的文件描述词,或该文件已关闭。
3.lseek
off_t:long类型,其默认为一个32位的整数 64位: long long int
off_t lseek(int fildes,off_t offset ,int whence);
成功时则返回目前的从开头开始的读写位置
whence:
SEEK_SET 参数offset即为新的读写位置。
SEEK_CUR 以目前的读写位置往后增加offset个位移量。
SEEK_END 将读写位置指向文件尾后再增加offset个位移量。
当whence 值为SEEK_CUR 或SEEK_END时,参数offet允许负值的出现
4.ioctl/fcntl
ioctl:
int ioctl(int fd, unsigned long request, ...);
fcntl:
int fcntl(int fd , int cmd);
int fcntl(int fd,int cmd,long arg);
int fcntl(int fd,int cmd,struct flock * lock);
cmd:
F_GETFL 取得文件描述词状态旗标,此旗标为open()的参数flags。
F_SETFL 设置文件描述词状态旗标,参数arg为新旗标,但只允许O_APPEND、O_NONBLOCK和O_ASYNC位的改变,其他位的改变将不受影响。
F_SETFD 设置close-on-exec 旗标。该旗标以参数arg 的FD_CLOEXEC位决定。
F_GETLK 取得文件锁定的状态。
F_SETLK 设置文件锁定的状态。
四、进阶文件I/O
1.独占方式创建文件
open()时标志位 指定 O_EXCL|O_CREAT 如果打开已存在返回错误,保证进程是打开文件的创建者
2.向文件尾部追加数据 与非阻塞IO
open()时标志位 指定 O_APPEND
open()时标志位 指定 O_NONBLOCK
3.复制文件描述符
int dup (int oldfd); 返回新文件描述符
int dup2(int odlfd,int newfd);
4.在文件特定偏移量处的I/O:pread/pwrite
lseek跟read和write结合 如果分开会出现在当lseek和read之间被其他线程切走,而在此修改偏移就会有误,存在竞争 结合后保证原子性
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
返回写或读的字节数,-1为失败
5.分散输入和集中输出:readv/writev
函数用于在一次函数调用中读、写多个非连续缓冲区
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
从fd读取一片连续的字节,然后将其散置于iov指定缓冲区,从iov[0]开始依次填满
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
将iov指定所以缓冲区的数据集中起来,然后以连续的字节序列写入fd指向的文件中
ssize_t preadv(int fd, const struct iovec *iov, int iovcnt,off_t offset);
ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt,off_t offset);
readv/writev与lseek结合
iov:指向iovec 结构体
struct iovec {
void *iov_base; /* Starting address */
size_t iov_len; /* Number of bytes to transfer */
};
iovcnt:指定iov成员个数
6.截断文件 truncate/ftruncate 与创建临时文件mkstemp/tmpfile
截断文件:
int truncate(const char *path, off_t length);//可直接使用,不用open得文件描述符
int ftruncate(int fd, off_t length); //在调用open之后使用
返回0成功 -1失败
创建临时文件:
int mkstemp(char *template);
template:最后6个字符必须为XXXXXX 如:char template[] = "/tmp/somestringXXXXXX"
删除临时文件: int unlink(const char * pathname);
FILE* tmpfile(void);
内部调用unlink
7.大文件I/O
a、通常将文件偏移量的数据类型off_t为一个有符号的长整型(-1来表示错误情况),在32位系统下,这将文件大小置于2^31 -1字节(2GB)的限制下。在64位系统长整型为64位。
如要访问大文件超过2GB,应用程序有两种方式:
1、备选API
2、将宏**_FILE_OFFSET_BITS 的值定义为64位。 #define _FILE_OFFSET_BITS 64 要求文件偏移量的变量应正确地使用off_t**,不能使用原生的C语言整数。
b、要打印off_t值 :printf(“offset = %lld \n”,(long long )offset);
限定符%lld和进行强制转换为long long
long int = int = 4字节
long long = 8字节