【C语言】文件(FILE)

一、文件

        在程序运行时,常常需要将一些数据(运行的最终结果和中间数据)输出到磁盘上存放起来,以后需要时再从磁盘中输入到计算机内存。这就要用到磁盘文件。
C语言把文件看作是一个字符(字节)的序列,即一个一个字符(字节)的数据顺序组成。根据数据的组织形式,可分为ASCII文件和二进制文件。ASCII文件又称文本文件,它的每一个字节放一个ASCII代码,代表一个字符。二进制文件是把内存中的数据按其在内存中的存储形式原样输出到磁盘上存放。
        在过去使用的C版本(如UNIX系统下使用的C)有两种对文件的处理方法:一种叫“缓冲文件系统”,一种叫“非缓冲文件系统”。所谓缓冲文件系统是指系统自动地在内存中为每一个正在使用的文件名开辟一个缓冲区。从内存向磁盘输出数据必须先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘去。如果从磁盘向内存读入数据,则一次从磁盘文件将一批数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(给程序变量)。缓冲区的大小由各个具体的C版本确定,一般为512字节。

在这里插入图片描述
所谓“非缓冲文件系统”是指系统不自动开辟确定大小的缓冲区,而由程序为每个文件设定缓冲区。
UNIX系统下,用缓冲文件系统来处理文本文件,用非缓冲文件系统处理二进制文件。用缓冲文件系统进行的输入输出又称为高级(或高层)磁盘输入输出(高层I/O),用非缓冲文件系统进行的输入输出又称为低级(低层)输入输出系统。ANSI C标准决定不采用非缓冲文件系统,而只采用缓冲文件系统。即既用缓冲文件系统处理文本文件,也用它来处理二进制文件。也就是将缓冲文件系统扩充为可以处理二进制文件。
在C语言中,没有输入输出语句,对文件的读写都是用库函数来实现的。ANSI规定了标准输入输出函数,用它们对文件进行读写。

1.1 文件类型指针

        缓冲文件系统中,关键的概念是“文件指针”。每个被使用的文件都在内存中开辟一个区,用来存放文件的有关信息(如文件的名字、文件状态及文件当前位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是由系统定义的,取名为FILE。

1.2 文件的打开与关闭

1.2.1 文件的打开

在这里插入图片描述

#include <malloc.h>
#include <stdio.h>
// 主函数
int main(int argc, char *argv[]) // *argv[]:指针数组
{
  FILE *fp; // 文件指针
  fp = fopen("input.txt","r"); // 打开文件
  read/write fucntions // 读或者写函数
  fclose(fp); // 关闭文件指针

  return 0;
}

说明

(1)用“r”方式打开的文件只能用于向计算机输入而不能用作向该文件输出数据,而且该文件应该已经存在,不能用“r”方式打开一个并不存在的文件(即输入文件),否则出错。
(2)用“w”方式打开的文件只能用于向该文件写数据(即输出文件),而不能用来向计算机输入。如果原来不存在该文件,则在打开时新建立一个以指定的名字命名的文件。如果原来已存在一个以该文件名命名的文件,则在打开时将该文件删去,然后重新建立一个新文件。
(3)如果希望向文件末尾添加新的数据(不希望删除原有数据),则应该用“a”方式打开。但此时该文件必须已存在,否则将得到出错信息。打开时,位置指针移到文件末尾。
(4)用“r+”、“w+”、 “a+”方式打开的文件既可以用来输入数据,也可以用来输出数据。用“r+”方式时该文件应该已经存在,以便能向计算机输入数据。用“w+”方式则新建立一个文件,先向此文件写数据,然后可以读此文件中的数据。用“a+”方式打开的文件,原来的文件不被删去,位置指针移到文件末尾,可以添加,也可以读。
(5)如果不能实现“打开”的任务,fopen函数将会带回一个出错信息。出错的原因可能是用“r”方式打开一个并不存在的文件;磁盘出故障;磁盘已满无法建立新文件等。此时fopen函数将带回一个空指针值NULL。	
(6)在向计算机输入文本文件时,将回车换行符转换为一个换行符,在输出时把换行符转换成为回车和换行两个字符。在用二进制文件时,不进行这种转换,在内存中的数据形式与输出到外部文件中的数据形式完全一致,一一对应。
(7)在程序开始运行时,系统自动打开3个标准文件:标准输入、标准输出、标准出错输出。这3个文件都与终端相联系。因此以前所用到的从终端输入或输出都不需要打开终端文件。系统自动定义了3个文件指针stdin、stdout和stderr,分别指向终端输入、终端输出和标准出错输出(也从终端输出)。如果程序中指定要从stdin所指的文件输入数据,就是指从终端键盘输入数据。

1.2.2 文件的关闭

在使用完一个文件后应该关闭它,以防止它再被误用。“关闭”就是使文件指针变量不指向该文件,也就是文件指针变量与文件“脱钩”,以后不能再通过该指针对原来与其相联系的文件进行读写操作。除非再次打开,使该指针变量重新指向该文件。

1.3 文件的读写

1.3.1 文本文件的读写

1.3.1.1 写字符函数fputc和读字符函数fgetc

1、从键盘输入一些字符,逐个把它们送到磁盘上去,直到输入一个“#”为止

#include <stdio.h>
#include <stdlib.h>

// 主函数
int main(int argc, char *argv[]) // *argv[]:指针数组
{

  FILE *fp;
  fp = fopen("input.txt", "r");
  if (fp == NULL)
  {
    exit(1);
  }

  char c;
  c = fgetc(fp);
  
  while (c != EOF)
  {
    putchar(c);
    c = fgetc(fp);
  }

  return 0;
}
#include <stdio.h>
#include <stdlib.h>

// 主函数
int main(int argc, char *argv[]) // *argv[]:指针数组
{

  FILE *fp;
  char filename[10]; //文件名
  printf("input file name: ");
  scanf("%s", &filename);
  

  if ((fp = fopen(filename, "w")) == NULL)
  {
    exit(1);
  }

  
  fflush(stdin); // 将缓冲区内的数据写回参数stream指定的文件中
  printf("input character\n");
  char c;
  c = getchar(); // 输入字符
  while (c != '#') // 判别
  {
    fputc(c, fp);
    c = getchar();
  }

  return 0;
}

在这里插入图片描述

2、 将一个磁盘文件中的信息复制到另一个磁盘文件中

#include <stdio.h>
#include <stdlib.h>

// 主函数
int main(int argc, char *argv[]) // *argv[]:指针数组
{

  FILE *in, *out;
  char infile[10]; //文件名
  char outfile[10];

  printf("enter the infile name:\n");
  scanf("%s", infile);

  printf("enter the outfile name:\n");
  scanf("%s", outfile);

  if ((in = fopen("input.txt", "rb")) == NULL)
    exit(0);

  if ((out = fopen("out.txt", "wb")) == NULL)
    exit(0);

  char c;
  c = fgetc(in);
  while (!feof(in)) // 判断文件是否结果
  {                // 判断文件不为空
    fputc(c, out); // 写入到out.txt文件中
    c = fgetc(in); // 循环读取下一个字符
  }
  

  fclose(in);
  fclose(out);

  return 0;
}

1.3.1. 2 写字符串函数fputs和读字符串函数fgets

1、将学生数据,由键盘输入并存储到磁盘文件中

#include <stdio.h>
#include <stdlib.h>

// 主函数
int main(int argc, char *argv[]) // *argv[]:指针数组
{

  FILE *fp;
  char student[50]; // 创建字符数组-存储输入的字符串

  printf("input filename:");
  scanf("%s", student);

  if ((fp = fopen("input.txt", "w")) == NULL)
  {
    exit(1);
  }

  for (int i = 0; i < 3; i++)
  {
    gets(student); // 获取输入的字符串
    fputs(student, fp);// 存储到文件fp
    fputs("\n", fp);

  }

  fclose(fp);
  return 0;
}

2、从上例文件中读取学生数据,并显示在屏幕上

#include <stdio.h>
#include <stdlib.h>

// 主函数
int main(int argc, char *argv[]) // *argv[]:指针数组
{

  FILE *fp;
  if ((fp = fopen("input.txt", "r")) == NULL)
    exit(0);

  char string[80];
  while (fgets(string, 80, fp) != NULL)
  {
    printf("%s", string);
  }

  fclose(fp);
  return 0;
}

1.3.1.3 格式化写函数fprintf和格式化读函数fscanf

1、将学生数据,由键盘输入并存储到磁盘文件中

#include <stdio.h>
#include <stdlib.h>

// 主函数
int main(int argc, char *argv[]) // *argv[]:指针数组
{

  FILE *fp;
  if ((fp = fopen("input.txt", "w")) == NULL)
    exit(0);

  long num;
  char name[10];
  int age;

  for (int i = 0; i < 5; i++) {

    printf("第%d个学生:\n", i + 1);
    printf("学号:\n");
    scanf("%ld", &num);

    printf("姓名:\n");
    scanf("%s", &name);

    printf("年龄:\n");
    scanf("%d", &age);

    fprintf(fp, "学号:%ld 姓名:%9s 年龄:%d\n", num, name, age);
  }

  fclose(fp);

  return 0;
}

2、从上例文件中读取学生数据,并显示在屏幕上

#include <stdio.h>
#include <stdlib.h>

// 主函数
int main(int argc, char *argv[]) {
  FILE *fp;

  if ((fp = fopen("student.txt", "r")) == NULL)
    exit(0);

  long num;
  char name[10];
  int age;

  while (!feof(fp)) {

    // fprintf(fp, "学号:%ld 姓名:%9s 年龄:%d\n", num, name, age);
    fscanf(fp, "学号:%ld 姓名:%9s 年龄:%d\n", &num, name, &age);
    printf("%ld %s %d\n", num, name, age);
  }

  fclose(fp);

  return 0;
}

1.3.2 二进制文件的读写

1.3.1 fread函数和fwrite函数

从键盘输入4个学生数据,然后把它们转存到磁盘文件上去,然后再从磁盘文件中一次性读入内存并显示出来

#include <stdio.h>
#include <stdlib.h>

struct student
{
  long num;
  char name[10];
  int age;
} s[4];

// 主函数
int main(int argc, char *argv[])
{
  FILE *fp;

  if ((fp = fopen("student.dat", "w")) == NULL)
    exit(0);

  for (int i = 0; i < 4; i++)
  {
    printf("第%d个学生:\n", i + 1);
    printf("学号:\n");
    scanf("%ld", &s[i].num);
    printf("姓名:\n");
    scanf("%s", s[i].name);
    printf("年龄:\n");
    scanf("%d", &s[i].age);
  }

  fwrite(s, sizeof(struct student), 4, fp);

  rewind(fp);
  // for (int i = 0; i < 4; i++)
  // {
  //   fread(&s, sizeof(struct student), 1, fp);
  //   printf("%ld %s %d\n", s->num, s->name, s->age);
  // }

  fread(s, sizeof(struct student), 4, fp);
  for (int i = 0; i < 4; i++)
  {
    printf("%ld %s %d\n", s[i].num, s[i].name, s[i].age);
  }

  fclose(fp);
  return 0;
}

1.3.3 文件的定位

文件中有一个位置指针,指向当前读写的位置。如果顺序读写一个文件,每次读写一个字符,则读写完一个字符后,该位置指针自动移动指向下一个字符位置。如果想改变这样的规律,强制使位置指针指向其他指定的位置,可以用有关函数。

1.3.3.1 rewind()函数

rewind函数的作用是使位置指针重新返回文件的开头,此函数没有返回值。
有一个磁盘文件,第一次将它的内容显示在屏幕上,第二次把它复制到另一文件上

/*
 * @Author: jjk
 * @Date: 2019-03-31 10:02:20
 * @Last Modified by: jjk
 * @Last Modified time: 2019-04-02 13:15:13
 */

#include <stdio.h>
#include <stdlib.h>

struct student
{
  long num;
  char name[10];
  int age;
} s[4];

// 主函数
int main(int argc, char *argv[])
{
  FILE *fp;

  if ((fp = fopen("input.txt", "r")) == NULL)
    exit(0);

  char c;
  c = fgetc(fp);
  while (!feof(fp))
  {

    printf("%c", c);
    c = fgetc(fp);
  }

  FILE *fp2;
  if ((fp2 = fopen("student2.txt", "w")) == NULL)
    exit(0);

  rewind(fp); // 重置文件指针

  c = fgetc(fp);
  while (!feof(fp))
  {
    fputc(c, fp2);
    c = fgetc(fp);
  }

  fclose(fp);
  fclose(fp2);

  fclose(fp);
  return 0;
}

1.3.3.2 fseek()函数和随机读写

对流式文件可以进行顺序读写,也可以进行随机读写,关键在于控制文件的位置指针。如果位置指针是按字节位置顺序移动的,就是顺序读写;如果能将位置指针按需要移动到任意位置,就可以实现随机读写。所谓随机读写,是指读写完上一个字符(字节)后,并不一定要读写其后的字符(字节),而可以读写文件中任意位置上所需要的字符(字节)。

	用fseek函数可以实现改变文件的位置指针。
	在磁盘文件上存有10个学生的数据。要求将第1、3、5、7、9个学生数据输入计算机,并在屏幕上显示出来
/*
 * @Author: jjk
 * @Date: 2019-03-31 10:02:20
 * @Last Modified by: jjk
 * @Last Modified time: 2019-04-02 13:15:13
 */

#include <stdio.h>
#include <stdlib.h>

struct student
{
  long num;
  char name[10];
  int age;
} s[10];

// 主函数
int main(int argc, char *argv[])
{

  FILE *fp;

  if ((fp = fopen("student.dat", "w+")) == NULL)
    exit(0);

  for (int i = 0; i < 10; i++)
  {
    printf("第%d个学生:\n", i + 1);
    printf("学号:\n");
    scanf("%ld", &s[i].num);
    printf("姓名:\n");
    scanf("%s", s[i].name);
    printf("年龄:\n");
    scanf("%d", &s[i].age);
  }

  fwrite(s, sizeof(struct student), 10, fp);

  struct student stu;

  for (int i = 1; i <= 9; i = i + 2)
  {
    fseek(fp, (i - 1) * sizeof(struct student), SEEK_SET);
    fread(&stu, sizeof(struct student), 1, fp);
    printf("%ld %s %d\n", stu.num, stu.name, stu.age);
  }

  fclose(fp);

  return 0;
}

在这里插入图片描述

1.3.3.3 ftell()函数

ftell函数的作用是得到流失文件中的当前位置,用相对于文件开头的位移量来表示。由于文件中的位置指针经常移动,往往不容易知道其当前位置。用ftell函数可以得到当前位置。

#include <stdio.h>
#include <stdlib.h>

// 主函数
int main(int argc, char *argv[])
{

  FILE *stream;

  long position;
  char list[100];
  if ((stream = fopen("7-4-1.avi", "rb")) != NULL) // 二进制文件
  {
    // Move the pointer by reading data:
    fread(list, sizeof(char), 100, stream);
    // Get position after read:
    position = ftell(stream); // 获取当前指针位置
    printf("Position after trying to read 100 bytes: %ld\n", position);
    fclose(stream);
  
  }

  fclose(stream);

  return 0;
}


1.4 文件的检测

1.4.1 ferror()函数

1.4.2 clearerr函数

#include <stdio.h>
#include <stdlib.h>

// 主函数
int main(int argc, char *argv[]) {

  int count, total = 0;
  char buffer[100];
  FILE *stream;

  long position;
  char list[100];

  if ((stream = fopen("input.txt", "r")) == NULL)
    exit(1);

  int c;
  putc('c', stdin); //标准写入
  if (ferror(stdin)) {
    perror("Write error");
    //clearerr(stdin); // 清除这个错误:标准的输入文件处于正常状态
  }

  
  /* See if read causes an error. */
  printf("Will input cause an error? ");
  c = getc(stdin);
  if (ferror(stdin)) {
    perror("Read error");
    clearerr(stdin);
  }

  fclose(stream);

  return 0;
}

在这里插入图片描述

1.5 总结

在这里插入图片描述

发布了213 篇原创文章 · 获赞 303 · 访问量 49万+

猜你喜欢

转载自blog.csdn.net/Jiajikang_jjk/article/details/89041324