C语言学习——文件

目录

十三、文件

13.1C文件概述

13.2文件类型指针

13.3文件的打开与关闭

文件的打开(fopen函数)

文件的关闭(fclose函数)

13.4文件的读写

fputc函数和fgetc函数(putc函数和getc函数)

fread函数和fwrite函数

fprintf函数和fscanf函数


十三、文件

13.1C文件概述

文件(file)是程序设计中一个重要的概念。所谓“文件”一般指存储在外部介质上数据的集合。一批数据是以文件的形式存放在外部介质(如磁盘)上的。操作系统是以文件为单位对数据进行管理的,也就是说,如果想找存在外部介质上的数据,必须先按文件名找到所指定的文件,然后再从该文件中读取数据。要向外部介质上存储数据也必须先建立一个文件(以文件名标识),才能向它输出数据。

前面我们所用到的输入和输出,都是以终端为对象的,即从终端键盘输入护具,运行结果输出在终端上。从操作系统的角度看,每一个与主机相联的输入输出设备都看作是一个文件。例如:终端键盘是输入文件,显示屏和打印机是输出文件。

在程序运行时,常常需要将一些数据(运行的最终结果或中间数据)输出到磁盘上存放起来,以后需要时再从磁盘中输入到计算机内存。这就要用到磁盘文件。

C语言把文件看作是一个字符(字节)的序列,即由一个一个字符(字节)的数据顺序组成的。根据数据的形式,可分为ASCII文件和二进制文件。ASCII文件又称文本(text)文件,它的每一个字节放一个ASCII代码,代表一个字符。二进制文件时把内存中的数据按其在内存中的存储形式原样输出到磁盘存放。如果有一个整数10 000 ,在内存中占2个字节,如果按ASCII码形式输出,则占5个字节,而按二进制形式输出,在磁盘上只占2个字节,见下图。用ASCII码形式输出与字符一一对应,一个字节代表一个字符,因而便于对字符进行逐个处理, 也便于输出字符。但一般占存储空间较多,而且要花费转换时间(二进制形式与ASCII码间的转换)。用二进制形式输出数值,可以节省外存空间和转换时间,但一个字节并不对应一个字符,不能直接输出字符形式。一般中间用结果数据需要暂时保存在外存上,以后又需要输入到内存中,常用二进制文件保存。

由前所述,一个C文件是一个字节流或二进制流。它把数据看作是一连串的字符(字节),而不考虑记录的界限。换句话说,C语言中文件并不是由记录(record)组成的(这是和PASCAL或其他高级语言不同的)。在C语言中对文件的存取是以字符(字节)为单位的。

输入输出的数据流的开始和结束仅受程序控制而不受物理符号(如回车换行符)控制。也就是说,在输出时不会自动增加回车换行符以作为记录的标志,输入时不以回车换行符作为记录的间隔(事实上C文件并不由记录构成)。把这种文件成为流式文件。C语言允许对文件存取一个字符,这就增加了处理的灵活性。

在过去使用的C版本(如UNIX系统下使用的C)有两种对文件的处理方法:一种叫“缓冲文件系统”,一种叫“非缓冲文件系统”。所谓缓冲文件系统是指系统自动地在内存区为每一个正在使用的文件开辟一个缓冲区。从内存向磁盘输出数据必须先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘去。如果从磁盘向内存读入数据,则一次从磁盘文件将一批数据输入到内存缓冲区(充充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(给程序变量),见下图。缓冲区的大小由各个具体的C版本确定,一般为512字节。

所谓“非缓冲文件系统”是指系统不自动开辟确定大小的缓冲区,而由程序为每个文件设定缓冲区。

在UNIX系统下,用缓冲文件系统来处理文本文件,用非缓冲文件系统处理二进制文件。用缓冲文件系统进行的输入输出又称为高级(或高层)磁盘输入输出(高层I/O),用非缓冲文件系统进行的输人输出又称为低级(低层)输入输出系统。ANSI C标准不采用非缓冲文件系统,而只采用缓冲文件,即既用缓冲文件系统处理文本文件,也用它来处理二进制文件,也就是将缓冲文件系统扩充为可以处理二进制文件。

在C语言中,没有输入输出语句,对文件的读写都是用库函数来实现的。ANSI 规定了标准输入输出函数,用它们对文件进行读写。

13.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类型的变量,以变存放若干个文件的信息。例如,可以定义以下FILE类型的数组:

FILE f[5];  //定义了一个结构体数组f,它有5个元素,可以用来存放5个文件的信息

可以定义文件型指针变量。例如:

FILE *fp;  

fp是指向一个FILE类型结构体变量的指针变量。可以使fp指向某一文件的结构体变量,从而通过该结构体变量中的文件信息能够访问该文件。也就是说,通过文件指针变量能够找到与它相关的文件。如果有n个文件,一般应设n个指针变量(指向FILE类型结构体的指针变量),使它们分别指向n个文件(确切地说,指向存放该文件信息的结构体变量),以实现对文件的访问。

13.3文件的打开与关闭

和其他高级语言一样,对文件读写之前应该“打开”该文件,在使用结束之后应“关闭”该文件。

文件的打开(fopen函数)

调用方式为:

FILE *p;

fp = fopen(文件名,使用文件的方式);

例如:

fp = fopen("a1","r");

可以看出,在打开一个文件时,通知编译系统以下3个信息:

  • 需要打开的文件名,也就是准备访问的文件的名字

  • 使用文件的方式

  • 让哪一个指针变量指向被打开的文件

使用文件的方式见下表:

文件使用方式含义
"r"(只读)为输入打开一个文本文件
"w"(只写)为输出打开一个一个文本文件
"a"(追加)向文本文件尾添加数据
"rb"(只读)为输入打开一个二进制文件
"wb"(只写)为输出打开一个二进制文件
"ab"(追加)向二进制文件尾添加数据
"r+"(读写)为读写打开一个文本文件
"w+"(读写)为读写建立一个新的文本文件
"a+"(读写)为读写打开一个文本文件
"rb+"(读写)为读写打开一个二进制文件
"wb+"(读写)为读写建立一个新的二进制文件
"ab+"(读写)为读写打开一个二进制文件

说明:

(1)用"r"方式打开的文件只能用于向计算机输入而不能用作向该文件输出数据,而且该文件应该已经存在,不能用"r"方式打开一个并不存在的文件,即输入文件;否则出错。

(2)用"w"方式打开的文件只能用于向该文件写数据,即输出文本,而不能用来向计算机输入。如果原来不存在该文件,则在打开时新建立一个以指定名字命名的文件。如果原来已经存在一个以该文件命名的文件,则在打开时将该文件删去,然后重新建立该文件。

(3)如果希望向文本末尾添加新的数据(不希望删除原有数据),则应该用"a"的方式打开。但此时文件必须存在;否则将得到出错信息。打开时,位置指针移到文件末尾。

(4)用"r+"、"w+"、"a+"方式打开的文件既可以用来输入数据,也可以用来输出数据。用"r+"方式时该文件已经存在,以便能向计算机输入数据。用"w+"方式则新建一个文件,先向此文件写数据,然后可以读此文件中的数据。用"a+"方式打开的文件,原来的文件不被删去,位置指针移到文件末尾,可以添加,也可以读。

(5)如果文件不能实现打开的任务,fopen函数将会带回一个出错的信息。出错的原因可能是用"r"方式打开一个并不存在的文件;磁盘已满无法建立新文件等。此时fopen函数将带回一个空指针值NULL。

if ((fp=open("file1","r"))==NULL)
{printf("cannot open this file \n");exit(0);  //关闭所有文件,终止正在执行的程序,待用户检查出错误,修改后再进行
}

(6)以上方式可以打开文本文件或二进制文件,这是SNSI C的规定,用同一种缓冲文件系统来处理文本文件和二进制文件。

(7)在向计算机输入文本文件时,将回车换行符转换为一个换行符,在输出时把换行符换成为回车和换行两个字符。在用二进制文件时,不进行这种转换,在内存的数据形式与输出到外部文件中的数据形式完全一致,一一对应。

(8)在程序开始运行时,系统自动打开3个标准文件:标准输入、标准输出、标准出错输出。通常这3个文件都与终端相联系。

文件的关闭(fclose函数)

在使用完一个文件后应该关闭它,以防止它再被误用。“关闭”就是使文件指针变量不指向该文件。

fclose调用的一般形式为:

fclose(文件指针);

例如:

fclose(fp);

在程序终止前应该关闭所有文件,如果不关闭文件,则会丢失数据。

13.4文件的读写

fputc函数和fgetc函数(putc函数和getc函数)

fputc函数

把一个字符写到磁盘文件上去。其调用的一般形式为:

fputc(ch,fp);

fgetc函数

从指定的文件读入一个字符,该文件必须是以读或读写方式打开的。其调用的一般形式为:

ch = fgetc(fp);

fputc和fgetc函数使用举例

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

#include<stdio.h>
#include<stdlib.h>
​
int main()
{FILE *fp;char ch,filename[10];scanf("%s",filename);if((fp=fopen(filename,"w"))==NULL){printf("cannot open this file \n");exit(0); //终止程序 }ch = getchar();  //此语句用来接收在执行scanf语句时最后输入的回车符 ch = getchar();  //接收输入的第一个字符while(ch!='#'){fputc(ch,fp);putchar(ch);ch = getchar(); } putchar(10);  //向屏幕输出一个换行符 fclose(fp);  return 0;
}
​
//输入
file.c
computer and c#
//输出 
computer and c 

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

#include<stdio.h>
#include<stdlib.h>
​
int main()
{FILE *in,*out;char ch,infile[10],outfile[10];printf("Enter the infile name:\n");  //输入原有磁盘名 scanf("%s",infile);printf("Enter the outfile name:\n");  //输入新复制的磁盘文件名 scanf("%s",outfile);if((in=fopen(infile,"r"))==NULL){printf("cannot open infile \n");exit(0); //终止程序 }if((out=fopen(outfile,"w"))==NULL){printf("cannot open outfile \n");exit(0); //终止程序 }while(!feof(in))fputc(fgetc(in),out);fclose(in);fclose(out);return 0;
}

以上程序是按文本文件方式处理的。也可以用此程序来复制一个二进制文本,只需要将两个fopen函数中的"r"和"w"分别改成"rb"和"wb"即可。

fread函数和fwrite函数

用fgetc和fputc函数可以用来读写文件中的一个字符。但是常常要求一次读入一组数据(例如,一个实数或一个结构体变量的值),ANSI C标准提出设置两个函数(fread函数和fwrite函数),用来读写一个数据块。它们的一般调用形式为:

fread(buffer,size,count,fp);

fwrite(buffer,count,fp);

其中

buffer:是一个指针。对fread来说,它是读入数据的存放地址。对fwrite来说,是要输出数据的地址(以上指的是起始地址)。

size:要读写的字节数。

count:要进行读写多少个size字节的数据项。

fp:文件型指针。

如果文件以二进制形式打开,用fread和fwrite函数就可以读写任何类型的信息,例如:

fread(f,4,2,fp);

其中f是一个实型数组名。一个实型变量占4个字节。这个函数从fp所指向的文件读入2个4个字节的数据,存储到数组f中。

如果有一个如下的数据结构类型:

struct student_type{char name[10];int num;int age;char addr[30];
}stud[40];

结构体数组stud有40个元素,每一个元素用来存放一个学生的数据(包括姓名、学号、年龄、地址)。假设学生的数据已存放到磁盘文件中,可以用下面的for语句和fread函数读入40个学生的数据。

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

同样,以下for语句和fwrite函数可以将内存中的学生数据输出到磁盘文件中去:

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

如果fread或fwrite调用成功,则函数返回值为count的值,即输入或输出数据项的完整个数。

下面写一个完整的程序。

从键盘输入4个学生的有关数据,然后把它们转存到磁盘文件上去。

#include<stdio.h>
#define SIZE 4
​
struct student_type{char name[10];int num;int age;char addr[15];
}stud[SIZE];
​
void save()
{FILE *fp;int i;if((fp=fopen("stu_list","wb"))==NULL){printf("cannot open file\n");return ;}for(i=0;i<SIZE;i++){if(fwrite(&stud[i],sizeof(struct student_type),1,fp)!=1){printf("file write error\n");}}fclose(fp);} int 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();
}
//输入:
Zhang 1001 19 room_101
Fun 1002 20 room_102
Tan 1003 21 room_103
King 1004 21 room_104

程序运行时,屏幕上无任何信息,只是将从键盘输入的数据送到磁盘上。为了验证在磁盘文件“stu_list”中是否存在此数据,可以用以下程序从“stu_list”文件中读入数据,然后在屏幕上输出。

#include<stdio.h>
#define SIZE 4
​
struct student_type{char name[10];int num;int age;char addr[15];
}stud[SIZE];
​
​
int main()
{int i;FILE *fp;fp = fopen("stu_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);
}
​
//输出:
Zhang     1001  19room_101
Fun       1002  20room_102
Tan       1003  21room_103
King      1004  21room_104 

请注意输入输出数据的情况。从键盘输入4个学生的数据是ASCII码,也就是文本文件。在送到计算机内存时,回车和换行符转换成一个换行符。再从内存以"wb"方式(二进制)输出到"stu_list"文件,此时不发生字符转化,按内存中存储形式原样输出到磁盘文件上。在上面的验证程序中,又用fread函数从"stu_list"文件向内存肚饿数据,注意此时用的是"rb"方式,即二进制方式,数据按原样输入,也不发生字符转换。也就是这时内存中的数据恢复到第一个程序向"stu_list"输出以前的情况。最后在验证程序中,用printf函数输出到屏幕,printf是格式输出函数,输出ASCII码,在屏幕上显示字符。换行符又转换为回车加换行符。

fprintf函数和fscanf函数

fprintf函数、fscanf函数与printf函数、scanf函数作用相仿,都是格式化读写函数。只有一点不同:fprintf函数和fscanf函数的读写对象不是终端,而是磁盘文件。它们的一般调用格式为:

fprintf(文件指针,格式字符串,输出表列);

fscanf(文件指针,格式字符串,输出表列);

例如:

fprintf(fp,"%d,%6.2f",i,t);
fscanf(fp,"%d %f",&i,&t);

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

声明:本文章为个人学习笔记,资料整理参考谭浩强《C程序设计(第三版)》如有错误,欢迎大家指正!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/408217.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【qt】自定义信号

我们在上篇中&#xff0c;服务器收到的消息是由线程类去处理的&#xff0c;消息在线程类中&#xff0c;传不到widget中的ui中去&#xff0c;如果我们要在界面显示客户端的消息&#xff0c;必须通过自定义信号. 1.构建信号 当线程收到信息&#xff0c;就会被填充在ba中&#xf…

PHPShort轻量级网址缩短程序源码开心版,内含汉化包

需要网址缩短并且想获得更多有关链接点击率和流量的数据分析&#xff0c;那么 PHPShort 可能是一个非常好的选择。PHPShort 是一款高级的 URL 缩短器平台&#xff0c;可以帮助你轻松地缩短链接&#xff0c;并根据受众群体的位置或平台来定位受众。 该程序基于 Laravel 框架编写…

Fiddle抓手机app的包

前言 本次文章讲述的是&#xff0c;fiddle获取手机代理&#xff0c;从而获取手机app的http、https请求&#xff01; 一.下载安装汉化Fiddle 1.点击Fiddler官网下载链接&#xff1a;Download Fiddler Web Debugging Tool for Free by Telerik 2.直接运行&#xff0c;选择自己需…

Linux:进程的概念,进程相关函数

一、进程的概念 1.进程 进程是系统进行资源分配和调度的一个独立单元&#xff0c;它是操作系统结构的基础。进程是程序的一次执行过程&#xff0c;包含了程序代码、当前活动、系统资源&#xff08;如CPU、内存、文件等&#xff09;的使用情况等信息。每个进程都有自己独立的内…

AUTOSAR_EXP_ARAComAPI.pdf的第4章笔记

4 Fundamentals 为了理解AUTOSAR_EXP_ARAComAPI.pdf的第4章内容&#xff0c;生搬硬套的翻译了一把&#xff0c;准备先囫囵吞枣&#xff0c;再仔细理解。因为这些内容的理解也不是一时半会儿的。所以先放上来。 AUTOSAR_EXP_ARAComAPI.pdf的概述 因此&#xff0c;ara::com不提…

VS2022 - 制作自己的C#类库dll,并输出Unity识别的pdb调试信息文件

然后编写库代码&#xff0c;设置dll生成目录 *** 输出unity可以识别的pdb调试信息文件 *** 右键项目-属性-生成-高级-调试信息&#xff1a;可移植(Portable PDB) 这是因为Unity只能识别MDB和Portable PDB文件 这样设置后&#xff0c;把dll和pdb文件放入到Unity中同文件夹下&…

002、架构_概览

GoldenDB 主要由管理节点、计算节点、数据节点、全局事务节点等模块组成&#xff0c;各个节点无需共享任何资源&#xff0c;均为独立自治的通用计算机节点&#xff0c;之间通过高速互联的 网络通讯&#xff0c;从而完成对应用数据请求的快速处理和响应。 管理节点在数据库中主要…

【JVM】剖析字符串与数组的底层实现(一)

剖析字符串与数组的底层实现 字符数组的存储方式 JVM有三种模型: 1.Oop模型:Java对象对应的C对象2.Klass模型:Java类在JVM对应的C对象3.handle模型 字符串常量池 即String Pool&#xff0c;但是JVM中对应的类是StringTable&#xff0c;底层实现是一个hashtable,如代码所示 …

三级_网络技术_42_综合题(命令)

一、 如图所示&#xff0c;某园区网用10Gbps的POS技术与intemet相连&#xff0c;POS接囗的帧格式是SDH。在R3上配置一个oopback接口&#xff0c;"P地址为10.2.15.1&#xff0c;路由协议的选择方案是&#xff0c;园区网内部采用OSPF动态路由协议&#xff0c;园区网与Inter…

如何使用ssm实现基于面向对象的学生事务处理系统分析与设计

TOC ssm138基于面向对象的学生事务处理系统分析与设计jsp 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化…

go中 panicrecoverdefer机制

go的defer机制-CSDN博客 常见panic场景 数组或切片越界&#xff0c;例如 s : make([]int, 3); fmt.Println(s[5]) 会引发 panic: runtime error: index out of range空指针调用&#xff0c;例如 var p *Person; fmt.Println(p.Name) 会引发 panic: runtime error: invalid m…

AJAX(4)——XMLHttpRequest

XMLHttpRequest 定义&#xff1a;XMLHttpRequest(XHR)对象用于与服务器交互。通过XMLHttpRequest可以在不刷新页面的情况下请求特定URL&#xff0c;获取数据。这允许网页在不影响用于操作的情况下&#xff0c;更新页面的局部内容。XMLHttpRequest在AJAX编程中被大量使用 关系…

MATLAB 手动实现点云投影滤波器 (76)

点云投影到邻近的精确拟合平面,减少噪声点,此为投影滤波器 MATLAB 手动实现点云投影滤波器(76) 一、投影滤波器简介二、实现步骤二、算法实现1.代码2.效果这里用到的投影方法和平面拟合方法以及生成平面方法都在以往文章有所实现,有兴趣可参考: MATLAB点云处理总目录 一…

C++动态规划及九种背包问题

目录 目录 一&#xff0c;动态规划 一&#xff09;&#xff0c;动态规划的定义 二&#xff09;&#xff0c;动态规划其他的相关概念&#xff08;也是使用条件&#xff09; 1&#xff0c;重叠子问题 2&#xff0c; 最优子结构 3&#xff0c;无后效性 三&#xff09;&…

dompdf导出pdf中文乱码显示问号?、换行问题、设置图片大小

环境&#xff1a;PHP 8.0 框架&#xff1a;ThinkPHP 8 软件包&#xff1a;phpoffice/phpword 、dompdf/dompdf 看了很多教程&#xff08;包括GitHub的issue、stackoverflow&#xff09;都没有解决、最终找到解决问题的根本&#xff01; 背景&#xff1a;用Word模板做转PDF…

【JavaEE初阶】IP协议

目录 &#x1f4d5;引言 &#x1f334;IP协议的概念 &#x1f333;IP数据报 &#x1f6a9;IPv4协议头格式 &#x1f6a9;IPv6的诞生 &#x1f3c0;拓展 &#x1f384;IP地址 &#x1f6a9;IP地址的格式&#xff1a; &#x1f6a9;IP地址的分类 &#x1f3c0;网段划分…

【第57课】SSRF服务端请求Gopher伪协议无回显利用黑白盒挖掘业务功能点

免责声明 本文发布的工具和脚本&#xff0c;仅用作测试和学习研究&#xff0c;禁止用于商业用途&#xff0c;不能保证其合法性&#xff0c;准确性&#xff0c;完整性和有效性&#xff0c;请根据情况自行判断。 如果任何单位或个人认为该项目的脚本可能涉嫌侵犯其权利&#xff0…

Unity动画模块 之 Animator中一些常见参数

本文仅作笔记学习和分享&#xff0c;不用做任何商业用途 本文包括但不限于unity官方手册&#xff0c;unity唐老狮等教程知识&#xff0c;如有不足还请斧正 我发现我忘了写Animator了&#xff0c;正好有些不常用的参数还没怎么认识,笔记来源于唐老狮 1.状态窗口参数 2.连线参数…

如何使用ssm实现学生公寓管理系统的设计与实现

TOC ssm106学生公寓管理系统的设计与实现jsp 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;…

Qt第十八章 XML和Json格式解析

文章目录 JSON格式解析Json生成案例 XML简介与HTML的区别格式XML解析流的方式DOM XML生成 JSON与XML的区别比较 JSON 格式 JSON是一个标记符的序列。这套标记符包含六个构造字符、字符串、数字和三个字面名 六个构造字符 开始和结束数组&#xff1a;[ ]开始和结束对象&#x…