流的定位

简介: 流的定位

了解流的定位,需要先了解流的读写位置偏移的情况。通常每个打开的流内部都有一个当前读写位置,流被打开时,当前读写位置为 0 ,表示在文件的开始位置进行读写。每当读写一次数据后,当前读写位置自动增加实际读写的大小。在读写流之前可先对流进行定位,即移动到指定的位置再操作。


测试示例如下:

#include <stdio.h>
#define errlog(errmsg)                                                         \
  perror(errmsg);                                                              \
  printf("--%s--%s--%d--\n", __FILE__, __FUNCTION__, __LINE__);                \
  return -1;
int main(int argc, const char *argv[]) {
  FILE *fp;
  int ch;
  if ((fp = fopen("test.txt", "w+")) == NULL) {
    errlog("fopen error");
  }
  fputc('a', fp);
  fputc('b', fp);
  fputc('c', fp);
  fputc('d', fp);
  while ((ch = fgetc(fp)) != EOF) {
    printf("ch = %c\n", ch);
  }
  fclose(fp);
  return 0;
}点击复制复制失败已复制


编译并运行,可以看到同级的 test.txt 文件中的内容是 abcd

$ gcc main.c && ./a.out点击复制复制失败已复制


结果没有读到任何内容,但是文件中的数据存在。这说明读写位置发生了偏移。设想,当向文件中写入字符后,当前的读写位置已经不处于文件的开始处了。而是在写入的字符的末尾,因此当从这一位置开始读时,将无法读取任何内容。如同在 Windows 系统中写文本文件时,每次通过键盘输入之后,光标都会偏移到文字的下一位。这样,下一次写入从该位置开始写入。相反如果光标不发生移动,每次写都在一个位置,那么每次写入的数据势必会把上一次写入的数据覆盖。而如果从数据的末尾位置读取,光标前的数据将不会被读取。因此在对文件进行操作时,读写位置将十分关键。


fseek()和ftell()函数

fseek() 函数和 ftell() 函数被用来实现读写位置的定位及位置的查询。

#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
int ftell(FILE *stream);点击复制复制失败已复制


fseek() 函数,参数 stream 为指定的流, whence 为需要定位的位置,可设置为 SEEK_SETSEEK_CURSEEK_END ,分别表示定位到文件的开始处当前位置以及文件的末尾offset 表示在第三个参数已经定位的基础上再发生偏移的量,其值为长整型


ftell() 函数则用来获取读写位置,执行成功返回当前读写位置相对于文件开始处的偏移量。使用这两个函数对上例进行修改,如下所示:

#include <stdio.h>
#define errlog(errmsg)                                                         \
  perror(errmsg);                                                              \
  printf("--%s--%s--%d--\n", __FILE__, __FUNCTION__, __LINE__);                \
  return -1;
int main(int argc, const char *argv[]) {
  FILE *fp;
  int ch;
  long offset;
  if ((fp = fopen("test.txt", "w+")) == NULL) {
    errlog("fopen error");
  }
  fputc('a', fp);
  fputc('b', fp);
  fputc('c', fp);
  fputc('d', fp);
  fseek(fp, 0, SEEK_SET);
  while ((ch = fgetc(fp)) != EOF) {
    printf("ch = %c\n", ch);
  }
  offset = ftell(fp);
  printf("offset = %ld\n", offset);
  fclose(fp);
  return 0;
}点击复制复制失败已复制


上述代码中,使用 fseek() 函数进行重新定位,将读写位置定位到文件的开始处。此时执行循环读取则可以从文件开始处读取相应的内容。 ftell() 函数获取读取之后的读写位置的偏移量。编译并运行如下所示:

$ gcc main.c && ./a.out
ch = a
ch = b
ch = c
ch = d
offset = 4点击复制复制失败已复制


注意

如果打开文件的方式为 "a+" ,则会出现不同的情况。


修改上述示例,使用 a+ 打开文件,内容如下:

#include <stdio.h>
#define errlog(errmsg)                                                         \
  perror(errmsg);                                                              \
  printf("--%s--%s--%d--\n", __FILE__, __FUNCTION__, __LINE__);                \
  return -1;
int main(int argc, const char *argv[]) {
  FILE *fp;
  int ch;
  long offset;
  if ((fp = fopen("test.txt", "a+")) == NULL) {
    errlog("fopen error");
  }
  fputc('a', fp);
  fputc('b', fp);
  fputc('c', fp);
  fputc('d', fp);
  fseek(fp, 0, SEEK_SET);
  fputc('e', fp);
  offset = ftell(fp);
  printf("offset = %ld\n", offset);
  fclose(fp);
  return 0;
}点击复制复制失败已复制


实践下来得到的结果是:

$ gcc main.c && ./a.out
offset = 5点击复制复制失败已复制


但是书中说得是:

$ gcc main.c && ./a.out
offset = 1点击复制复制失败已复制


与书中给出的不一致。书中的观点:在执行定位操作后,写入字符 'e' ,并没有从文件开始处写入将字符 'a' 覆盖,而是追加到文件的末尾,但是使用 ftell() 函数查询读写位置偏移量时,偏移量为 1 ,并非是 5 。因此,这样的结果总会让人产生定位操作在文件采用 aa+ 方式打开时失效的感觉。这种情况要格外注意。使用追加的方式打开文件后,写操作默认是从文件末尾处开始的


示例:获取文件的大小

#include <stdio.h>
#define errlog(errmsg)                                                         \
  perror(errmsg);                                                              \
  printf("--%s--%s--%d--\n", __FILE__, __FUNCTION__, __LINE__);                \
  return -1;
int main(int argc, const char *argv[]) {
  FILE *fp;
  if ((fp = fopen(argv[1], "r")) == NULL) {
    errlog("fopen error");
  }
  fseek(fp, 0, SEEK_END);
  printf("The size of %s is %ld\n", argv[1], ftell(fp));
  fclose(fp);
  return 0;
}点击复制复制失败已复制


编译运行:

$ gcc main.c && ./a.out test.txt
The size of test.txt is 5点击复制复制失败已复制


通过上述示例,可以看到关于读写位置定位的简单应用。通过这些定位函数也可以用于实现文件的分隔合并。例如, Linux 内核启动引导系统 Bootloader 的镜像合并就可以采用这些函数接口通过定位来实现。

目录
相关文章
|
9月前
|
开发者
数据解析之Xpath解析(超详细定位)2
数据解析之Xpath解析(超详细定位)2
56 0
|
1月前
|
存储 C++ iOS开发
C++ 文件和流
C++ 文件和流
24 0
|
8月前
|
Linux
44 # 流的原理
44 # 流的原理
34 0
|
1月前
|
存储 编解码 缓存
【ffmpeg 移动视频流位置】深入理解FFmpeg:精细探讨seek操作和编解码上下文
【ffmpeg 移动视频流位置】深入理解FFmpeg:精细探讨seek操作和编解码上下文
92 0
|
9月前
|
Java 程序员
Stream流操作-结果收集终止方法-最终篇
Stream流操作-结果收集终止方法-最终篇
83 0
|
7月前
流的关闭的几种方式
流的关闭的几种方式
|
9月前
|
XML 数据格式 开发者
数据解析之Xpath解析(超详细定位)1
数据解析之Xpath解析(超详细定位)1
196 0
|
11月前
|
存储 Java
流及其相关操作
流及其相关操作
|
11月前
|
人工智能 监控 算法
《多路监控视频实时拼接》原理及案例分享
通过视频拼接技术,对有重叠区域的多路源视频数据利用拼接算法进行无缝实时拼接,消除重叠区域,形成宽角度、大视场视频图像,从而实现将多路监控视频拼接成一路视频,使拼接后的视频清晰无缝,并实时播放,同时支持回放查看,解决多个人同时对同一监控场景不同角度进行观看的需求,充分满足用户的需求。
551 0
|
前端开发
我所理解的标准流与浮动流
我所理解的标准流与浮动流
78 0