【《C Primer Plus》读书笔记】第13章:文件输入/输出
- 13.1 与文件进行通信
 - 13.1.1 文件是什么
 - 13.1.2 文本模式和二进制模式
 - 13.1.3 I/O的级别
 - 13.1.4 标准文件
 
- 13.2 标准I/O
 - 13.3 一个简单的文件压缩程序
 - 13.4 文件I/O:fprintf()、fscanf()、fgets()和fputs()
 - 13.5 随机访问:fseek()和ftell()
 - C 库函数 - fseek()
 - C 库函数 - ftell()
 - reverse.c
 
13.1 与文件进行通信
13.1.1 文件是什么
文件通常是在磁盘或固态硬盘上的一段已命名的存储区。
C把文件看作是一系列连续的字节,每个字节都能被单独读取。
C提供两种文件模式:文本模式和二进制模式。
13.1.2 文本模式和二进制模式
所有文件的内容(在磁盘上)都以二进制(0或1)表示。
因为编码方式不同,导致展现的形式不同。
定义:
- 如果文件使用二进制编码的字符,如Unicode,ASCII表示文本,那么就是文本文件。
 - 如果二进制代表的是机器语言,或数值数据,或图片,或音乐编码,该文件就是二进制文件。
 
区别:
-  
对于字符串中
\n的处理不同。
在Windows系统中用文本模式打开文件时会发现文件中的换行符是\n,这看起来再正常不过了,但当你改为用二进制模式打开文件时会发现文件中的换行符变为\r\n。
事实上,在Windows系统中的的文件中的换行符都是\r\n,只不过在使用文本模式时,会把文件内容从文本的系统表示法映射为C表示法,而使用二进制表示法时不会进行映射转换。 -  
以二进制模式打开文件是,可以逐字节读取文件。
这也就是为什么在使用部分函数例如fseek()和ftell()时必须以二进制模式打开文件 -  
使用文本模式打开文件时,就要用fprintf()写入数据,使用二进制模式打开文件时候,就要用fwrite()写入数据。
这是因为fprintf()类型的函数会将数据转换为文本,而fwrite()这类函数则不会。 
为了规范文本文件的处理,C提供两种访问文件的途径:文本模式和二进制模式。
- 在二进制模式中,程序可以访问文件的每个字节。
 - 在文本模式中,程序所见的内容与文件实际内容不同。典型代表就是对于字符串中
\n的处理。 
13.1.3 I/O的级别
- 底层I/O:使用操作系统的基本I/O服务。
 - 标准高级I/O:使用C库的标准包和stdio.h头文件定义。
 
C标准只支持标准I/O包。有些实现会提供底层库,但C标准建立了可移植的I/O模型。
13.1.4 标准文件
C程序会自动打开3个文件:标准输入、标准输出、标准错误输出。
标准输入是系统的普通输入设备,通常是键盘。
标准输出、标准错误输出是系统的普通输出设备,通常是显示屏。
标准错误输出提供了一个从逻辑上不同的地方来发送错误的信息。
13.2 标准I/O
标准I/O由ANSI C标准定义,C标准中定义了C库,标准I/O就是C库中用来输入和输出的函数。标准I/O在系统调用的上一层多加了一个缓冲区,通过缓冲机制减少了系统调用,实现更高的效率。
标准I/O包的3个好处:
- 可移植
 - 标准I/O有许多专门的函数简化了处理不同I/O的问题
 - 输入和输出都是缓冲的
 
程序清单13.1 count.c程序
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    int ch;
    FILE *fp;
    unsigned count = 0;
    if (argc != 2)
    {
        printf("Usage: %s filename\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    if ((fp = fopen(argv[1], "r")) == NULL)
    {
        printf("Can't open %s\n", argv[1]);
        exit(EXIT_FAILURE);
    }
    while ((ch = getc(fp)) != EOF)
    {
        putc(ch, stdout);
        count++;
    }
    fclose(fp);
    printf("File %s has %lu characters\n", argv[1], count);
    system("pause");
    return 0;
}
 
13.3 一个简单的文件压缩程序
程序清单13.2 reducto.c程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEN 40
int main(int argc, char *argv[])
{
    FILE *in, *out;
    int ch;
    char name[LEN];
    int count = 0;
    if (argc < 2)
    {
        fprintf(stderr, "Usage: %s filename\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    if ((in = fopen(argv[1], "r")) == NULL)
    {
        fprintf(stderr, "I couldn't open the file \"%s\"\n", argv[1]);
        exit(EXIT_FAILURE);
    }
    strncpy(name, argv[1], LEN - 5);
    name[LEN - 5] = '\0';
    strcat(name, ".red");
    if ((out = fopen(name, "w")) == NULL)
    {
        fprintf(stderr, "Can't create output file.\n");
        exit(3);
    }
    while ((ch = getc(in)) != EOF)
        if (count++ % 3 == 0)
            putc(ch, out);
    if (fclose(in) != 0 || fclose(out) != 0)
        fprintf(stderr, "Error in closing files\n");
    
    system("pause");
    return 0;
}
 
运行结果:


13.4 文件I/O:fprintf()、fscanf()、fgets()和fputs()
程序清单13.3 addword.c程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 41
int main(void)
{
    FILE *fp;
    char words[MAX];
    if ((fp = fopen("wordy", "a+")) == NULL)
    {
        fprintf(stderr, "Can't open \"wordy\" file.\n");
        exit(EXIT_FAILURE);
    }
    puts("Enter words to add to the file; press the #");
    puts("key at the beginning of a line to terminate.");
    while ((fscanf(stdin, "%40s", words) == 1) && (words[0] != '#'))
        fprintf(fp, "%s\n", words);
    
    puts("File contents:");
    rewind(fp); /*返回到文件开始处*/
    while (fscanf(fp, "%s", words) == 1)
        puts(words);
    puts("Done!");
    if (fclose(fp) != 0)
        fprintf(stderr, "Error closing file\n");
    
    system("pause");
    return 0;
}
 
运行结果:


13.5 随机访问:fseek()和ftell()
C 库函数 - fseek()
描述:
 C 库函数 int fseek(FILE *stream, long int offset, int whence) 设置流 stream 的文件位置为给定的偏移 offset,参数 offset 意味着从给定的 whence 位置查找的字节数。
声明:
 下面是 fseek() 函数的声明。
int fseek(FILE *stream, long int offset, int whence)
 
参数:
- stream – 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
 - offset – 这是相对 whence 的偏移量,以字节为单位。
 - whence – 这是表示开始添加偏移 offset 的位置。它一般指定为下列常量之一:
 
| 常量 | 描述 | 
|---|---|
| SEEK_SET | 文件的开头 | 
| SEEK_CUR | 文件指针的当前位置 | 
| SEEK_END | 文件的末尾 | 
返回值:
 如果成功,则该函数返回零,否则返回非零值。
实例:
 下面的实例演示了 fseek() 函数的用法。
#include <stdio.h>
int main ()
{
   FILE *fp;
   fp = fopen("file.txt","w+");
   fputs("This is runoob.com", fp);
  
   fseek( fp, 7, SEEK_SET );
   fputs(" C Programming Langauge", fp);
   fclose(fp);
   
   return(0);
}
 
让我们编译并运行上面的程序,这将创建文件 file.txt,它的内容如下。最初程序创建文件和写入 This is runoob.com,但是之后我们在第七个位置重置了写指针,并使用 puts() 语句来重写文件,内容如下:
This is C Programming Langauge
 
现在让我们使用下面的程序查看上面文件的内容:
#include <stdio.h>
int main ()
{
   FILE *fp;
   int c;
   fp = fopen("file.txt","r");
   while(1)
   {
      c = fgetc(fp);
      if( feof(fp) )
      {
          break ;
      }
      printf("%c", c);
   }
   fclose(fp);
   return(0);
}
 
运行结果:

C 库函数 - ftell()
描述:
 C 库函数 long int ftell(FILE *stream) 返回给定流 stream 的当前文件位置,即:参数指向文件的当前位置距文件开始处的字节数。
声明:
 下面是 ftell() 函数的声明。
long int ftell(FILE *stream)
 
参数:
 stream – 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
返回值:
 该函数返回位置标识符的当前值。如果发生错误,则返回 -1L,全局变量 errno 被设置为一个正值。
 返回类型为long。
实例:
 下面的实例演示了 ftell() 函数的用法。
#include <stdio.h>
int main ()
{
   FILE *fp;
   int len;
   fp = fopen("file.txt", "r");
   if( fp == NULL ) 
   {
      perror ("打开文件错误");
      return(-1);
   }
   fseek(fp, 0, SEEK_END);
   len = ftell(fp);
   fclose(fp);
   printf("file.txt 的总大小 = %d 字节\n", len);
   
   return(0);
}
 
假设我们有一个文本文件 file.txt,它的内容如下:
This is runoob.com
 
让我们编译并运行上面的程序,如果文件内容如上所示,这将产生以下结果,否则会根据文件内容给出不同的结果:
file.txt 的总大小 = 18 字节
运行结果:

reverse.c
程序清单13.4 reverse.c
逆序打印文件内容。
代码:
#include <stdio.h>
#include <stdlib.h>
#define CNTL_Z '\032' /* eof marker in DOS text files */
#define SLEN 81
int main(void)
{
    char file[SLEN];
    char ch;
    FILE *fp;
    long count, last;
    puts("Enter the name of the file to be processed:");
    scanf("%80s", file);
    if ((fp = fopen(file, "rb")) == NULL)
    { /* read-only mode   */
        printf("reverse can't open %s\n", file);
        exit(EXIT_FAILURE);
    }
    fseek(fp, 0L, SEEK_END); /* go to end of file */
    last = ftell(fp);
    for (count = 1L; count <= last; count++)
    {
        fseek(fp, -count, SEEK_END); /* go backward      */
        ch = getc(fp);
        if (ch != CNTL_Z && ch != '\r') /* MS-DOS files */
            putchar(ch);
    }
    putchar('\n');
    fclose(fp);
    return 0;
}
 
文件内容:

运行结果:



![[计算机网络(第八版)]第一章 概述(学习笔记)](https://img-blog.csdnimg.cn/82bb51e12f6f4978949e1f0594fc9c8d.png)
















