一、Linux文件IO接口
1、Linux 文件 io 接口 - open/close
在 Linux 系统下, 用于对文件操作的库函数叫做文件 I/O
主要包括 open()/close()/read()/write() /lseek() 相应的系统调用 (准确说法是对系统调用的封装的库函数)文件描述符是一个非负整数, 当打开一个已存在文件或者创建一个新文件时, 内核向进程返回一个文件描述符
每个程序运行后, 操作系统会默认打开三个文件 标准输入 标准输出 标准错误输出, 文件描述符分别为 0 , 1 , 2
标准输入对应的设备一般为键盘
标准输出与标准错误输出设备一般为显示器
open函数
函数示例:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
头文件说明:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
参数说明:
pathname : 文件路径名
flags : 打开标志
- O_RDONLY: 只读方式打开文件
- O_WRONLY: 可写方式打开文件
- O_RDWR: 读写方式打开文件
- O_CREAT: 如果该文件不存在就创建一个新的文件,并用第三的参数为其设置权限
- O_EXCL: 如果使用 O_CRATE 时文件存在, open() 报错
- O_TRUNC: 如果文件已经存在,并且以读 / 写或只写成功打开, 并清零
- O_APPEND: 以添加的方式打开文件,在打开文件的同时,文件指针指向文件末尾
mode:指定创建新的文件的默认权限
返回值:
- 成功: 返回 文件描述符
- 失败 : 返回 -1, 并将错误编码保存到 errno
通过只读的方式打开一个文件
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<unistd.h>
#include<string.h>
int main(int argc,char *argv[] ){
int fd;
if(argc==2){
fd=open(argv[1],O_RDONLY);
if(fd==-1){
perror("Open:");
}
close(fd);//close函数关闭
}
return 0;
}
以只写的方式打开文件, 如果不存在则创建, 如果文件存在则截短
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<unistd.h>
#include<string.h>
int main(int argc,char *argv[] ){
int fd;
if(argc==2){
fd=open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,0644);
if(fd==-1){
perror("Open:");
}
close(fd);
}
return 0;
}
errno
是 Linux 操作系统中用于存储错误编码的全局变量, 错误编码在 Linux 系统中的定义如下:
函数头文件
#include <stdio.h>
函数原型
void perror(const char *s)
函数参数
-
s : 自定义字符串参数
-
错误信息转换主要使用strerror()函数, 具体说明如下:
函数头文件#include <string.h>
函数原型char *strerror(int errnum)
函数功能 :
将错误编码转换成字符串信息,并返回该字符串的地址
函数参数–errnum : 错误编码
函数返回值:返回错误码转换之后的字符串 or “Unknown error nnn”
使用 strerror 函数转换错误码
include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main(int argc,char *argv[]){
int fd;
fd = open(argv[1],O_RDONLY,0644);
if (fd == -1) {
fprintf(stderr,"open() : %s\n",strerror(errno));
return -1;
}
return 0;
}
2、Linux 文件 io 接口 - read/write/lseek
read函数
函数头文件:
#include <unistd.h>
函数原型:
ssize_t read(int fd, void * buf, size_t count);
函数参数
- fd : 文件描述符
- buf : 数据缓冲区
- count : 能够读取的最大字节数
函数返回值
- 成功 : 返回实际读取的字节数
- 失败 : -1, 并将错误编码设置到 errno 中
wirte函数
函数头文件
#include <unistd.h>
函数原型
ssize_t write(int fd, const void *buf, size_t count);
函数参数
-
fd : 文件描述符
-
buf : 缓冲区地址
-
count : 需要写入的字节数
函数返回值
- 成功: 返回实际成功写入的字节数
- 失败: 返回 -1, 并设置 errno
示例 : 通过 write 函数 使用标准输出来打印 Hello world
#include<stdio.h>
#include<unistd.h>
int main(int argc,char *argv[]){
write(1,"helloworld",10);//标准输出是1
return 0;
}
lseek函数
函数头文件
#include <sys/types.h>
#include <unistd.h>
函数原型
off_t lseek(int fd, off_t offset, int whence);
函数参数
- fd : 文件描述符
- offset : 偏移量, 可以为正数或者负数
- whence : 偏移相对位置
SEEK_CUR : 相对于文件当前偏移
SEEK_SET : 相对于文件开始位置
SEEK_END : 相对于文件尾偏移
函数返回值
成功: 返回 0
失败 : 返回 -1, 并设置 errno
示例:将一个字符串 “hello linux io” 写入到文件中,在读取出来
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(int argc,char *argv[]){
int fd;
char rd_buff[64]={0};
char wr_buff[14]={"hello linux io"};
fd=open(argv[1],O_RDWR|O_CREAT);
if(fd==-1){
perror("Open(): ");
return -1;
}
ssize_t wbytes=write(fd,wr_buff,14);
if(wbytes==-1){
perror("Write():");
return -1;
}
lseek(fd,0,SEEK_SET);
ssize_t rdbytes=read(fd,rd_buff,wbytes);
if(rdbytes==-1){
perror("Read():");
return -1;
}
printf("%s\n",rd_buff);
close(fd);
return 0;
}
二、Linux标准IO接口
简介:标准 IO 是另外一套 IO 接口,具有如下特点:
- 标准 I/O 是属于跨平台, 可以在 Linux、windows、mac os 上运行, 文件 IO 只能在Linux 平台运行
- 标准 I/O 自带缓冲区,有更高的 IO 效率
- 标准 IO 提供丰富的操作文本信息接口
- 标准 IO 底层需要依赖于 文件 IO
- 在 Linux 系统下, 标准 I/O 是属于 glibc 库的一部分
函数原型
int fprintf(FILE *stream, const char *format, …);
函数功能:将格式化数据写入到指定文件中
函数参数:
stream : 流对象指针
format : 格式字符串
示例 : 通过 stdout 与 stderr 进行输出
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<unistd.h>
#include<string.h>
int main(int argc,char *argv[] ){
fprintf(stdout,"hello world\n");
fprintf(stderr,"can't open file");
while(1){}//死循环
return 0;
}
注意:
在上述程序中, 将 ‘\n’ 去掉之后, 在添加一个死循环后, 则程序运行的结果则不同, 这里是与标准 I/O 的缓冲区有关系
标准 I/O 的缓存大小为 8192, 在系统中定义如下 (stdio.h):
一般标准 I/O 的分类为:
全缓存: 当相应的缓冲区已经装满数据时, 才进行一次 I/O 操作
行缓存: 当相应的缓冲区存储一行时,则进行一次 I/O 操作, stdout 就是行缓存
不缓存: 直接进行 I/O 操作, 不进行缓存, stderr 就是不缓存
一般情况下, 程序在结束时会自动刷新缓冲区,但是当程序还未结束时, 刷新缓冲区则需要调用fflush()函数
#include <stdio.h>
int main(void){
printf("hello.");
fflush(stdout);
while(1){}
return 0;
}
1、Linux 标准 io - fopen/fclose
fopen函数
函数头文件:
#include <stdio.h>
函数原型:
FILE *fopen(const char *pathname, const char *mode);
函数功能:打开文件,并获取流对象指针
函数参数:
pathname : 路径名
mode : 打开模式(b:二进制)
- r 或 rb : 打开只读文件,该文件必须存在
- r + 或 r+b : 打开可读写的文件,该文件必须存在
- w 或 wb : 打开只写文件,若文件存在则文件长度清为 0, 即会擦些文件以前内容。若文件不存在则建立该文件.
- w + 或 w+b 或 wb+ : 打开可读写文件,若文件存在则文件长度清为零,即会擦些文件以前内容. 若文件不存在则建立该文件.
- a 或 ab 以附加的方式打开只写文件。若文件不存在,则会建立该文件, 如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留
函数返回值:
成功 : 返回文件指针
失败 : 返回 NULL, 并设置 errno
函数头文件:
#include <stdio.h>
fclose函数
函数原型:
int fclose(FILE *stream);
函数功能:关闭已经打开的文件
函数参数:stream : 文件指针
2、Linux 标准 io - fgetc/fputc
fgetc函数
函数头文件:
#include <stdio.h>
函数原型:
int fgetc(FILE *stream)
函数返回值:
读取成功,返回读取字符的ASCII码值;
读取失败,返回EOF
fpuc函数
函数头文件:
#include <stdio.h>
函数返回值:成功 : 返回 实际读取的字符
失败 : 返回 EOF, 并设置 errno
函数原型:
int fputc(int c, FILE *stream);
函数参数:
c : 待写入的字符
stream : 文件指针
成功 : 返回 实际写入的字符
失败 : 返回 EOF, 并设置 errno
示例 : 实现 cat 命令功能, 将文件中的数据显示到 stdout 上
#include<stdio.h>
int main(int argc,char *argv[] ){
FILE *fd;
char ch;
fd=fopen(argv[1],"r");
while(ch!=EOF){
ch=fgetc(fd);
fputc(ch,stdout);
}
fclose(fd);
return 0;
}
3、Linux 标准io - fgets/fputs
fgets函数
函数头文件:
#include <stdio.h>
函数功能: 从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内
函数原型:
char *fgets(char *s, int size, FILE *stream);
函数参数:
s : 缓冲区地址
size : 最大可读取大小
stream : 文件指针
函数返回值:
- 成功 : 返回缓冲区的地址,当读到文件尾时 会返回 NULL
- 失败 : 返回 NULL(不是EOF)
fputs函数
函数头文件:
#include <stdio.h>
函数原型:
int fputs(const char *s, FILE *stream);
函数返回值:
成功:返回一个非负数
失败:返回EOF
示例:使用fgets与fputs输出文件内容到stdout上
#include<stdio.h>
int main(int argc,char *argv[] ){
FILE *fd=NULL;
char buff[64];
fd=fopen(argv[1],"r");
//注意fgets的参数顺序
while(fgets(buff,sizeof(buff),fd)){
fputs(buff,stdout);
}
fclose(fd);
return 0;
}
4、Linux 标准 io - 格式化输入输出 与时间获取
格式化输入输出
函数原型
int printf(const char *format, …);
函数原型
int fprintf(FILE *stream, const char *format, …);
函数参数
stream : 流对象指针
format : 格式字符串
函数原型
int sprintf(char *str, const char *format, …);//函数功能
将格式化数据输出到字符串缓冲区中
函数参数
str : 字符串缓冲区地址
format : 格式字符串地址
函数原型
int scanf(const char *format, …);
函数原型
int fscanf(FILE *stream, const char *format, …);//从文件中读取格式化的数据
int sscanf(const char *str, const char *format, …);//从字符串读取格式化数据
函数参数
str : 字符串地址
format : 格式字符串地址
示例:格式化写入文件
#include <stdio.h>
int main(int argc,char *argv[]){
char buffer[64];
int numa=10,numb=20,numc=30;
FILE *fd=fopen(argv[1],"r+");
if(fd==NULL){
fprintf(stderr,"Open():");
}
fprintf(fd,"%d-%d-%d",numa,numb,numc);
sprintf(buffer,"%d-%d-%d",numa,numb,numc);
puts(buffer);
fclose(fd);
return 0;
}
格式化读取文件
#include <stdio.h>
int main(int argc,char *argv[]){
char buffer[64];
int numa=0,numb=0,numc=0;
FILE *fd=fopen(argv[1],"r");
if(fd==NULL){
fprintf(stderr,"Open():");
}
fscanf(fd,"%d-%d-%d",&numa,&numb,&numc);//从文件中读取该格式的内容赋值给numa,numb,numc
sprintf(buffer,"%d-%d-%d",numa,numb,numc);
puts(buffer);
fclose(fd);
return 0;
}
时间获取
在 Linux 中获取主要需要以下两个步骤
Step 1 : 通过 time() 函数获取从 1970 年至今的秒数
Step 2 : 通过 localtime() 或者 ctime() 函数转换
函数头文件
#include <time.h>
函数原型:
time_t time(time_t *tloc);
struct tm *localtime(const time_t *timep);//将时间戳转换成本地时间,并存储到 struct tm 结构体变量中
struct tm 结构体:
struct tm {
int tm_sec; /* Seconds (0-60) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) */
int tm_year; /* Year - 1900 */
int tm_wday; /* Day of the week (0-6, Sunday = 0) */
int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */
int tm_isdst; /* Daylight saving time */
}
示例:获取当前时间并打印
#include<stdio.h>
#include<time.h>
int main(){
time_t t;
struct tm *p_datatime;
t=time(NULL);
p_datatime=localtime(&t);
printf("%d-%d-%d::%d::%d::%d\n",p_datatime->tm_year+1900,p_datatime->tm_mon+1,p_datatime->tm_mday,p_datatime->tm_hour,p_datatime->tm_min,p_datatime->tm_sec);
return 0;
}
5、二进制读写与文件定位
在标准 I/O 中, 用于进行二进制文件进行读写时需要调用fread与fwrite
fread函数
函数头文件
#include <stdio.h>
函数功能:从二进制文件中读取数据到缓冲区
函数原型:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
函数参数:
- ptr : 缓冲区地址
- size : 读取每个数据块的大小
- nmemb : 读取数据对象的个数
- stream : 文件指针
函数返回值:
成功 : 返回实际读取的数据对象的个数
失败: 当到达文件尾或者发生错误,返回较小的数据对象个数或者 0
fwrite函数
函数头文件
#include <stdio.h>
函数功能:将缓冲区中的数据写入到文件中
函数原型:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
函数参数
- ptr : 缓冲区地址
- size : 写入每个数据块的大小
- nmemb : 写入数据对象的个数
- stream : 文件指针
函数返回值
- 成功 : 返回实际写入的数据对象的个数
- 失败: 当到达文件尾或者发生错误,返回较小的数据对象个数或者 0
fseek函数
函数头文件
#include <stdio.h>
函数功能:对文件进行定位
函数原型
int fseek(FILE *stream, long offset, int whence);
函数参数
- stream : 文件指针
- offset : 偏移量
- whence: 偏移相对位置
SEEK_SET :相对于文件头
SEEK_CUR : 相对于文件当前位置
SEEK_END : 相对于文件尾
函数返回值
成功: 返回设置后的偏移位置
失败:返回 -1, 并设置 errno
示例 : 使用 fread 与 fwrite 存储一个浮点数组的数据到文件中
#include <stdio.h>
int main(int argc,char *argv[]){
float numbers[5]={1.1,1.2,1.3,1.4,1.5};
size_t write_size,read_size;
FILE *fd=fopen(argv[1],"w+");
if(fd==NULL){
fprintf(stderr,"Open():");
}
write_size=fwrite(numbers,sizeof(float),5,fd);
if(write_size!=5){
fprintf(stderr,"Write():");
}
float numbers2[5]={0.0};
fseek(fd,0,SEEK_SET);//修改位置偏移量
read_size=fread(numbers2,sizeof(float),5,fd);
if(read_size!=5){
fprintf(stderr,"Read():");
}
for(int i=0;i<5;i++){
printf("%f ",numbers2[i]);
}
fclose(fd);
return 0;
}
示例:使用二进制读取文件复制图片
用fseek来测量这个图片一共有多少个字节:
1、把光标设置到文件的尾部。
2、接着使用ftell()函数来获取长度length。
3、把光标设置回文件的开头。
ftell()函数的作用是 获取文件的 当前指针位置 相对于 文件首地址 的 偏移字节数 ;
函数原型
#include <stdio.h>
long ftell(FILE *stream);
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define BYTE unsigned char
int main(){
BYTE *buffer;
FILE *rfd;
FILE *wfd;
rfd=fopen("./Vue.jpg","rb");
wfd=fopen("./temp.jpg","wb");
fseek(rfd,0,SEEK_END);//移动光标到结尾
int len=ftell(rfd);//计算文件长度
buffer=(BYTE *) malloc(sizeof(BYTE)*len);
fseek(rfd,0,SEEK_SET);
fread(buffer,sizeof(BYTE),len,rfd);
fwrite(buffer,sizeof(BYTE),len,wfd);
fclose(rfd);
fclose(wfd);
return 0;
}
也可以不使用ftell()函数测量文件长度
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define BYTE unsigned char
int main(){
BYTE buffer[64]={0};
FILE *rfd;
FILE *wfd;
rfd=fopen("./Vue.jpg","rb");
wfd=fopen("./temp.jpg","wb");
while(fread(buffer,sizeof(BYTE),sizeof(buffer),rfd)){
fwrite(buffer,sizeof(BYTE),sizeof(buffer),wfd);
}
fclose(rfd);
fclose(wfd);
return 0;
}