C语言文件操作详解(下)

简介: C语言文件操作详解

四:sscanf与sprintf函数

1:sprintf总述

int sprintf ( char * str, const char * format, ... );
功能总述:
将格式化数据读取后存放到str所指向的区域中,如果有'\n'也会加入其中
例如:
sprintf(arr, "%d %f %s\n", s.a, s.s, s.q);
将从结构体变量s中读取到的对应的格式型数据存放到数组arr中

2:sprintf使用实例

这里修改了一下结构体S

struct S
{
  int a;
  float s;
  char q[10];
};
int main()
{
  char arr[30] = { 0 };
  struct S s = { 100,3.14f,"hello" };
  sprintf(arr, "%d %f %s\n", s.a, s.s, s.q);
  //将格式化数据读取后存放到arr数组中,如果有'\n'也会读入其中
  printf("%s", arr);
  printf("you can see '\\n'");//用\把'\n'中的\n转义了一下
}

3:sscanf总述

int sscanf ( const char * s, const char * format, ...);\
功能总述:
1.从字符串中读取格式化数据
2.从 s 读取数据并根据参数格式将它们存储到附加参数给出的位置,就像使用 scanf 一样,但从 s 而不是标准输入 (stdin) 读取。
例如:
struct S tmp = { 0 };
sscanf(arr, "%d %f %s", &(tmp.a), &(tmp.s), tmp.q);
将从arr数组中读取到的格式化数据写入结构体变量tmp中

4:sscanf使用实例

int main()
{
  char arr[30] = { 0 };
  struct S s = { 100,3.14f,"hello" };
  sprintf(arr, "%d %f %s", s.a, s.s, s.q);
  //将格式化数据读取后存放到arr数组中,如果有'\n'也会读入其中
  struct S tmp = { 0 };
  sscanf(arr, "%d %f %s", &(tmp.a), &(tmp.s), tmp.q);
  //将从arr数组中读取到的格式化数据写入结构体变量tmp中
  printf("%d %f %s", tmp.a, tmp.s, tmp.q);
}

五:fread与fwrite函数

两者被称为二进制输入/输出函数.

将读取到的数据以二进制的方式写入流中

针对的对象是文件

1:fwrite总述

size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream);
功能总述:
从ptr指向的内存空间中读取count个大小为size的数据存放到stream流中

2:fwrite使用实例

int main()
{
  FILE* pf = fopen("data.txt", "wb");//二进制的写要加上'b'
  if (NULL == pf)
  {
    perror("fopen");
    return 1;
  }
  struct S s = { 4,2.5f,"hello"};
  //写文件
  fwrite(&s, sizeof(struct S), 1, pf);
  //从s指向的内存空间中读取1个大小为sizeof(struct S)的数据存放到pf指向的流中
  fclose(pf);
  pf = NULL;
  return 0;
}

因为写入的是二进制数据,所以以普通的文本文件打开方式打开会出现下面这种情况

也就是出现一堆乱码

而我们在VS中用二进制编辑器打开后就能很好地查看其中的数据了

我这台电脑是小端存储方式,关于大小端的问题,大家可以看我的另一篇博客:

C语言数据存储深度剖析

3:fread总述

二进制读文件
读二进制文件存放到其他空间(ptr指向的空间)中
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
功能总述:
1.从流中读取count个大小为size的数据存放到ptr指向的空间中
2.这里用void* 指针类型便于接受任何类型的指针
3.返回实际读到的数据的个数
根据这个来决定还要不要往下继续读取
例如:
fread(&s, sizeof(struct S), 1, pf);
从流中读取count个大小为size的数据存放到s中

4:fread使用实例

int main()
{
  FILE* pf = fopen("data.txt", "rb");//二进制的写要加上'b'
  if (NULL == pf)
  {
    perror("fopen");
    return 1;
  }
  //struct S s = { 4,2.5f,"hello"};
  //写文件
  //fwrite(&s, sizeof(struct S), 1, pf);
  //读文件
  struct S s = { 0 };
  fread(&s, sizeof(struct S), 1, pf);
  //从pf指向的流中读取1个大小为sizeof(struct S)的数据存放到s中
  printf("%d %f %s\n", s.a, s.s, s.q);
  fclose(pf);
  pf = NULL;
  return 0;
}

从这个二进制文件中读取数据写到s中

六:scanf函数’族’与printf函数’族’的对比

scanf / fscanf / sscanf
printf / fprintf / sprintf
scanf:从标准输入流读取格式化的数据
printf:向标准输出流中写格式化的数据
fscanf:适用于所有输入流的格式化输入函数
fprintf:适用于所有输出流的格式化输出函数
sscanf:将格式化的数据,转换成字符串
sprintf:从字符串中读取格式化的数据

四:文件的随机读写

fseek和ftell和rewind

1:fseek和ftell和rewind总述

fseek
根据文件指针的位置和偏移量来定位文件指针
int fseek(FILE* stream, long int offset, int origin);
ftell
返回文件指针相对于起始位置的偏移量
long int ftell ( FILE * stream );
rewind
void rewind ( FILE * stream );
让文件指针的位置回到文件的起始位置
也可以用fseek函数
int fseek(FILE* stream, 0, SEEK_SET);
origin:用作偏移参考的位置,类似于物理中的参照物
使得我们所要填写得偏移量是以参照物的(起始地址/末尾地址)为原点,偏移offset个单位,offset可正可负
取值类型及含义:
  SEEK_SET  Beginning of file(文件的起始位置)(第一个数据的起始地址(即左侧))
  SEEK_CUR  Current position of the file pointer(文件指针目前所在的位置)
  SEEK_END  End of file(文件的末尾位置)(最后一个数据的末尾地址(即右侧))
例如:
我们目前文件中的数据为01234567
这么设置的好处是假设ftell返回的数值是point
那么就说明pf现在指向了point这个数据的左侧
例如:point==1
pf指向1的左侧
不过:如果指向7的右侧则point为8
起初:
文件指针pf指向0的左侧,即0这个数据的起始地址
我们想让pf改为指向4的左侧,即让它指向4的起始地址
1.fseek(pf,4,SEEK_SET)
offset的符号:
向右移动:+号(可省略)
向左移动:-号(不可省略)
2.fseek(pf,-4,SEEK_END)
注意:此时以文件末尾位置为参照物:
(文件的末尾位置)(最后一个数据的末尾地址(即右侧))
向左4个单位后指向4的左侧
3.假设此时pf指向4的左侧,我们想让pf指向1的右侧(即2的左侧)
fseek(pf,-2,SEEK_CUR)
移动文件指针即可
在此过程中,我们用ftell来看文件指针指向哪里,
注意:每次使用fseek定位文件指针时无需将其回复到文件的起始位置
最后使用rewind让文件指针的位置回到文件的起始位置(这里是为了介绍rewind这个函数)

2:fseek和ftell和rewind实例演示

此时我已经将文件data.txt中的内容改为了

int main()
{
  FILE* pf = fopen("data.txt", "r");
  if (pf == NULL)
  {
    perror("fopen");
    return 1;
  }
  //读文件
  int point = ftell(pf);
  printf("初始:pf:%d\n", point);
  fseek(pf, 4, SEEK_SET);
  point = ftell(pf);
  printf("第一种方案移动后:pf:%d\n", point);
  fseek(pf, -4, SEEK_END);
  point = ftell(pf);
  printf("第二种方案移动后:pf:%d\n", point);
  fseek(pf, -2, SEEK_CUR);
  point = ftell(pf);
  printf("第三种方案移动后:pf:%d\n", point);
  rewind(pf);
  point = ftell(pf);
  printf("使用rewind还原后:pf:%d\n", point);
  //关闭文件
  fclose(pf);
  pf = NULL;
  return 0;
}

五:文本文件和二进制文件

文本文件和二进制文件
根据数据的组织形式,数据文件被称为文本文件或者二进制文件。
1.数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。
2.如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文
本文件。
一个数据在内存中是怎么存储的呢?
字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。
如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而
二进制形式输出,则在磁盘上只占4个字节(VS2013测试)。

六:文件读取结束的判定

文件读取结束的判定
被错误使用的feof
牢记:在文件读取过程中,不能用feof函数的返回值直接来判断文件的是否结束。
feof 的作用是:当文件读取结束的时候,判断是读取结束的原因是否是:遇到文件尾结束。
1. 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
例如:
fgetc 判断是否为 EOF .
fgets 判断返回值是否为 NULL .
2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如:
fread判断返回值是否小于实际要读的个数。

七:文件缓冲区

文件缓冲区
1.ANSIC 标准采用“缓冲文件系统”处理的数据文件的.
2.所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。
3.从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。
4.如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。
5.缓冲区的大小是根据C编译系统决定的。
6.这里可以得出一个结论:
因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文
件。
如果不做,可能导致读写文件的问题。

其中,程序数据区,输入缓冲区,输出缓冲区都位于内存当中

以上就是C语言文件操作详解的全部内容,希望能对大家有所帮助,谢谢大家

相关文章
|
6天前
|
存储 编译器 C语言
关于文件操作---C语言
关于文件操作---C语言
|
6天前
|
存储 程序员 C语言
C语言-文件操作
C语言-文件操作
55 2
|
6天前
|
安全 算法 程序员
【C/C++ 文件操作】深入理解C语言中的文件锁定机制
【C/C++ 文件操作】深入理解C语言中的文件锁定机制
49 0
|
6天前
|
C语言
C语言文件操作
C语言文件操作
18 0
C语言文件操作
|
6天前
|
C语言
C语言文件操作
C语言文件操作
|
4天前
|
存储 程序员 C语言
C语言之详细讲解文件操作(抓住文件操作的奥秘)
C语言之详细讲解文件操作(抓住文件操作的奥秘)
10 0
|
6天前
|
存储 程序员 编译器
【C语言】深度探讨文件操作(一)
【C语言】深度探讨文件操作(一)
|
6天前
|
存储 程序员 C语言
C语言:文件操作
C语言:文件操作
12 1
|
6天前
|
存储 C语言 C++
【C语言】文件与文件操作
前言:我们通过学习的技术可以完成计算与字符串处理,但程序结束之后就都消失了,这样岂不可惜。我们通过文件与数据持久化保存相关的基础知识。
12 0
|
6天前
|
存储 编译器 C语言
C语言中的文件操作指南
C语言中的文件操作指南
14 0