APUE——标准IO相关

18 篇文章 0 订阅
3 篇文章 0 订阅

1.缓冲相关

标准I/O库提供缓冲的目的是尽可能减少使用read和write调用的次数。它也对每个I/O流自动地进行缓冲管理

  1. 全缓冲:标准I/O缓冲区满了后执行IO操作,也可使用fflush强制冲洗
  2. 行缓冲:换行时候执行IO操作,每一行的缓冲区满了也会执行,也可以强制fflush冲洗,当流涉及一个终端时(例如标准输入和标准输出),通常使用行缓冲
  3. 无缓冲:标准I/O库不对字符进行缓冲存储。例如,如果用标准I/O函数fputs写15个字符到不带缓冲的流中,则该函数很可能用write系统调用函数将这些字符立即写到相关联的打开文件上。标准出错流stderr通常是不带缓冲的
    要了解,如果在一个函数内分配一个自动变量类的标准I/O缓冲区,则从该函数返回之前,必须关闭该流,一般而言,应由系统选择缓冲区的长度,并自动分配缓冲区。在这种情况下关闭此流时,标准I/O库将自动释放缓冲区。
#include <stdio.h>
int fflush( FILE *fp );
返回值:若成功则返回0,若出错则返回EOF

此函数使该流所有未写的数据都被传送至内核。作为一个特例,如若fp是NULL,则此函数将导致所有输出流被冲洗。

使用setvbuf,我们可以精确地指定所需的缓冲类型IOFBF 全缓冲,_IOLBF 行缓冲,_IONBF 不带缓冲

#include<stdio.h>
#include<stdlib.h>

int main()
{
	int i;
	fprintf(stderr,"hello world\n");
	printf("before");//加上换行符printf("before\n")可以打印
	fflush(NULL); //NULL冲洗全部缓冲
	while(1);

	printf("after");

	exit(0);// exit会自动冲洗流
}

1.2 流定位

SEEK_SET表示从文件的起始位置开始,SEEK_CUR表示从当前文件位置开始,SEEK_END表示从文件的尾端开始。
#include <stdio.h>

long ftell( FILE *fp ); 获取当前文件位置
返回值:若成功则返回当前文件位置指示,若出错则返回-1L,long可能不够用

int fseek( FILE *fp, long offset, int whence );  设置文件位置
返回值:若成功则返回0, 若出错则返回非0void rewind( FILE *fp );  设置文件位置为开始位置fseek(fp1,0,SEEK_SET)

2 文件相关函数

当打开一个流时,标准I/O函数fopen返回一个指向FILE对象的指针。该对象通常是一个结构体,它包含了标准I/O库为管理该流所需要的所有信息,包括:

  1. 用于实际I/O的文件描述符、
  2. 指向用于该流缓冲区的指针、
  3. 缓冲区的长度、
  4. 当前在缓冲区中的字符数以及出错标志等等

2.1 fopen,fclose函数

#include <stdio.h>
FILE *fopen( const char *restrict pathname, const char *restrict type );
FILE *freopen( const char *restrict pathname, const char *restrict type, FILE *restrict fp );
FILE *fdopen( int filedes, const char *type );
三个函数的返回值:若成功则返回文件指针,若出错则返回NULL
#include <stdio.h>
int fclose( FILE *fp );
返回值:若成功则返回0,若出错则返回EOF

这里要注意下冲洗,在exit,return等进程终止时会冲洗,fclose()函数就可以把缓冲区内最后剩余的数据输出到内核缓冲区并关闭底层文件描述符。见man 3 fclose

在该文件被关闭之前,冲洗缓冲区中的输出数据。丢弃缓冲区中的任何输入数据。
如果标准I/O库已经为该流自动分配了一个缓冲区,则释放此缓冲区。
当一个进程正常终止时(直接调用exit函数,或从main函数返回),则所有带未写缓冲数据的标准I/O都会被冲洗,
所有打开的标准I/O流都会被关闭。
  1. fopen打开一个指定的文件。
  2. freopen在一个指定的流上打开一个指定的文件,如若该流已经打开,则先关闭该流。若该流已经定向,则freopen清除该定向。此函数一般用于将一个指定的文件打开为一个预定义的流:标准输入、标准输出或标准出错。
  3. fdopen获取一个现有的文件描述符(我们可能从open、dup、dup2、fcntl、pipe、socket、socketpair或accept函数得到此文件描述符),并使一个标准的I/O流与该描述符相结合。此函数常用于由创建管道和网络通信通道函数返回的描述符。因为这些特殊类型的文件不能用标准I/O fopen函数打开,所以我们必须先调用设备专用函数以获得一个文件描述符,然后用fdopen使一个标准I/O流与该描述符相关联。

在这里插入图片描述
在这里插入图片描述

2.2 fgetc, fputc函数

  1. 每次一个字符的I/O。一次读或写一个字符,如果流是带缓冲的,则标准I/O会处理所有缓冲。

  2. 每次一行的I/O。如果想要一次读或写一行,则使用fgets和fputs。每行都以一个换行符终止。当调用fgets时,应说明能处理的最大行长。

  3. 直接I/O。fread和fwrite函数支持这种类型的I/O。每次I/O操作读或写某种数量的对象,而每个对象具有指定的长度。这两个函数常用于从二进制文件中每次读或写一个结构。

#include <stdio.h>
int getc( FILE *fp );
int fgetc( FILE *fp );
int getchar( void ); 为
三个函数的返回值:若成功则返回下一个字符,若已到达文件结尾或出错则返回EOF
#include <stdio.h>
int putc( int c, FILE *fp );
int fputc( int c, FILE *fp );
int putchar( int c );
三个函数返回值:若成功则返回c,若出错则返回EOF

#include <stdio.h>
#include <stdlib.h>



int main(int argc, char* argv[])
{
	int a= 0;
	FILE *fp1, *fp2;
	fp1 = fopen(argv[1],"r");
	if(fp1 == NULL)
	{
		perror("fopen argv[1] error");
		exit(1);
	}
	fp2 = fopen(argv[2],"w");
	if(fp2 == NULL)
	{
		perror("fopen argv[2] error");
		exit(1);

	}
	while((a = fgetc(fp1)) != EOF)
	{
		fputc(a,fp2);
		

	}
	fseek(fp2,0L,SEEK_END);//定位指针到文件结尾,
	fprintf(stderr,"%ld\n",ftell(fp2));//可以打印出文章字节长度
								//rewind(fp2)= fseek(fp2,0L,SEEK_SET)
	fclose(fp2);
	fclose(fp1);
	exit(0);

}


2.3 fread,fwrite函数

fread和fwrite函数支持这种类型的I/O。每次I/O操作读或写某种数量的对象,而每个对象具有指定的长度。这两个函数常用于从二进制文件中每次读或写一个结构
这里建议将第二个参数,size设置为1,个数为字节个数,因为如果size比较大,fread(buffer,50,5,fp1),则读入文件字符总数不足50时,返回0,且目标文件没有写入!

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) 
如果成功返回nmemb,即size的个数,失败则返回0或者失败,
size -- 这是要读取的每个元素的大小,以字节为单位。
nmemb -- 这是元素的个数,每个元素的大小为 size 字节。
#include <stdio.h>
#include <stdlib.h>

#define BUFFERSIZE 1024

int main(int argc, char* argv[])
{
	size_t a= 0;
    size_t b= 0;
    char buffer[BUFFERSIZE];
	FILE *fp1, *fp2;
	fp1 = fopen(argv[1],"r");
	if(fp1 == NULL)
	{
		perror("fopen argv[1] error");
		exit(1);
	}
	fp2 = fopen(argv[2],"w");
	if(fp2 == NULL)
	{
		perror("fopen argv[2] error");
		exit(1);

	}
	a = fread(buffer,1,BUFFERSIZE,fp1);  //a 为成功读取的字符个数,第二个参数设置1,保证个数为字节个数,小于0不写
    fprintf(stderr,"a %lu\n",a);  // stderr 与 stdout 均指向终端显示

    b = fwrite(buffer,1,a,fp2);   //这里要注意写入的个数应该是读到的个数,返回写入的个数
    fprintf(stderr,"b %lu\n",b);

	fclose(fp2);
	fclose(fp1);

	exit(0);

}


2.4 fgets fputs函数

fputs与fgets使用时注意,fgets中的n为str指针指向的buffer长度,每次从流中get n-1长度的字符串(不包括\0),放入buffer,其最后一位自动补\0,同理fputs时,也只将n-1长度的buffer放入流中!!!
C 库函数 int fputs(const char *str, FILE *stream) 把字符串写入到指定的流 stream 中,但不包括空字符
C 库函数 char *fgets(char *str, int n, FILE *stream) 从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。

char *fgets(char *str, int n, FILE *stream)
如果成功,该函数返回相同的 str 参数。如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针。
如果发生错误,返回一个空指针。
str -- 这是指向一个缓冲区的地址。
n -- 缓冲区的长度n(包括最后的空(NUL)字符码'\0')。实际的strlen(str)最大为n-1
stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了要从中读取字符的流
int fputs(const char *str, FILE *stream)
函数fputs将一个以null符终止的字符串写到指定的流,尾端的终止符null不写出
include <stdio.h>
#include <stdlib.h>

#define BUFFERSIZE 1024

int main(int argc, char* argv[])
{
	size_t a= 0;
    int b= 0;
    char buffer[BUFFERSIZE];
	char* c = NULL;
	FILE *fp1, *fp2;
	fp1 = fopen(argv[1],"r");
	if(fp1 == NULL)
	{
		perror("fopen argv[1] error");
		exit(1);
	}
	fp2 = fopen(argv[2],"w");
	if(fp2 == NULL)
	{
		perror("fopen argv[2] error");
		exit(1);

	}
	while(1)
	{
	c = fgets(buffer,BUFFERSIZE,fp1);  //gets不检查缓冲区溢出,没有size,用fgets代替,读取BUFFERSIZE-1个字节
	if(c==NULL)
		break;
    fprintf(stderr,"a %s, %d\n",c,strlen(buffer));  // c为fegts返回值,如果正确返回buffer指针,错误返回NULL

    b = fputs(buffer,fp2); // fputs将buffer字符串写入fp2文件,但是不包括'\0' NULL符
	if(b == EOF)    //fputs返回的是写入buf的字节个数,是int型,用EOF判断
	{
		perror("error of fputs");
	}
    fprintf(stderr,"b %d\n",b);
		

	}

	fclose(fp2);
	fclose(fp1);
	exit(0);

}
a hello world
, 12
b 1
a how are you
, 12
b 1
a sunny, 5
b 1

2.5 fprintf, sprintf函数

C 库函数 int fprintf(FILE *stream, const char *format, …) 发送格式化输出到流 stream 中。
C 库函数 int sprintf(char *str, const char *format, …) 发送格式化输出到 str 所指向的字符串

int fprintf(FILE *stream, const char *format, ...)
如果成功,则返回写入的字符总数,否则返回一个负数。
int sprintf(char *str, const char *format, ...);
如果成功,则返回写入的字符总数,不包括字符串追加在字符串末尾的空字符。如果失败,则返回一个负数。
可以实现与atoi相反的操作,格式化输出字符串
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{

	char ste[] = "123456";
	char buf[1024];
	int year =2019, month = 1, day  = 30;
	fprintf(stderr,"%d\n",atoi(ste));  //int atoi(const char *nptr); 字符串转整形
	sprintf(buf,"%d - %d - %d",year,month,day); //可以实现与atoi相反的操作,格式化输出字符串
	fputs(buf,stderr);
	exit(0);
}


2.6 getline函数

getline函数从FILE中获取一行数据,通过malloc申请内存,如果超过长度,则realloc在该地址上重新申请内存

       #include <stdio.h>
       ssize_t getline(char **lineptr, size_t *n, FILE *stream);
       返回-1读取失败,正确返回读取成功的字节个数

注意

  1. 初始时需要*lineptr = NULL,*n = 0来实现判断是否是第一次,需要malloc,第一次malloc n大小为120,后续n可能增加

  2. 上述函数只申请内存,并不释放内存,可通过free来释放

  3. getline返回值为每一行strlen(char* a)的大小,不包括null的‘\0’

  4. void realloc(void *ptr,size_t new_size),ptr为之前malloc获得的指针,如果ptr为NULL,则与malloc一致

  5. realloc申请的内存,如果比原来的小则后部拿掉,如果比原来的大则后面增加内存
    man 3 getline如下

    If *lineptr is set to NULL and *n is set 0 before the call, then get‐
    line() will allocate a buffer for  storing  the  line.   This  buffer
    should be freed by the user program even if getline() failed.
    
    Alternatively,  before  calling  getline(),  *lineptr  can  contain a
    pointer to a malloc(3)-allocated buffer *n bytes  in  size.   If  the
    buffer  is  not  large  enough to hold the line, getline() resizes it
    with realloc(3), updating *lineptr and *n as necessary.
    
    On success, getline() and getdelim() return the number of  characters
    read, including the delimiter character, but not including the termi‐
    nating null byte ('\0').  This value can be used to  handle  embedded
    null bytes in the line read.
    
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
int main(int argc, char* argv[])
{
	FILE *fp1, *fp2;
	char* lineptr = NULL;
	size_t getbyte =0 ;


	fp1 = fopen(argv[1],"r");
	fp2 = fopen(argv[2],"r");

	while(1)
	{
		if(getline(&lineptr,&getbyte,fp1)<0){


			perror("getline error");
			break;
		}
			printf("line1 %d\n",getbyte);
			fprintf(stderr,"line2 %d\n",strlen(lineptr));

	}
	fclose(fp1);
	fclose(fp2);

	exit(0);// exit会自动冲洗流
}

文件里内容为
hello world
how are you
sunny

line0 12
line1 120
line2 12
line0 12
line1 120
line2 12
line0 5
line1 120
line2 5
getline error: Success
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值