欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

C语言的文件操作:重新理解与演绎

最编程 2024-08-13 10:22:06
...

C语言 文件操作

文件概述

一、磁盘文件和设备文件

1、磁盘文件

指一组相关数据的有序集合,通常存储在外部介质(如磁盘)上,使用时才调入内存。

2、设备文件

在操作系统中把每一个与主机相连的输入、输出设备看作是一个文件,把它们的输入、输出等同于对磁盘文件的读和写。

二、磁盘文件的分类

计算机的存储在物理上是二进制的,所以物理上所有的磁盘文件本质上都是一样的:以字节为单位进行顺序存储。

 从用户或者操作系统使用的角度(逻辑上)把文件分为:

  • 文本文件:基于字符编码的文件
  • 二进制文件:基于值编码的文件

三、文本文件和二进制文件

1、文本文件

  • 基于字符编码,常见编码有ASCII、UNICODE等
  • 一般可以使用文本编辑器直接打开

数5678的以ASCII存储形式(ASCII码)为:

00110101 00110110 00110111 00111000

2、二进制文件

  • 基于值编码,自己根据具体应用,指定某个值是什么意思
  • 把内存中的数据按其在内存中的存储形式原样输出到磁盘上

 数5678的存储形式(二进制码)为:

00010110 00101110

文件打开和关闭

一、文件指针

在C语言中用一个指针变量指向一个文件,这个指针称为文件指针

typedef struct
{
       short           level;       //缓冲区"满"或者"空"的程度
       unsigned        flags;       //文件状态标志
       char            fd;          //文件描述符
       unsigned char   hold;        //如无缓冲区不读取字符
       short           bsize;       //缓冲区的大小
       unsigned char   *buffer;     //数据缓冲区的位置
       unsigned        ar;          //指针,当前的指向
       unsigned        istemp;      //临时文件,指示器
       short           token;       //用于有效性的检查
}FILE;

FILE是系统使用typedef定义出来的有关文件信息的一种结构体类型,结构中含有文件名、文件状态和文件当前位置等信息。

声明FILE结构体类型的信息包含在头文件“stdio.h”中,一般设置一个指向FILE类型变量的指针变量,然后通过它来引用这些FILE类型变量。通过文件指针就可对它所指的文件进行各种操作。

C语言中有三个特殊的文件指针由系统默认打开,用户无需定义即可直接使用:

  • stdin: 标准输入,默认为当前终端(键盘),我们使用的scanf、getchar函数默认从此终端获得数据。
  • stdout:标准输出,默认为当前终端(屏幕),我们使用的printf、puts函数默认输出信息到此终端。
  • stderr:标准出错,默认为当前终端(屏幕),我们使用的perror函数默认输出信息到此终端。

二、文件打开

1、说明

任何文件使用之前必须打开:

#include <stdio.h>
FILE * fopen(const char * filename, const char * mode);

功能:打开文件
参数:

  • filename:需要打开的文件名,根据需要加上路径
  • mode:打开文件的模式设置

返回值:

  • 成功:文件指针
  • 失败:NULL

2、第一个参数的几种形式

FILE *fp_passwd = NULL;
 
//相对路径:
//打开当前目录passdw文件:源文件(源程序)所在目录
FILE *fp_passwd = fopen("passwd.txt", "r");
      
//打开当前目录(test)下passwd.txt文件
fp_passwd = fopen(". / test / passwd.txt", "r");
      
//打开当前目录上一级目录(相对当前目录)passwd.txt文件
fp_passwd = fopen(".. / passwd.txt", "r");
             
//绝对路径:
//打开C盘test目录下一个叫passwd.txt文件
fp_passwd = fopen("c://test//passwd.txt","r");

3、第二个参数的几种形式(打开文件的方式)

注意事项:

  • b是二进制模式的意思,b只是在Windows有效,在Linux用r和rb的结果是一样的
  • Unix和Linux下所有的文本文件行都是\n结尾,而Windows所有的文本文件行都是\r\n结尾
  • 在Windows平台下,以“文本”方式打开文件,不加b:
  • 当读取文件的时候,系统会将所有的 "\r\n" 转换成 "\n"
  • 当写入文件的时候,系统会将 "\n" 转换成 "\r\n" 写入
  • 以"二进制"方式打开文件,则读\写都不会进行这样的转换
  • 在Unix/Linux平台下,“文本”与“二进制”模式没有区别,"\r\n" 作为两个字符原样输入输出

打开模式

含义

r或rb

以只读方式打开一个文本文件(不创建文件,若文件不存在则报错)

w或wb

以写方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件)

a或ab

以追加方式打开文件,在末尾添加内容,若文件不存在则创建文件

r+或rb+

以可读、可写的方式打开文件(不创建新文件)

r+或rb+

以可读、可写的方式打开文件(不创建新文件)

w+或wb+

以可读、可写的方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件)

a+或ab+

以添加方式打开文件,打开文件并在末尾更改文件,若文件不存在则创建文件

4、案例

#include <stdio.h>

int main(void)
{
       FILE *fp = NULL;
 
       // "\\"这样的路径形式,只能在windows使用
       // "/"这样的路径形式,windows和linux平台下都可用,建议使用这种
       // 路径可以是相对路径,也可是绝对路径
       fp = fopen("../test", "w");
       //fp = fopen("..\\test", "w");
 
       if (fp == NULL) //返回空,说明打开失败
       {
              //perror()是标准出错打印函数,能打印调用库函数出错原因
              perror("open");
              return -1;
       }
 
       return 0;
}
文件打开 使用案例

三、文件关闭

1、说明

任何文件在使用后应该关闭:

  • 打开的文件会占用内存资源,如果总是打开不关闭,会消耗很多内存
  • 一个进程同时打开的文件数是有限制的,超过最大同时打开文件数,再次调用fopen打开文件会失败
  • 如果没有明确的调用fclose关闭打开的文件,那么程序在退出的时候,操作系统会统一关闭。
#include <stdio.h>
int fclose(FILE * stream);

功能:关闭先前fopen()打开的文件。此动作让缓冲区的数据写入文件中,并释放系统所提供的文件资源。
参数:

  • stream:文件指针

返回值:

  • 成功:0
  • 失败:-1

2、案例

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

int main(void)
{
    // 定义文件指针
    FILE* fp = fopen("C:/Users/Administrator/Desktop/xsk.txt", "r");
    
    // 判断文件是否打开成功
    // 1、找不到文件
    // 2、文件权限(读、写、执行)
    // 3、程序打开文件超出上限 65535
    if (fp == NULL)
    {
        printf("打开文件失败\n");
        return -1;
    }
    
    printf("文件打开成功:%p\n",fp);
    
    // 关闭文件指针
    fclose(fp);

    return 0;
}
文件关闭 使用案例

文件字符读写

一、写文件

1、说明

#include <stdio.h>
int fputc(int ch, FILE * stream);

功能:将ch转换为unsigned char后写入stream指定的文件中
参数:

  • ch:需要写入文件的字符
  • stream:文件指针

返回值:

  • 成功:成功写入文件的字符
  • 失败:返回-1

2、案例

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

int main(void)
{
    // 以写的方式打开文件、如果文件不存在则创建一个新文件、如果文件存在则清空内容
    FILE* fp = fopen("C:/Users/Administrator/Desktop/xsk.txt", "w");
    
    if (!fp)
    {
        printf("打开文件失败\n");
        return -1;
    }
    
    // 创建字符
    // char ch = 'a';

    // 字符写入
    // 约束:if(value==-1)
    // fputc(ch, fp);
    
    // 通过键盘输入方式存字符
    char ch;
    while (1)
    {
        scanf("%c", &ch);
        // 输入@关闭文件
        if (ch == '@')
        {
            break;
        }
        fputc(ch, fp);
    }


    // 关闭文件
    fclose(fp);

    return 0;
}
fputc 使用案例

二、文件结尾

1、概述

在C语言中,EOF表示文件结束符(end of file)。在while循环中以EOF作为文件结束标志,这种以EOF作为文件结束标志的文件,必须是文本文件。在文本文件中,数据都是以字符的ASCII代码值的形式存放。我们知道,ASCII代码值的范围是0~127,不可能出现-1,因此可以用EOF作为文件结束标志。

#define EOF     (-1)

当把数据以二进制形式存放到文件中时,就会有-1值的出现,因此不能采用EOF作为二进制文件的结束标志。为解决这一个问题,ANSI C提供一个feof函数,用来判断文件是否结束。feof函数既可用以判断二进制文件又可用以判断文本文件。

2、说明

#include <stdio.h>
int feof(FILE * stream);

功能:检测是否读取到了文件结尾。判断的是最后一次“读操作的内容”,不是当前位置内容(上一个内容)。
参数:

  • stream:文件指针

返回值:

  • 非0值:已经到文件结尾
  • 0:没有到文件结尾

3、案例

三、读文件

1、说明

#include <stdio.h>
int fgetc(FILE * stream);

功能:从stream指定的文件中读取一个字符
参数:

  • stream:文件指针

返回值:

  • 成功:返回读取到的字符
  • 失败:-1

2、案例

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

int main(void)
{
    FILE* fp = fopen("C:/Users/Administrator/Desktop/xsk.txt", "r");
    
    if (!fp)
    {
        printf("打开文件失败\n");
        return -1;
    }
    
    // 定义字符
    char ch;

    // 文件字符读取
    // 文件默认结尾为 -1
    // ch = fgetc(fp);

    // 不能修改文件指针
    // 文件在读取时光标流会自动向下移动
    // fp++;

    // 循环打印
    while ((ch = fgetc(fp)) != EOF)
    {
        printf("%c", ch);
    }

    // 关闭文件
    fclose(fp);

    return 0;
}
fgetc 使用案例

四、案例

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

int main(void)
{
    FILE* fp1 = fopen("C:/Users/Administrator/Desktop/解密.txt", "r");
    FILE* fp2 = fopen("C:/Users/Administrator/Desktop/加密.txt", "w");
 if (!fp1 || !fp2)return -1;
    char ch;
    while ((ch = fgetc(fp1)) != EOF)
    {
        // 加密
        ch++;
        fputc(ch, fp2);
    }

    fclose(fp1);
    fclose(fp2);

    return 0;
}
字符读写 使用案例:加密
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

int main(void)
{
    FILE* fp1 = fopen("C:/Users/Administrator/Desktop/加密.txt", "r");
    FILE* fp2 = fopen("C:/Users/Administrator/Desktop/解密文件.txt", "w");
    if (!fp1 || !fp2)return -1;
    char ch;
    while ((ch = fgetc(fp1)) != EOF)
    {
        // 解密
        ch--;
        fputc(ch, fp2);
    }

    fclose(fp1);
    fclose(fp2);

    return 0;
}
字符读写 使用案例:解密

文件行读写

一、写文件

1、说明

#include <stdio.h>
int fputs(const char * str, FILE * stream);

功能:将str所指定的字符串写入到stream指定的文件中,字符串结束符 '\0' 不写入文件。
参数:

  • str:字符串
  • stream:文件指针

返回值:

  • 成功:0
  • 失败:-1

2、案例

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

int main(void)
{
    // 打开文件
    FILE* fp = fopen("C:/Users/Administrator/Desktop/xsk.txt", "w");
    // 判断
    if (!fp)return -1;

    // 创建字符串
    // "你想怎\0么滴吧" 遇到\0则停止
    // char ch[] = "你想怎么滴吧";
    //fputs(ch, fp);

    // 通过键盘输入获取字符串
    char* p = (char*)malloc(sizeof(char) * 1024);
    while (1)
    {
        memset(p, 0, 1024);
        
        // 问题:scanf("%s", p);无法接收回车空格
        // 方式一、fgets() 可接收空格
        // 方式二、scanf("%[^\n]", p); 避免吞噬空格回车
        scanf("%[^\n]", p);
        // 吞噬回车\n
        getchar();

        // 停止输入命令:comm=exit
        if (!strcmp(p, "comm=exit",9))break;

        // 追加换行符
        strcat(p, "\n");

        // 写入字符串
        fputs(p, fp);
    }

    free(p);
    fclose(fp);

    return 0;
}
fputs 使用案例

二、读文件

1、说明

#include <stdio.h>
char * fgets(char * str, int size, FILE * stream);

功能:从stream指定的文件内读入字符,保存到str所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了size - 1个字符为止,最后会自动加上字符 '\0' 作为字符串结束。
参数:

  • str:字符串
  • size:指定最大读取字符串的长度(size - 1)
  • stream:文件指针

返回值:

  • 成功:成功读取的字符串
  • 读到文件尾或出错: NULL

2、案例

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

int main(void)
{
    // 打开文件
    FILE* fp = fopen("C:/Users/Administrator/Desktop/xsk.txt", "r");
    // 判断
    if (!fp)return -1;

    // 开辟堆空间
    char* p = (char*)malloc(sizeof(char) * 1024);
    
    // 初始化内存空间 
    // memset(p, 0, 5);
    // 通过堆开放内存,打印文件中100个字节
    // 根据光标位置继续向下读取字符串
    // fgets(p, 5, fp);
    // printf("%s", p);
    
    // 打印
    // feof(文件指针):判断文件是否到结尾 可以判断文本文件也可以判断二进制文件
    // 如果到文件结尾返回值为 非0的值
    // 如果没到文件结尾返回值为 0的值
    while (!feof(fp))
    {
        memset(p, 0, 100);
        fgets(p, 100, fp);
        printf("%s", p);
    }

    free(p);
    fclose(fp);

    return 0;
}
fgets 使用案例

三、案例

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

enum opt
{
    add,sub,mlt,dive
};

int main(void)
{
    
    srand((size_t)time(NULL));
    
    // 打开文件
    FILE* fp = fopen("C:/Users/Administrator/Desktop/xsk.txt", "w");
    // 判断
    if (!fp)return -1;

    int a, b;
    char c; // + - * /
    char * p = (char*)malloc(sizeof(char) * 20);

    for (int i = 0; i < 100; i++)
    {
        a = rand() % 10 + 1;
        b = rand() % 10 + 1;

        switch (rand() % 4)
        {
        case add:c = '+';break;
        case sub:c = '-';break;
        case mlt:c = '*';break;
        case dive:c = '/';break;
        }

        memset(p, 0,20);
        sprintf(p, "%d%c%d=\n", a, c, b);
        fputs(p, fp);//fgets(p,size,fp)

    }

    free(p);
    fclose(fp);
    p = NULL;
    fp = NULL;

    return 0;
}
文件行读写 使用案例:四则运算(打印运算表达式)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

int main(void)
{
    
    srand((size_t)time(NULL));
    
    // 打开文件
    FILE* fp1 = fopen("C:/Users/Administrator/Desktop/xsk.txt", "r");
    FILE* fp2 = fopen("C:/Users/Administrator/Desktop/xsk2.txt", "w");
    // 判断
    if (!fp1 || !fp2)return -1;

    // 判断文件结尾
    // !feof(fp);
    // EOF -1

    int a, b, sum;
    char c;
    char * p = (char*)malloc(sizeof(char) * 20);

    for (int i = 0; i < 100; i++)
    {
        memset(p, 0, 20);
        // 打印1行内容
        fgets(p, 20, fp1);

        sscanf(p, "%d%c%d=\n", &a, &c, &b);

        switch (c)
        {
        case '+':sum = a + b; break;
        case '-':sum = a - b; break;
        case '*':sum = a * b; break;
        case '/':sum = a / b; break;
        }

        memset(p, 0, 20);
        sprintf(p, "%d%c%d=%d\n", a, c, b, sum);
        fputs(p, fp2);
    }

    free(p);
    fclose(fp1);
    fclose(fp2);

    return 0;
}
文件行读写 使用案例:四则运算(打印运算表达式结果)

文件格式化

一、写文件

1、说明

#include <stdio.h>
int fprintf(FILE * stream, const char * format, ...);

功能:根据参数format字符串来转换并格式化数据,然后将结果输出到stream指定的文件中,指定出现字符串结束符 '\0' 为止。
参数:

  • stream:已经打开的文件
  • format:字符串格式,用法和printf()一样

返回值:

  • 成功:实际写入文件的字符个数
  • 失败:-1

2、案例

二、读文件

1、说明

#include <stdio.h>
int fscanf(FILE * stream, const char * format, ...);

功能:从stream指定的文件读取字符串,并根据参数format字符串来转换并格式化数据。
参数:

  • stream:已经打开的文件
  • format:字符串格式,用法和scanf()一样

返回值:

  • 成功:参数数目,成功转换的值的个数
  • 失败: - 1

2、案例

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

int main(void)
{
    
    // 打开文件
    FILE* fp = fopen("C:/Users/Administrator/Desktop/xsk.txt", "r");
    // 判断
    if (!fp)return -1;

    char * p = (char*)malloc(sizeof(char) * 100);
    
    // 直接格式化取出字符串
    // 遇到空格与回车就结束
    // fscanf(fp, "%s", p);
    // printf("%s", p);


    int a, b, c;

    // 1+2=3
    // 处理表达式
    fscanf(fp, "%d+%d=%d", &a,&b,&c);

    printf("%d\n", a);
    printf("%d\n", b);
    printf("%d\n", c);

    free(p);
    fclose(fp);

    return 0;
}
fscanf 使用案例

三、案例