文件I/O库函数与控制台I/O函数非常相似。实际上,大多数控制台I/O函数可以被视为文件I/O函数的特殊情况。库函数包括:

  • fopen():创建或打开文件进行读写。
  • fclose():在读写操作后关闭文件。
  • fseek():定位文件中的特定位置。
  • rewind():将文件指针重置到文件开头并保持文件打开。
  • rename():重命名文件。
  • remove():删除文件。
  • fprintf():格式化写入文件。
  • fscanf():格式化读取文件。
  • fwrite():非格式化写入文件。
  • fread():非格式化读取文件。
  • putc():向文件写入一个字节。
  • getc():从文件读取一个字节。
  • fputs():向文件写入一个字符串。
  • fgets():从文件读取一个字符串。

所有这些库函数依赖于stdio.h头文件中的定义,因此需要声明:

#include <stdio.h>

C语言文档通常将这些函数称为执行“流I/O”而不是“文件I/O”。之所以有这个区别,是因为这些函数同样可以处理通过调制解调器传输的数据,而不仅仅是文件,因此使用了更通用的术语“数据流”而不是“文件”。然而,为了简便起见,本文件仍然使用“文件”这一术语。


文件I/O函数

fopen()函数用于打开并(如果需要)创建文件。其语法如下:

<file pointer> = fopen(<filename>, <access mode>);

fopen()函数返回一个“文件指针”,声明如下:

FILE *<file pointer>;

如果发生错误,文件指针将返回NULLNULLstdio.h中定义。access mode(访问模式)有以下几种:

  • r:以只读模式打开文件。
  • w:以写入模式打开并清空(或创建)文件。
  • a:以追加模式打开(或创建)文件,写入内容追加到文件末尾。
  • r+:以读写模式打开文件。
  • w+:以读写模式打开并清空(或创建)文件。
  • a+:以读写模式打开文件并追加内容。

filename只是一个字符串,表示文件名。

通常,使用相同的语句来与文件或标准I/O进行通信是有用的。为此,stdio.h头文件中包括了预定义的文件指针,分别为stdinstdout。不需要对它们调用fopen(),可以直接将它们分配给文件指针:

fpin = stdin;
fpout = stdout;

随后任何文件I/O操作都不会知道这是标准输入或标准输出。


关闭文件

fclose()函数用于关闭由文件指针参数指定的文件。它的语法是:

fclose(fp);

定位文件

fseek()函数允许选择文件中的字节位置进行读写。它的语法是:

fseek(<file_pointer>, <offset>, <origin>);

offset是一个long类型,指定文件中的偏移量(单位:字节)。origin是一个int类型,表示文件位置的起始点,它有以下三种标准值,在stdio.h中定义:

  • SEEK_SET:文件开头。
  • SEEK_CUR:当前位置。
  • SEEK_END:文件末尾。

fseek()函数成功时返回0,失败时返回非零值。


重置文件、重命名文件和删除文件

rewind()rename()remove()函数的使用非常简单:

  • rewind()函数将打开的文件重置到文件开头,并保持文件打开。语法是:

    rewind(<file_pointer>);
    
  • rename()函数用于更改文件名:

    rename(<old_file_name_string>, <new_file_name_string>);
    
  • remove()函数用于删除文件:

    remove(<file_name_string>);
    

格式化输出到文件

fprintf()函数允许将格式化的ASCII数据输出到文件,语法是:

fprintf(<file pointer>, <string>, <variable list>);

fprintf()函数的语法与printf()相同,唯一的区别是多了一个文件指针参数。例如,在以下程序中,fprintf()调用将格式化输出数据到文件:

/* fprpi.c */
#include <stdio.h>

void main()
{
  int n1 = 16;
  float n2 = 3.141592654f;
  FILE *fp;

  fp = fopen("data", "w");
  fprintf(fp, "  %d   %f", n1, n2);
  fclose(fp);
}

该程序将存储以下ASCII数据:

16   3.14159

格式化代码与printf()相同:

  • %d:十进制整数
  • %ld:长整型十进制整数
  • %c:字符
  • %s:字符串
  • %e:科学记数法表示的浮点数
  • %f:十进制表示的浮点数
  • %g:根据需要选择%e%f
  • %u:无符号十进制整数
  • %o:无符号八进制整数
  • %x:无符号十六进制整数

还可以使用字段宽度说明符。fprintf()函数返回写入文件的字符数,或者在出现错误时返回负数。

fscanf()函数与fprintf()的关系就像scanf()printf()的关系:它将格式化的ASCII数据读取到一个变量列表中。其语法如下:

fscanf(<file pointer>, <string>, <variable list>);

然而,string只包含格式代码,没有文本内容,variable list则包含变量的地址,而不是变量本身。例如,下面的程序读取了上一个例子中通过fprintf()存储的两个数字:

/* frdata.c */

#include <stdio.h>

void main()
{
  int n1;
  float n2;
  FILE *fp;

  fp = fopen("data", "r");
  fscanf(fp, "%d %f", &n1, &n2);
  printf("%d %f", n1, n2);
  fclose(fp);
}

fscanf()函数使用与fprintf()相同的格式代码,且有一些常见的例外:

  • 没有%g格式代码。
  • %f%e格式代码表现一致。
  • 有一个%h格式代码用于读取短整型整数。
  • 当然,也可以使用数字修饰符。

fscanf()函数返回它成功读取的项目数,如果遇到文件末尾或错误,则返回EOF(一个int类型)。

以下程序展示了fprintf()fscanf()的使用:

/* fprsc.c */

#include <stdio.h>

void main()
{
  int ctr, i[3], n1 = 16, n2 = 256;
  float f[4], n3 = 3.141592654f;
  FILE *fp;

  fp = fopen("data", "w+");

  /* 写入数据:十进制整数格式
                 十进制、八进制、十六进制整数格式
                 浮点数格式 */

  fprintf(fp, "%d %10d %-10d \n", n1, n1, n1);
  fprintf(fp, "%d %o %x \n", n2, n2, n2);
  fprintf(fp, "%f %10.10f %e %5.4e \n", n3, n3, n3, n3);

  /* 重置文件指针。 */

  rewind(fp);

  /* 读取数据。 */

  puts("");
  fscanf(fp, "%d %d %d", &i[0], &i[1], &i[2]);
  printf("   %d\t%d\t%d\n", i[0], i[1], i[2]);
  fscanf(fp, "%d %o %x", &i[0], &i[1], &i[2]);
  printf("   %d\t%d\t%d\n", i[0], i[1], i[2]);
  fscanf(fp, "%f %f %f %f", &f[0], &f[1], &f[2], &f[3]);
  printf("   %f\t%f\t%f\t%f\n", f[0], f[1], f[2], f[3]);

  fclose(fp);
}

该程序生成以下输出:

16         16         16
256        256        256
3.141593   3.141593   3.141593   3.141600

fwrite()fread()

fwrite()fread()函数用于二进制文件I/O。fwrite()的语法如下:

fwrite(<array_pointer>, <element_size>, <count>, <file_pointer>);

array_pointer的类型是void,因此数组可以是任何类型。element_sizecount分别表示每个数组元素的字节数和数组中元素的数量,它们的类型是size_t,即与unsigned int等价。

fread()函数的语法类似:

fread(<array_pointer>, <element_size>, <count>, <file_pointer>);

fread()函数返回它实际读取的项目数。

以下程序将一个数据数组存储到文件中,然后使用fwrite()fread()读取回来:

/* fwrrd.c */

#include <stdio.h>
#include <math.h>

#define SIZE 20

void main()
{
  int n;
  float d[SIZE];
  FILE *fp;

  for (n = 0; n < SIZE; ++n)  /* 填充数组,计算平方根。 */
  {
    d[n] = (float)sqrt((double)n);
  }

  fp = fopen("data", "w+");  /* 打开文件。 */
  fwrite(d, sizeof(float), SIZE, fp);  /* 将数据写入文件。 */
  rewind(fp);  /* 重置文件指针。 */
  fread(d, sizeof(float), SIZE, fp);  /* 读取数据。 */

  for (n = 0; n < SIZE; ++n)  /* 打印数组。 */
  {
    printf("%d: %7.3f\n", n, d[n]);
  }

  fclose(fp);  /* 关闭文件。 */
}

字符操作:putc()getc()

putc()函数用于向打开的文件写入一个字符。其语法如下:

putc(<character>, <file pointer>);

getc()函数用于从打开的文件中读取一个字符。其语法如下:

<character variable> = getc(<file pointer>);

getc()函数在发生错误时返回EOF。控制台I/O函数putchar()getchar()实际上是putc()getc()的特殊情况,它们默认使用标准输出和标准输入。


写入与读取字符串:fputs()fgets()

fputs()函数将一个字符串写入文件。其语法是:

fputs(<string / character array>, <file pointer>);

如果发生错误,fputs()函数将返回EOF。例如:

fputs("This is a test", fptr);

fgets()函数从文件中读取一个字符串。其语法是:

fgets(<string>, <max_string_length>, <file_pointer>);

fgets()函数会读取字符串,直到遇到换行符或读取到<string_length-1>个字符为止。发生错误时,它将返回NULL

以下示例程序通过fgets()fputs()将一个文件的内容复制到另一个文件:

/* fcopy.c */

#include <stdio.h>

#define MAX 256

void main()
{
  FILE *src, *dst;
  char b[MAX];

  /* 尝试打开源文件和目标文件。 */

  if ((src = fopen("infile.txt", "r")) == NULL)
  {
    puts("Can't open input file.");
    exit();
  }

  if ((dst = fopen("outfile.txt", "w")) == NULL)
  {
    puts("Can't open output file.");
    fclose(src);
    exit();
  }

  /* 将一个文件内容复制到另一个文件。 */

  while ((fgets(b, MAX, src)) != NULL)
  {
    fputs(b, dst);
  }

  /* 完成,关闭文件。 */
  
  fclose(src);
  fclose(dst);
}

这段程序打开一个文件并将其内容复制到另一个文件。

Last modified: Monday, 27 January 2025, 11:52 PM