linux标准io
标准I/O
标准I/O是C库函数,该库通过系统调用完成自己的目标。
文件IO的头文件是unistd.h
标准IO的为 stdio.h
文件IO: | 标准IO |
---|---|
open(char*, flag, mode) | FILE *fopen (const char *path, const char *mode) |
close(fd) | int fclose(fd) |
lseek(int fd, off_t offset, int whence) | fseek, rewind |
1.printf函数以及缓冲区问题
【1.LINUX系统下】
一般而言,printf是带有行缓冲的函数,printf把打印的消息先输出到行缓冲区,在以下几种情况下:1.
程序结束时调用exit(0)/return;2.
遇到回车\n(因此,这种缓存也叫行缓存),3.
调用fflush函数(fclose()会调用该函数);4.
缓冲区满。会自动刷新缓冲区,缓冲区的内容显示到标准输出上。
比如在LINUX系统下,执行如下程序:
#include <stdio.h>
int main(void)
{
printf("hello");
while(1);
return 0;
}
使用GCC编译后执行,发现shell中并没有输出hello,这是因为LINUX系统下,printf把“hello”输出到缓冲区,而此时没有发生缓冲区刷新的4种情况,因此shell中并不会看到hello。但是如果使用printf("hello\n");或者在printf后使用fflush(stdout);那么执行时在shell中就会看到hello输出。
【2.WINDOWS系统下】
同样这段程序,如果在Windows下编译运行(使用VC++6.0),会发现控制台中马上看到hello的输出。分析原因发现,Windows下stdout没有提供缓冲(不知道这个原因是否确切)。比如执行如下程序:
#include <iostream>
int main() {
char a[] = "hello linux";
printf("%s",a);
}
可以看到stdout的缓冲区大小为0,这也就意味着,stdout是没有缓冲区的,因此printf输出到stdout,不存在缓冲,这个和LINUX系统中对printf处理的机制是不一样的。
缓冲区的概念:
三个缓存的概念(数组):
-
我们的程序中的缓存,就是你想从内核读写的缓存(数组)----用户空间的缓存
-
每打开一个文件,内核在内核空间中也会开辟一块缓存,这个叫内核空间的缓存
文件IO中的写即是将用户空间中的缓存写到内核空间的缓存中。
文件IO中的读即是将内核空间的缓存写到用户空间中的缓存中。
-
标准IO的库函数中也有一个缓存,这个缓存称为----库缓存
库缓存写满时,会调用系统调用函数,将lib_buffer 内容写到kernel_buffer中去。库缓存的测试大小时1024byte。
2.FILE *fopen()
FILE *fopen (const char *path, const char *mode);
FILE 定义:struct _IO_FILE,在/usr/include/libio.h
包含读写缓存的首地址、大小、位置指针等。
参数Mode: 类似于 文件IO flag
r或rb | 打开只读文件,该文件必须存在。 |
---|---|
r+或r+b | 打开可读写的文件,该文件必须存在。 |
w或wb | 打开只写文件,若文件存在则文件长度清为0,即会擦些文件以 前内容。若文件不存在则建立该文件。 |
w+或w+b或wb+ | 打开可读写文件,若文件存在则文件长度清为零,即会擦些文件 以前内容。若文件不存在则建立该文件。 |
a或ab | 以附加的方式打开只写文件。若文件不存在,则会建立该文件, 如果文件存在,写入的数据会被加到文件尾,即文件原先的内容 会被保留。 |
a+或a+b或ab+ | 以附加方式打开可读写的文件。若文件不存在,则会建立该文 件,如果文件存在,写入的数据会被加到文件尾后,即文件原先 的内容会被保留。 |
简单来说:
b:二进制文件
r: 只读方式打开文件,文件必须存在;
w或a:只写方式打开文件,文件不存在则创建;
区别: w等价O_TRUNC,a等价O_APPEND;
+:读写方式打开文件,文件必须存在;
返回值:FILE * 文件流指针 类似于文件IO 中的文件描述符
若使用fopen函数创建了一个文件,则设置权限为 0666(在umask码为0000的情况下)
例:以读写方式打开一个文件,该文件必须存在: r+
以追加方式打开一个文件,若文件不存在,则创建: a或a+
3.int fclose(FILE *stream)
fclose()调用成功返回0,失败返回EOF,并设置errno
在该文件被关闭之前,刷新缓存中的数据。如果标准I / O库已经为该流自动分配了一个缓存,则释放此缓存。
即使是行缓存,close()之后,即使没达到缓冲区溢出标准,也会进行输出
4.行缓存与全缓存函数
行缓存
4.1fgets fputs fgetc fputc get put
fgets
char *fgets (char *buf, int size, FILE *stream)
第一个参数:缓存,即读到哪里去
第二个参数:读多少个字节
第三个参数:从什么地方读
返回值:若成功则为s(缓存的地址),若已处文件尾端或出错则为null
fputs
int fputs(const char *buf,int size,FILE *stream);
第一个参数:缓存,即写什么内容
第二个参数:写到哪里去
若成功则为非负值,若出错或到结尾则为EOF== -1 。
一个字符读写函数fgetc(FILE *stream)返回值:正确为读取的字符,到文件结尾或出错时返回EOF。
*fputc(int c,FILE stream) 返回值:成功则返回输入的字符,出错返回EOF。 该函数不是行缓存\n不能清除缓存
,但是它是有缓存的。
gets() puts()不安全,已经被废弃。
4.2 fprintf、printf、sprintf
int fprintf(FILE *stream,”字符串格式”)
fprintf可以输出到文件中,也可输出到显示器,
printf 只能输出到显示器中。
int sprintf(str *, “字符串格式”)
输出内容到一个字符串中
输出内容到一个字符串中
全缓存(必须写满才会输入内核)
4.3 fread
size_t fread( void *restrict buffer, size_t size, size_t count, FILE *restrict stream )
buffer :指向要读取的数组中首个对象的指针
size :每个对象的大小(单位是字节)
count :要读取的对象个数
stream :输入流
返回值 :返回成功读取的对象个数,若出现错误或到达文件末尾,则可能小于count。若size或count为零,则fread返回零且不进行其他动作。fread不区分文件尾和错误,因此调用者必须用feof和ferror才能判断发生了什么。
4.4 fwrite
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
5.读写位置指针 fseek
int fseek(FILE *stream, long offset, int fromwhere)
fseek() 参数与lseek是一样的,使用方式也相同。
rewind(FILE *fp)
用于设定流的文件位置指示为文件开始,该函数调用成功无返回值。
等价于(void)fseek(fp 0, SEEK_SET);
ftell(FILE *fp)
用于取得当前的文件位置,调用成功则为当前文件位置指示,若出错则为-1L;
但是返回值不一样
Lseek的返回值是:当前文件的位置指针值
fseek()的返回值是:成功返回0,失败返回-1,并设置errno
6.刷新缓存:fflush(FILE *fp)
把库函数中的缓存的内容强制写到内核中。
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(){
char buf[]="hello linux";
FILE *fp = fopen("./a.c","w+");
if(fp==NULL){
printf("file open failure\n");
exit(-1);
}
fputs(buf,fp);
//hello linux is not in a.c if no fflush(fp);=============================================
fflush(fp);
while(1);
close(fp);
}
7.stderr和stdout
无缓存:stderr 若输出是stderr,fputs()一样无缓存。
行缓存: stdout
9. 判断是否已经到文件结束int feof(FILE *stream);
功能:判断是否已经到文件结束
参数:文件流
返回值:到文件结束,返回为非0,没有则返回0
10.判断是否读写错误int ferror(FILE *stream);
功能:判断是否读写错误(因为fgets()得到EOF有两种可能,1出错,2结尾,该函数可以用来对这两种情况进行判断)
参数:文件流
返回值:是读写错误,返回为非0,不是则返回0
11.清除流错误void clearerr(FILE *stream);
功能:clearerr的作用是使文件错误标志和文件结束标志置为0.假设在调用一个输入输出函数时出现了错误,ferror函数值为一个非零 值。在调用clearerr(fp)后,ferror(fp)的值变为0。
参数:文件流
用fgetc,fputc实现cat
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
int main(int argc, char* argv[]) {
//open source file
FILE* src_fp;
if (argc < 2) {
printf("please input src file\n");
return -1;
}
src_fp = fopen(argv[1], "r");
if (src_fp == NULL) {
printf("open input file fail\n", argv[1]);
return -2;
}
printf("open input file success\n", argv[1]);
//read and write
while (1) {
int read_c = fgetc(src_fp);
if (feof(src_fp)) {
printf("%s read at end", argv[1]);
break;
}
//注意 stdout是一个File* 不能按照read系统调用里的写 1标准输出
fputc(read_c, stdout);
}
fclose(src_fp);
return 0;
}