记录关于输入输出流和文件类型的相关知识点
文件的类型
大家都知道计算机的存储,物理上是二进制的,所以文本文件与二进制文件的区别并不是物理上的,而是逻辑上的。这两者只是在编码层次上有差异。简单来说,文本文件是基于字符编码的文件,常见的编码有 ASCII 编码,二进制文件是基于值编码
的文件。
* 文本文件:
以 ASCII 码格式存放,一个字节存放一个字符。 文本文件的每一个字节存放一个 ASCII 码,代表一个字符。这便于对字符的逐个处理,但占用存储空间
较多,而且要花费转换时间。
* 二进制文件:
以值(补码)编码格式存放。二进制文件是把数据以二进制数的格式存放在文件中的,其占用存储空间较少。 数据按其内存中的存储形式原样存放。
实例
接下来通过一个例子说明两者的区别:
short int
型的变量在内存中占2个字节(8位)。
short int a = 10000;
数字10000
的二进制表示是 0010 0111 0001 0000
十六进制表示为0x2710
。
也就是说在计算机内存中变量a的值是以0x2710
表示的,也就是之前说的二进制文件。
接下来通过输出到文本中,验证之前的表述。
#include <stdio.h>
int main()
{
short a = 10000;
FILE * fp = fopen("ascii.txt","w");
fprintf(fp,"%d",a);
fclose(fp);
FILE * fp2 = fopen("bin.txt","w");
fwrite(&a,2,1,fp2);
fclose(fp2);
return 0;
}
分别以文本和二进制的形式输出到ascii.txt
、bin.txt
文件中。以十六进制格式查看。
// ascii.txt
31 30 30 30 30 ;10000
在文本输出中,分别以ASCII码的形式表示了1 0 0 0 0
这5个数字。
// bin.txt
10 27 ;
在二进制输出中,把内存中的储存形式直接输出到了txt文件中。
C语言中文件的输入输出
用到的函数和结构体
FILE 结构体
FILE 结构体是对缓冲区和文件读写状态的记录者,所有对文件的操作,都是通过
FILE 结构体完成的。
fopen 函数
以 Mode 的方式,打开一个 filename 命名的文件,返回一个指向该文件缓冲的 FILE 结构体指针。
Mode
Mode | 处理方式 | 当文件不存在 | 当文件存在时 | 向文件输入 | 从文件输出 |
---|---|---|---|---|---|
“r” | 读取 | 出错 | 打开文件 | 不能 | 可以 |
“w” | 写入 | 建立新文件 | 覆盖原有文件 | 可以 | 不能 |
“a” | 追加 | 建立新文件 | 追加 | 可以 | 不能 |
“r+” | 读取\写入 | 出错 | 打开文件 | 可以 | 可以 |
“w+” | 写入\读取 | 建立新文件 | 覆盖原有文件 | 可以 | 可以 |
“a+” | 读取\追加 | 建立新文件 | 追加 | 可以 | 可以 |
fclose 函数
fclose()用来关闭先前 fopen()打开的文件. 此动作会让缓冲区内的数据写入文件中, 并释放系统所提供的文件资源.
单字符读写
fputc 函数
将 ch 字符,写入文件。
#include <stdio.h>
int main()
{
FILE* fp = fopen("ascii.txt","w");
if(fp == NULL)
{
printf("open error\n");
return -1;
}
for(char ch = 'A';ch<='Z';ch++)
{
fputc(ch,fp);
}
fclose(fp);
return 0;
}
fgetc 函数
从文件流中读取一个字符并返回。
#include <stdio.h>
int main()
{
FILE * fp = fopen("ascii.txt","r");
if(fp == NULL)
{
printf("open error\n");
return -1;
}
char ch;
while((ch = fgetc(fp))!=EOF)
printf("%2c",ch);
fclose(fp);
return 0;
}
读取字符,重点是判断。
在C语言中,或更精确地说成C标准函数库中表示文件结束符(end of file)。在while循环中以EOF作为文件结束标志,这种以EOF作为文件结束标志的文件,必须是文本文件。在文本文件中,数据都是以字符的ASCII代码值的形式存放。我们知道,ASCII代码值的范围是0~127,不可能出现-1,因此可以用EOF作为文件结束标志。
feof 这个函数,是去读标志位判断文件是否结束的。即在读到文件结尾的时候 再去读一次,标志位才会置位,此时再来作判断文件处理结束状态,文件到结尾。如果用于打印,则会出现多打一次的的现象。所以普遍这样使用:
#include <stdio.h>
int main()
{
FILE * fp = fopen ("ascii.txt","w+");
if(fp == NULL)
{
printf("open erroe\n");
return -1;
}
for (char ch = 'a';ch<='z';ch++)
{
fputc(ch,fp);
}
rewind(fp);
char ch;
while ((ch = fgetc(fp))&& !feof(fp)) {
printf("%3c",ch);
}
fclose (fp);
return 0;
}
即先读,判断标志位。因为读到末尾也可能会让标志位置位,进而跳出循环。
文本操作 (一次读一行)
fputs 函数
把 str 指向的字符串写入 fp 指向的文件中。
#include <stdio.h>
int main()
{
FILE * fp = fopen ("ascii2.txt","w");
if(fp == NULL)
{
printf("open erroe\n");
return -1;
}
fputs("abcdefghijk\n",fp);
fputs("123456789\n",fp);
char buf[] = "xyz";
fputs(buf,fp);
fclose(fp);
return 0;
}
fputs 函数
从 fp 所指向的文件中,至多读 length-1 个字符,送入字符数组 str 中,如果在读入 length-1 个字符结束前遇\n 或 EOF,读入即结束,字符串读入后在最后加一个‘\0’字符。
fgets 函数返回有三个条件:
1. 读 n-1 个字符前遇到\n,读取结束。
2. 读 n-1 个字符前遇到 EOF,读取结束。
3. 最多读 n-1 个符,在每读一行后自动追加’\0’。
#include <stdio.h>
int main()
{
FILE * fp = fopen ("ascii2.txt","r");
if(fp == NULL)
{
printf("open erroe\n");
return -1;
}
char buf[1024];
while (fgets(buf,1024,fp) != NULL)
{
printf("%s",buf);
}
fclose(fp);
return 0;
}
二进制操作 (一次读写一块字符)
fwrite 函数
把 buffer 指向的数据写入 fp 指向的文件中
#include <stdio.h>
#include <string.h>
int main()
{
FILE * fpw = fopen ("bin.txt","wb");
if(fpw == NULL)
return -1;
char * p = "china\nis\ngreat";
fwrite(p,1,strlen(p),fpw);
fclose(fpw);
return 0;
}
输出结果
china
is
great
可以看出,不同于文本写入,二进制写入可吧特殊符号如\n
写入。
fread 函数
把 fp 指向的文件中的 数据读到 buffer 中
#include <stdio.h>
#include <string.h>
int main()
{
FILE * fpr = fopen ("bin.txt","rb");
if(fpr == NULL)
return -1;
char buf[1024];
int n;
n = fread(buf,1,1024,fpr);
printf("n =%d\n",n);
for(int i = 0;i<n;i++)
{
printf("%#x\t",buf[i]);
}
fclose(fpr);
return 0;
}
运行结果
0x63 0x68 0x69 0x6e 0x61 0xa 0x69 0x73 0xa 0x67 0x72 0x65 0x61 0x74
其中文本格式中的回车\n
,以0x0a
读回。此时的回车符号被当做一个普通的
数据。
fread/fwrite 函数的输入返回参数
函数声明如下
int fwrite (void *buffer , int num_bytes , int count , FILE *pf)
int fread (void *buffer , int num_bytes , int count , FILE *pf)
参数 | 含义 |
---|---|
buffer | 指向要输入/输出数据存储区的首地址的指针 |
num_bytes | 每个要读/写的字段的字节数 |
count | 要读/写的字段的个数 |
pf | 要读/写的文件指针 |
num_bytes和count两个参数控制的是读写内容的大小。也就是读10个字节的内容,
num_bytes=2
、count=5
与num_bytes=5
、count=2
两组参数的效果在读取内容上是相同的但是考虑到返回参数的含义,我们通常以读写内容的最小单位设定num_bytes的值。应为返回参数有如下定义:成功,返回读/写的字段数;出错或文件结束,返回 0。如果num_bytes的值与实际的读写单位大小不同,会造成返回值无意义或者无法通过返回值判断读写情况。
文件指针移动函数
- rewind : 将文件指针重新指向一个流的开头。
- ftell : 得到流式文件的当前读写位置,其返回值是当前读写位置偏离文件 头部的字节数。
- fseek : 偏移文件指针。