橙色
当你在文件中写入了10个字符后,又想把这10个字符读出来,该怎么做呢?因为有文件操作符指针的存在,此时该指针已经指在了这10个字符末尾,所以需要把该指针重定向,这就用了本文中所介绍的几个函数
fseek
:设置流 stream 的文件位置为给定的偏移 offset,参数 offset 意味着从给定的 whence 位置查找的字节数。
int fseek(FILE *stream, long int offset, int whence)
ftell
:返回给定流 stream 的当前文件位置。
long int ftell(FILE *stream)
stream
— 这是指向 FILE 对象的指针,该 FILE 对象标识了流。程序实例——求程序的有效字节
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv){
FILE *fp;
if(argc < 2) {
fprintf(stderr, "Usage...\n");
exit(1);
}
fp = fopen(argv[1], "r");
if(fp == NULL) {
perror("fopen()");
exit(1);
}
// 将指针定位在文件末尾
fseek(fp, 0, SEEK_END);
printf("%d\n", ftell(fp));
exit(0);
}
rewind
:设置文件位置为给定流 stream 的文件的开头。
void rewind(FILE *stream)
相当于(void) fseek(stream, 0, SEEK_SET);
注意:
fseek
和ftell
中偏移offset
的修饰类型是long
,因此只能对2G左右大小的文件进行操作,否则会超出long
的范围;
fseeko
和ftello
则将偏移的修饰类型使用typedef
定义为offset_t
,具体类型交由系统决定,因此不存在文件大小的限制。但是这两个函数不是C标准库函数,而是隶属于POSIX标准(POSIX是标准C库的超集,或者说,C库是普通话,而POSIX是方言)。
fflush
:刷新流 stream 的输出缓冲区。刷新,指的是将缓冲区(内存上的一片区域)的内容写入到磁盘(外存)中,或者输出到终端上显示。
int fflush(FILE *stream)
NULL
(即没传递参数),则刷新所有的已打开的流代码示例
#include <stdio.h>
int main() {
printf("Before while(1)");
while(1);
printf("After while(1)");
exit(0);
}
打印结果:
// 什么都不打印
原因:
对于标准输出,输出缓冲区刷新的时机:
\n
因此,可以修改为:
#include <stdio.h>
#include <stdlib.h>
int main() {
// 遇到\n刷新
printf("Before while(1)\n");
while(1);
printf("After while(1)\n");
exit(0);
}
或者修改为:
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("Before while(1)");
// 强制刷新
fflush(stdout);
// 或者 fflush(NULL);
while(1);
printf("After while(1)");
exit(0);
}
缓冲区的作用:大多数情况下是好事,合并系统调用,增加程序的吞吐量。
缓冲的分类:
line buffered
:针对标准输出(终端设备),有换行刷新,缓冲满刷新,强制刷新三种,后两个和全缓冲一致;fully buffered
:默认缓冲机制(除标准输出【终端设备】,例如重定向到文件),有缓冲满刷新,强制刷新两种,强制刷新例如调用fflush函数,或者进程结束时也会强制刷新;此时换行符仅仅只是个换行符,没有刷新功能;unbuffered
:例如stderr
,需要立即输出,数据会立即读入内存或者输出到外存文件和设备上;setvbuf
:定义流 stream 应如何缓冲。理解即可。
int setvbuf(FILE *stream, char *buffer, int mode, size_t size)
之前介绍的函数,都不能获得完整的一整行(有缓冲区大小的限制),而下面介绍的getline
函数则可以动态分配内存,当装不下完整一行时,又会申请额外的内存来存储。
getline
是C++标准库函数,但不是C标准库函数,而是POSIX所定义的标准库函数(在POSIX IEEE Std 1003.1-2008标准出来之前,则只是GNU扩展库里的函数)。在gcc编译器中,对标准库stdio
进行了扩展,加入了一个getline函数。
getline
会生成一个包含一串从输入流读入的字符的字符串,直到以下情况发生会导致生成的此字符串结束:
函数原型:
#include <stdio.h>
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
// 传参:
char *ptr;
// 函数内的实际操作:
// 假设读取到的字符串Hello的首地址为0x000
&ptr = 0x000; // 此时ptr就指向了Hello
n:如果是由系统malloc的指针填0;
stream:函数需要读取的FILE流
返回值:成功返回读取的字节数,失败或读完返回-1。
代码示例:
int main(int argc, char **argv) {
FILE *fp;
// 一定要初始化,否则指针会指向内存中的随机位置
char *linebuf = NULL;
size_t linesize = 0;
if(argc < 2) {
fprintf(stderr, "Usage...\n");
}
fp = fopen(argv[1], "r");
if(fp == NULL) {
perror("fopen()");
exit(1);
}
while(1) {
// 当返回-1时则读完
if(getline(&linebuf, &linesize, fp) < 0)
break;
printf("%d\n", strlen(linebuf));
}
fclose(fp);
exit(0);
}