利用C语言操作文件

语言把文件看作一个字符的序列,即由一个一个字符(字节)的数据顺序组成。根据数据的组织形式,可分为ASCII文件和二进制文件。ASCII文件有称为文本(text)文件,它的每个字节存放一个ASCII代码,代表一个字符。二进制文件是把内存中的数据按其在内存中的存储形式原样输出。如果一个数10000,在内存中占2个字节。如果按照ASCII存储则占5个字节,如按二进制存储则占2字节。

1、C文件概述
C语言把文件看作一个字符的序列,即由一个一个字符(字节)的数据顺序组成。根据数据的组织形式,可分为ASCII文件和二进制文件。ASCII文件有称为文本(text)文件,它的每个字节存放一个ASCII代码,代表一个字符。二进制文件是把内存中的数据按其在内存中的存储形式原样输出。如果一个数10000,在内存中占2个字节。如果按照ASCII存储则占5个字节,如按二进制存储则占2字节。如下:

二进制形式内存中样式ASCII形式
00100111 000100000100111 0001000

00110001 ----> 1

00110000 ----> 0  

00110000 ----> 0 

00110000 ----> 0 

00110000 ----> 0

由前述,一个C文件是一个字符流或二进制流。在C语言中文件的存取以字符(字节)为单位。输入输出的数据流的开始和结束仅受程序控制而不受物理符号(如回车换行符)控制。也就是说,在输出时不会增加回车换行符作为记录结束的标志,输入时不以回车换行符作为间隔。这种文件称为流式文件。

在过去使用C版本有两种对文件的处理方式:a、缓冲文件系统 b、非缓冲文件系统。缓冲文件系统在每次向文件读写时都进过缓冲区。

2、文件类型指针
缓冲文件系统中,关键的概念是“文件指针”。每个被使用的文件在内存中开辟一个区,用来存放文件的有关信息。这些信息保存在一个结构体变量中。该结构体有系统定义的,取名为FILE。在Turbo C中stdio.h对文件定义如下:

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

有了文件FILE结构体类型后,就可以定义文件类型的变量和指针变量。如:

FILE f[5]  定义一个文件数组

FILE *fp 定义一个文件指针

3、文件的打开和关闭
ANSI C规定了标准输入输出函数库,用fopen()函数来实现打开文件。fopen函数调用方式通常为:

FILE * fp;
fp = fopen(文件名, 使用文件方式);
文件使用方式含义
“r”(只读)        为输入打开一个文本文件
“w”(只写)       为输出打开一个文本文件
“a”(追加)       向文本文件尾添加数据
“rb”(只读)      为输入打开一个二进制文件
“wb”(只写)     为输出打开一个二进制文件
“ab”(追加)     向二进制文件末尾添加数据
“r+”(读写)      为读写打开一个文本文件
“w+”(读写)      为读写建立一个新的文本文件
“a+”(读写)     为读写打开一个文本文件
“rb+”(读写)    为读写打开一个二进制文件
“wb+”(读写)   为读写建立一个新的二进制文件
“ab+”(读写)   为读写打开一个二进制文件

说明:

(1)用"r"打开的文件只能用于向计算机输入文件,而且该文件已经存在。不能打开不存在的文件,否则会报错。
(2)用"w"打开的文件只能用于输出文件。如果不存在文件,则在打开是新建一个以指定名称的文件。如果存在,则删除存在的文件,然后重新建立一个新文件。
(3)如果使用"a"方式打开则可以向文件末尾追加数据,但是该文件必须已经存在。打开时,位置指针移动到文件末尾。
(4)使用"r+"、"w+"、"a+"方式打开的文件仅可以读也可以写。用“r+”打开的文件必须存在,用"w+"删除旧文件,重新建立新文件。用"a+"方式打开不会删除文件。
(5)如果打开文件失败,fopen()函数返回一个NULL(NULL在stdio.h文件中定义为0)。如:  

if((fp = fopen("file1", "r")) == NULL)
{
  printf("不能打开这个文件\n");
  exit(0);
}

(6)在向计算机输入文本文件时,回车换行符被替换成一个换行符,输出时转换成回车换行符。二进制文件,则原样保存。
(7)在程序开始运行时,系统自动打开3个标准输入输出。stdin标准输入、stdout标准输出、stderr标准出错输出。

常用函数简介:

1、fclose(文件指针)函数用于关闭文件,关闭成功返回0。否则返回EOF(-1)。如:fclose(fp);

2、fputc(输出字符,文件指针)函数把一个字符写到磁盘上去。如:fputc(ch,fp)表示将ch字符输出到fp指向的文件中去。输出成功返回输出的字符,否则返回EOF

3、putchar函数是从fputc函数派生而来。#define putchar(c) fputc(c, stdout)

4、fgetc(文件指针)函数从指定的文件中读取一个字符,该文件必须以读或读写方式打开的。如:ch = fgetc(fp);从fp指定的文件中取出一个字符,赋给ch变量。

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

以上是将一个文件以顺序显示在屏幕上。

注意:

EOF不是可输出字符,因为ASCII码不可能出现-1。当读取文件等于-1(EOF)时,表示读入的已不是正常字符而是文件结束符。但是值适合于文本文件。当读取二进制文件时,读取的数据可能为-1,因此ANSI C提供了feof(文件指针)函数来判断文件是否已经结束。如果结束,feof(文件指针)返回值为1(真),否则为0(假);
如:

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

可以顺利的读取一个二进制文件

【实例】从键盘输入一个字符串,逐个把他们写到磁盘文件上,知道输入‘#’为止。

#include <stdio.h>
#include <stdlib.h>
void main()
{
    FILE * fp;
    char ch, filename[10];
    scanf("%s", filename); // 提示用户输入文件名
    if((fp = fopen(filename, "w")) == NULL)
    {
        printf("不能打开文件\n");
        exit(0); // 终止程序
    }
    ch = getchar(); // 用来接收在执行scanf语句时最后输入的回车符
    ch = getchar(); // 接受输入的第一个字符
    while(ch != '#')
    {
        fputc(ch, fp); // 写入到文件中去
        putchar(ch);
        ch = getchar();
    }
    putchar(10); // 向屏幕输出一个换行符
    fclose(fp);
}

【实例】将文件中的数据输出到屏幕上

#include <stdio.h>
#include <stdlib.h>
void main()
{
    FILE *in;
    char ch, infile[10];
    printf("请输入文件名:\n");
    scanf("%s", infile);
    if( (in = fopen(infile, "r")) == NULL)
    {
        printf("打开输入文件失败\n");
        exit(0);
    }
    // 将文件中的数据输出到屏幕上
    while( !feof(in))
    {
        putchar(fgetc(in));
    }
    // 关闭文件
    close(in);
}

【实例】将一个文件的数据写入到另一个文件中去。

#include <stdio.h>
#include <stdlib.h>
void main()
{
    FILE *in, *out;
    char ch, infile[10], outfile[10];
    printf("请输入被拷贝文件名:\n");
    scanf("%s", infile);
    printf("请输入拷贝文件名:\n");
    scanf("%s", outfile);
    // 打开输入文件
    if( (in = fopen(infile, "r")) == NULL)
    {
        printf("打开输入文件失败\n");
        exit(0);
    }
    // 打开输出文件
    if( (out = fopen(outfile, "w")) == NULL)
    {
        printf("打开输出文件失败\n");
        exit(0);
    }
    // 将输入文件的数据写入到输出文件中去
    while( !feof(in))
    {
        fputc(fgetc(in), out);
    }
    // 关闭文件
    close(in);
    close(out);
}

对文件之间拷贝的改进:

#include <stdio.h>
#include <stdlib.h>
void main(int argc, char * argv[]) // argc-表示参数argv数组的长度 argv-表示参数
{
    FILE *in, *out;
    char ch;
    if(argc != 3)     // argv[0]-可执行文件名 argv[0]-输入文件名 argv[0]-输出文件名
    {
        printf("你忘记输入文件名");
        exit(0);
    }
    if( (in = fopen(argv[1], "r")) == NULL)
    {
        printf("打开输入文件失败\n");
        exit(0);
    }
    if( (out = fopen(argv[2], "w")) == NULL)
    {
        printf("打开输出文件失败\n");
        exit(0);
    }
    // 将文件中的数据输出到屏幕上
    while( !feof(in))
    {
        fputc(fgetc(in), out);
    }
    // 关闭文件
    close(in);
}

 运行方式:

    C:\> fileExe text.txt new.txt

5、fread(buffer, size, count, fp) 和 fwrite(buffer、size、count、fp)

参数介绍:
    buffer   要读入/写出存放数据的地址(起始地址)
    size      要去读的字节数
    count    要读写多少个size字节的数据
    fp          文件指针
如:fread(f,4,2,fp)表示从fp中读取2个4字节的数据到数组f中

如果存在一下学生信息结构体:

struct student_type
(
    char     name[20];    -- 学生姓名
    int        num;            --    学号
    int        age;             --    年龄
    char     addr[30];     -- 居住地址
)stud[40];

我们可以使用一下语句读取和写出数据。

for(i = 0; i < 40; i ++)
{
    fread(&stud[i], sizeof(struct student_type), 1, fp);
}
for(i = 0; i < 40; i ++)
{
    fwrite(&stud[i], sizeof(struct student_type), 1, fp);
}

如果调用成功放回count的值。

【实例】从键盘输入4个学生的有关数据,然后将他们存到磁盘文件上。

#include <stdio.h>
#include <stdlib.h>
#define SIZE 4
struct student_type
{
    char name[20];       // 姓名
    int    num;          // 学号
    int    age;          // 年龄
    char addr[15];       // 地址
} stud[SIZE];
void save()
{
    FILE *fp;
    int i;
    if((fp = fopen("student_list","wb")) == NULL)
    {
        printf("打开student_list文件失败\n");
        return;
    }
    for(i=0; i<SIZE; i++)
    {
        if(fwrite(&stud[i], sizeof(struct student_type), 1 , fp) != 1)
        {
            printf("写入错误\n");
        }
    }
    fclose(fp);
}
void main( )
{
    int i;
    for(i = 0; i < SIZE; i ++)
    {
        scanf("%s%d%d%s", stud[i].name, &stud[i].num, &stud[i].age, stud[i].addr);
    }
    save();
}

【实例】将上面写入的数据读取到屏幕上面

#include <stdio.h>
#include <stdlib.h>
#define SIZE 4
struct student_type
{
    char name[20];
    int num;
    int age;
    char addr[15];
} stud[SIZE];
void main()
{
    int i;
    FILE *fp;
    fp = fopen("student_list", "rb");
    for(i = 0; i < SIZE; i ++)
    {
        fread(&stud[i], sizeof(struct student_type), 1, fp);
        printf("%-10s%4d%4d%-15s\n", stud[i].name, stud[i].num, stud[i].age, stud[i].addr);
    }
    fclose(fp);
}

注意:fread()和fwrite()一般用于二进制文件的输入输出。因为他们是按照数据块的长度来处理输入输出的,在字符串发生转换的情况下很有可能出现与原设想不同的情况。

【实例】将一个已经存在学生基本信息的文件加载到内存,然后写入到新的文件中去。

#include <stdio.h>
#include <stdlib.h>
#define SIZE 4
struct student_type
{
    char name[20];
    int  num;
    int  age;
    char addr[15];
} stud[SIZE];
/**
* 将student_list文件中保存的学生信息加载到内存中
*/
void load()
{
    FILE *fp;
    int i;
    if((fp = fopen("student_list","rb")) == NULL)
    {
        printf("打开student_list文件失败\n");
        exit(0);
    }
    for(i=0; i<SIZE; i++)
    {
        if(fread(&stud[i], sizeof(struct student_type), 1, fp) != 1)
        {
            if(feof(fp))
            {
                fclose(fp);
                return;
            }
            printf("\n");
        }
    }
}
/**
* 将加载到内存的数据保存到new_student_list文件中
*/
void save()
{
    FILE *fp;
    int i;
    if((fp=fopen("new_student_list", "wb")) == NULL)
    {
        printf("打开new_student_list文件失败\n");
        exit(0);
    }
    for(i=0; i<SIZE; i++)
    {
        if(fwrite(&stud[i], sizeof(struct student_type), 1, fp) != 1)
        {
            printf("向new_student_list文件写入数据失败\n");
        }
    }
    fclose(fp);
}
void main()
{
    load();
    save();
}

6、fprintf( )和fscanf( )函数
    这两个函数与printf和scanf函数作用相仿,都是格式化读写函数。只有一点不同:fprintf和fscanf函数的读写对象不是终端而是磁盘文件。它们的一般格式为:
fprintf(文件指针、格式化字符串、输出列表)
fscanf(文件指针、格式化字符串、输入列表)
如:fprintf(fp, "%d,%6.2f", i, c) 写到磁盘文件
       fscanf(fp, "%d,%f", &i, &t) 磁盘文件上存在:3,4.5

【实例】将0~9十个数字写入到number.txt文件中

#include <stdio.h>
#include <stdlib.h>
void main()
{
    FILE *fp;
    int i;
    if((fp = fopen("number.txt", "w")) == NULL)
    {
        printf("不能打开文件\n");
        exit(0);
    }
    // 向number.txt文件循环写入0~9数字
    for(i=0; i<10; i++){
        fprintf(fp, "%d", i);
    }
    fclose(fp);
}

【实例】从磁盘文件读取数据,存到变量中,然后将变量输出到屏幕上。

#include <stdio.h>
#include <stdlib.h>
void main()
{
    FILE *fp;
    int a;
    float b;
    if((fp = fopen("number.txt", "r")) == NULL)
    {
        printf("不能打开文件\n");
        exit(0);
    }
    fscanf(fp, "%d,%f", &a, &b); // 20,25.4
    printf("a=%d, b=%f\n", a, b); // a=20, b=25.4
    fclose(fp);
}

用fprintf或fscanf对磁盘文件进行读写,使用方便,容易理解。但由于在输入时要将ASCII码转换成二进制格式,在输出时要将二进制转换成字符,花费时间较多。因而,在内存与磁盘交换频繁时,最好不要用fprintf和fscanf函数,而用fread和fwrite函数。

不傲才以骄人,不以宠而作威。——诸葛亮
0 不喜欢
说说我的看法 -
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号