标准IO
接上节
函数接口
(1)fseek函数:
1.功能:将文件流中的文件指针从指定的起始位置开始偏移指定的字节数。
2.参数:(目标文件,偏移量,参考点)
stream
:要移动文件指针的目标文件流对象,注意不支持设备文件,一般用于普通文件。offset
:要在文件内偏移的距离,单位为字节。如果值为正数,则向文件末尾偏移;如果值为负数,则向文件开头偏移。whence
:偏移的起始位置,由系统定义的三个宏确定,分别是SEEK_SET
(文件的开头位置)、SEEK_CUR
(文件的当前位置)、SEEK_END
(文件的末尾位置)。
3.返回值:成功返回0,失败返回 - 1。
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
FILE* fp = fopen("1.txt","w+");
if(NULL == fp)
{
printf("fopen error\n");
return 1;
}
char buf[128]="hello world";
fputs(buf,fp);
fseek(fp,0,SEEK_SET);
bzero(buf,sizeof(buf));
fgets(buf,sizeof(buf),fp);
printf("buf is %s\n",buf);
fclose(fp);
return 0;
}
(2)ftell函数:
1.功能:获取当前文件流指针的具体位置,一般以文件开头到当前指针的字节数为返回值。
2.参数:stream
为要返回指针距离的文件流对象。
3.返回值:成功获取到的距离长度,单位是字节;失败返回 - 1。
(3)rewind函数:
1.功能:将文件指针重新定位到文件的开头,等效于fseek(stream, 0L, SEEK_SET)
。
2.参数:stream
为要进行操作的文件流对象。
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE* fp = fopen("1.txt","r");
if(NULL == fp)
{
printf("fopen error");
return 1;
}
char buf[1024]={0};
fgets(buf,sizeof(buf),fp);
printf("1 buf is %s\n",buf);
rewind(fp);
fgets(buf,sizeof(buf),fp);
printf("2 buf is %s\n",buf);
fclose(fp);
return 0;
}
(4)feof函数:
标准输入输出库中的一个函数,用于判断文件流指针是否到达文件结尾。
- 1.功能:判断当前参数
stream
的文件流指针是否到达文件结尾。如果到达文件结尾则返回真,否则返回假。注意,该操作一定要在一次IO操作之后判断。 - 2.参数:
stream
为要判断结尾的文件流对象,可以是stdin
(程序会阻塞等待),如果是普通文件fp
则指向文件第一行数据。 - 3.返回值:成功到达结尾返回真,否则返回假。
在使用feof
函数时,通常与其他文件操作函数结合使用,例如fgetc
、fread
等,用于判断文件是否读取完毕。
缓冲区
1.行缓冲(Line Buffering)
行缓存是一种缓存机制,主要用于人机交互,如终端(stdout)。
特点:
(1) 缓存区大小通常为1K。
(2)刷新条件包括遇到\n
刷新、缓存区满刷新、程序结束刷新以及使用fflush
刷新(fflush(stdout)
)。
(3)行缓存多是关于终端的一些操作,例如在输入输出中,当遇到换行符\n
时,会刷新缓存区,将数据输出或读取。
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
// 1 printf("hello\n");
/* 2
int i = 0 ;
for(i = 0 ;i<1025;i++)
{
//printf("a");
fputc('a',stdout);
}
while(1)
sleep(1);
*/
//3
// printf("hello");
//
printf("hello");
fflush(stdout);
while(1);
return 0;
}
2.全缓冲(Full Buffering):
全缓存主要用于文件的读写操作。
特点:
(1)缓存区大小通常为4K。
(2)刷新条件包括缓存区满刷新、程序结束刷新以及使用fflush
刷新(fflush(fp)
)。
(3)对普通文件进行标准IO操作时,建立的缓存一般为全缓存。
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
FILE* fp = fopen("1.txt","w");
/*char buf[]="hello";
fputs(buf,fp);
*/
/* 4K
int i = 0 ;
for(i = 0 ;i<4097;i++)
{
fputc('a',fp);
}
while(1)
sleep(1);
*/
char buf[]="hello";
fputs(buf,fp);
fflush(fp);
while(1)
sleep(1);
return 0;
}
3.无缓冲(No Buffering):
不对数据进行缓存,直接刷新。
特点:
(1)主要用于出错处理信息的输出,如stderr。
(2)不缓存数据,直接将数据输出或处理。
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
fprintf(stderr,"fopen error ");
while(1)
sleep(1);
return 0;
}
在标准IO中,不同的缓存机制适用于不同的场景,以提高数据处理的效率和性能。例如,行缓存适用于终端交互,能够及时响应用户的输入和输出;全缓存适用于文件读写,能够减少系统调用的次数,提高数据传输的效率;无缓存则适用于需要及时输出错误信息等情况。
文件io
在操作系统中,为了方便用户使用系统功能,系统对外提供了一组系统函数,称为系统调用,其中包括文件IO。文件IO是一种基于Linux内核的没有缓存的IO机制,主要用于对设备文件和普通文件进行操作。
一、文件IO的特性
- 无缓存区:文件IO操作直接对文件进行读写,不依赖于缓存区,这意味着数据的读写更加直接和高效,但也需要用户自己管理数据的缓存。
- 文件描述符:文件IO的操作对象是文件描述符,这是一个小的非负整数(通常在0 - 1023之间)。内核为每一个打开的文件分配一个文件描述符,程序可以通过文件描述符来对文件进行操作。
- 默认打开的描述符:在程序启动时,操作系统会默认为其打开三个与流对象匹配的描述符:
- 0 ==> STDIN_FILENO === stdin(标准输入)
- 1 ==> STDOUT_FILENO == stdout(标准输出)
- 2 ==> STDERR_FILENO == stderr(标准错误输出)
(1)open函数
1.功能:获得一个文件描述符。
2.参数:
- pathname:文件名。
- flags:
O_RDONLY | 只读模式 |
O_WRONLY | 只写模式 |
O_RDWR | 读写模式 |
O_CREAT | 创建文件 |
O_EXCL | 需要和O_CREAT同时使用,表示新建的文件不能存在,成功,否则open就会失败 |
O_NOCTTY | 不是终端设备 |
O_TRUNC | 文件内容清空 |
O_APPEND | 追加 |
O_ASYNC | 异步IO,什么时候IO不确定 |
O_NONBLOCK | 非阻塞 |
- 返回值:成功返回文件描述符,失败返回 - 1。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd= open("1.txt",O_WRONLY | O_CREAT|O_TRUNC,0666);//truncate
if(-1 == fd)
{
printf("open error\n");
return 1;
}
printf("fd is %d\n",fd);
return 0;
}
(2)write函数
1.功能:通过文件描述符向文件中写一串数据。
2.参数:
- fd:文件描述符。
- buf:要写入文件的字符串的首地址。
- count:要写入字符的个数。
3.返回值:成功返回实际写入的个数,失败返回 - 1。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
int fd= open("1.txt",O_WRONLY | O_CREAT|O_TRUNC,0666);//truncate
if(-1 == fd)
{
printf("open error\n");
return 1;
}
printf("fd is %d\n",fd);
char buf[128]="hello,world";
int wr_ret = write(fd,buf,strlen(buf));
if(-1 == wr_ret)
{
printf("write error\n");
return 1;
}
close(fd);
return 0;
}
(3)read函数
1.功能:通过文件描述符读取文件中的数据。
2.参数:
- fd:文件描述符。
- buf:存放数据空间的首地址。
- count:要读到数据的个数。
3.返回值:成功返回读到数据的个数,失败返回 - 1,读到文件结尾返回0。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd= open("1.txt",O_RDONLY);//truncate
if(-1 == fd)
{
printf("open error\n");
return 1;
}
printf("fd is %d\n",fd);
char buf[512]={0};
int rd_ret = read(fd,buf,sizeof(buf));
if(rd_ret<=0)
{
printf("read eof or error\n");
return 1;
}
printf("ret %d,%s\n",rd_ret,buf);
close(fd);
return 0;
}
(4)close函数
1.功能:关闭一个已打开的文件描述符,释放相关资源。
2.参数:需要关闭的文件描述符。
3.返回值:成功返回0,失败返回 - 1。
(5)lseek函数
1.功能:定位文件光标的位置。
2.参数:
fd
:文件描述符,表示要操作的文件。offset
:偏移量,可以是正数(向后偏移)、负数(向前偏移)或零(不偏移)。whence
:指定偏移的起始位置,有以下几种取值:SEEK_SET
:从文件开头开始计算偏移量。SEEK_CUR
:从当前文件光标位置开始计算偏移量。SEEK_END
:从文件末尾开始计算偏移量。
3.返回值:成功返回偏移量。失败返回 - 1。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd= open("1.txt",O_WRONLY);//truncate
if(-1 == fd)
{
printf("open error\n");
return 1;
}
printf("fd is %d\n",fd);
off_t offset = lseek(fd,6, SEEK_SET);
printf("offset %ld\n",offset);
write(fd,"china",5);
close(fd);
return 0;
}
(6)fileno函数
功能:返回与文件流stream相关联的文件描述符。
参数:stream是一个指向文件流的指针。
返回值:成功时返回文件描述符,出错时返回-1并设置errnoo
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
//FILE*---> int fd;
FILE* fp = fopen("1.txt","r");
if(NULL == fp)
{
printf("fopen error\n");
return 1;
}
char buf[256]={0};
int fd = fileno(fp);
read(fd, buf,sizeof(buf)-1);
printf("%s\n",buf);
fclose(fp);
return 0;
}
(7)fdopen函数
功能:将现有的文件描述符fd转换为文件流,并指定文件打开模式mode。
参数:
fd:文件描述符。
mode:文件打开模式(如"r"、“w"、“a”等)。
返回值:成功时返回FILE *指针,出错时返回NULL并设置errno。
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
//FILE*---> int fd;
int fd = open("1.txt",O_RDONLY);
if(-1 == fd)
{
printf("fopen error\n");
return 1;
}
char buf[256]={0};
FILE* fp = fdopen(fd,"r");
if(NULL == fp)
{
printf("fdopen error\n");
return 1;
}
fgets(buf,sizeof(buf),fp);
printf("%s\n",buf);
fclose(fp);
return 0;
}
文件IO与标准IO的比较
文件IO:
- 特点:基于Linux内核,没有缓存区,操作对象是文件描述符,直接对文件进行读写。
- 适用场景:用于底层设备相关的开发。
- 优势:在某些特定场景下,如对性能要求较高、需要直接操作设备文件等,具有优势。
- 不足:效率、安全性和移植性相对较差。
标准IO:
- 特点:是对文件IO的封装,使用流(FILE*)进行操作,具有缓存机制(行缓存、全缓存、无缓存)。
- 适用场景:适用于纯上层开发。
- 优势:效率和安全性较高,移植性好,使用方便。
- 不足:对于底层设备的直接操作能力相对较弱。
总的来说,文件IO更接近底层,适合一些对性能和直接控制有较高要求的场景;标准IO则在大多数上层应用中更为常用,因为它提供了更方便、高效和安全的文件操作方式。在实际开发中,应根据具体需求选择合适的IO方式。