标准I/O的效率
使用getc
和putc
函数将标准输入复制到标准输出。
#include "../../include/apue.h"
int main(void)
{
int c;
while((c=getc(stdin)) != EOF)
if(putc(c,stdout) == EOF)
err_sys("output error");
if(ferror(stdin))
err_sys("input error");
exit(0);
}
结果如下:
使用fgetc
和fputc
改写上面函数如下:
#include "../../include/apue.h"
int main(void)
{
int c;
while((c=fgetc(stdin)) != EOF)
if(fputc(c, stdout) == EOF)
err_sys("output error");
if(ferror(stdin))
err_sys("input error");
exit(0);
}
结果如下:
使用fgets
和fputs
函数来实现读写行。
#include "../../include/apue.h"
int main(void)
{
char buf[MAXLINE];
while(fgets(buf, MAXLINE, stdin) != NULL)
if(fputs(buf, stdout) == EOF)
err_sys("output error");
if(ferror(stdin))
err_sys("input error");
exit(0);
}
结果如下:
标准I/O库函数的调用与系统调用
read
和write
相比并不慢很多,对于大多数比较复杂的应用程序而言,最主要的用户CPU时间是由应用本身的各种处理消耗的,而不是由标准I/O例程消耗的。
二进制I/O
以下两个函数来实现二进制I/O操作。
#include <stdio.h>
size_t fread(void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
size_t fwrite(const void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
两个函数返回值:读或写的对象数。
测试示例(读或写数组元素):
#include "../../include/apue.h"
int main(void)
{
FILE *fp;
if((fp=fopen("fopenfile", "w+")) == NULL){
perror("fopen error.");
exit(1);
}
float data[10] = {0, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9};
if(fwrite(&data[2], sizeof(float), 4, fp) != 4)
err_sys("fwrite error");
rewind(fp);
float buf[4] = {0};
fread(buf, sizeof(float), 4, fp);
for(int i = 0; i < 4; i++)
printf("%.1f\n", buf[i]);
exit(0);
}
结果如下:
注意
fwrite
函数写入文件中的数据会出现乱码,无法直接查看。
测试示例(读或写结构体):
typedef struct {
short count;
long total;
char name[NAMESIZE];
}item;
void test2()
{
FILE *fp;
if((fp=fopen("fopenfile2","w+")) == NULL)
perror("fopen error");
item i = {12, 255, "dunkwan"};
if(fwrite(&i, sizeof(item), 1, fp) != 1)
{
err_sys("fwrite error");
}
rewind(fp);
item j;
memset(&j, 0, sizeof(item));
fread(&j, sizeof(item), 1, fp);
printf("%d %ld %s\n", j.count, j.total, j.name);
}
结果如下:
使用二进制I/O的基本问题是,它们只能用于读在同一系统上已写的数据。
定位流
三种定位标准I/O的方法。
ftell
和fseek
函数
#include <stdio.h>
long ftell(FILE *fp);
返回值: 若成功,返回文件当前位置;若出错,返回 -1L。
int fseek(FILE *fp, long offset, int whence);
返回值:若成功,返回0; 若出错,返回 -1。
void rewind(FILE *fp);
fseek
中offset
参数表示偏移量,whence
参数来指定在当前流中的位置,其值可为与lseek
中相同的三个值,分别是SEEK_SET
(表示文件起始位置)、SEEK_CUR
(表示文件当前位置)以及SEEK_END
(表示文件末尾位置)。rewind
函数用于将流当前位置设为文件的起始位置。
ftello
和fseeko
函数
#include <stdio.h>
off_t ftello(FILE *fp);
返回值: 若成功,返回文件当前位置;若出错,返回(off_t)-1。
int fseeko(FILE *fp, off_t offset, int whence);
返回值: 若成功,返回 0;若错误,返回 -1。
ftello
和ftell
除了off_t
与long
不同外,其他相同。fseeko
和fseek
也是如此。
fgetpos
和fsetpos
函数
#include <stdio.h>
int fgetpos(FILE *restrict fp, fpos_t *restrict pos);
int fsetpos(FILE *fp, const fpos_t *pos);
两个函数返回值:若成功, 返回0;若出错,返回非0。
测试示例:
#include "../../include/apue.h"
void test()
{
FILE *fp;
if((fp=fopen("fgfsfile", "w+")) == NULL)
perror("fopen error");
fseek(fp, 255, SEEK_CUR);
fpos_t *p =(fpos_t *)malloc(sizeof(fpos_t));
fgetpos(fp, p);
char buf[100] = "hello world!";
fwrite(buf, 1, strlen(buf), fp);
fsetpos(fp, p);
char res[100];
memset(res, 0,sizeof(res));
fread(res, 1, sizeof(res), fp);
printf("res = %s\n", res);
}
int main()
{
test();
return 0;
}
结果如下:
由上图可知,fgetpos
函数获取当前流的位置,经过fwrite
函数写入数据后,再将之前获取的流位置设置为流的位置,故通过fread
函数得到的数据为写入的数据