流的定位

简介: 流的定位

了解流的定位,需要先了解流的读写位置偏移的情况。通常每个打开的流内部都有一个当前读写位置,流被打开时,当前读写位置为 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 的镜像合并就可以采用这些函数接口通过定位来实现。

目录
相关文章
|
开发者
数据解析之Xpath解析(超详细定位)2
数据解析之Xpath解析(超详细定位)2
90 0
|
21天前
|
机器学习/深度学习 监控 算法
《C++ 实时视频流物体跟踪与行为分析全解析》
本文探讨了C++在实时视频流处理中的应用,涵盖物体跟踪和行为分析的关键技术。从视频读取与解码到特征提取、跟踪算法选择、数据关联及行为模型构建,详细介绍了技术要点和应用场景,如安防监控、智能交通和工业自动化。面对复杂环境,C++程序需不断优化以提高准确性和鲁棒性。
62 12
|
Linux
44 # 流的原理
44 # 流的原理
68 0
|
8月前
|
存储 C++ iOS开发
C++ 文件和流
C++ 文件和流
58 0
java流是指在Java中用来读写数据的一组有序的数据序列,它可以将数据从一个地方带到另一个地方。java流分为输入流和输出流,输入流是从源读取数据的流,而输出流是将数据写入到目的地的流。Java流又可以分为字节流和字符流,字节流读取的最小单位是一个字节(1byte=8bit),而字符流一次可以读取一个字符(1char = 2byte = 16bit)。Java流还可以分为节点流和处理流,节点流是直接从一个源读写数据的流(这个流没有经过包装和修饰),处理流是在对节点流封装的基础上的一种流。
131 0
|
8月前
|
程序员 C++ iOS开发
c++文件和流
c++文件和流
43 0
|
SQL JavaScript 前端开发
开始使用流
Java 8 中的 Stream 俗称为流,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念 Stream 用于对集合对象进行各种非常便利、高效的聚合操作,或者大批量数据操作 Stream API 借助于 Lambda 表达式,极大的提高编程效率和程序可读性 同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势 通过下面的例子我们可以初步体会到使用 Stream 处理集合的便利性
59 1
|
XML 数据格式 开发者
数据解析之Xpath解析(超详细定位)1
数据解析之Xpath解析(超详细定位)1
283 0
I/O流
IO流:I的全称是Input,O的全称是Output。表示读取,流可以看做是程序传输数据的通道。 作用:解决程序请求资源,输出资源的问题。
60 0

热门文章

最新文章