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处理的机制是不一样的。

缓冲区的概念:

三个缓存的概念(数组):

  1. 我们的程序中的缓存,就是你想从内核读写的缓存(数组)----用户空间的缓存

  2. 每打开一个文件,内核在内核空间中也会开辟一块缓存,这个叫内核空间的缓存

    文件IO中的写即是将用户空间中的缓存写到内核空间的缓存中。

    文件IO中的读即是将内核空间的缓存写到用户空间中的缓存中。

  3. 标准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不区分文件尾和错误,因此调用者必须用feofferror才能判断发生了什么。

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;

}
posted @ 2020-12-20 10:35  lsxkugou  阅读(155)  评论(0编辑  收藏  举报