目录
前言
文件操作在很多C书上都不是重点,所以导致很多人在学习的时候从心理上就排斥它,觉得它不重要,甚至直接跳过不学了。但事实上文件操作很重要。
文件操作很重要,而且事实上也不难。所以一定要重视,一定静下心来好好学习。
以上是原作者的话,此篇是一个章节的浓缩版,篇幅有点长。
正文
文件类型指针
FILE *fp ,fp就是文件类型指针。其中FILE是在<stdio.h>库中定义的。
struct _iobuf{ //iobuf输入输出缓冲区input output buffer.
char *_ptr;//位置指针, 指向缓冲区中下一个待操作的字节。
int _cnt;
char *_base;//位置指针指向的第一个地址。
int _flag;
int __file;
int _charbuf;
int _bufsiz;
char *_tmpfname;//临时文件名
};
typedef struct _iobuf FILE;//FILE就此诞生了,跟我们自己建造结构体一样啊啊
fp的定义一般用fopen()来定义。fopen()函数打开一个文件后,返回就是存储该文件信息的地址。
文件的打开与关闭
fopen()与fclose(),它们的家也是<stdio.h>,f是“file”的缩写。它们是一对CP。一个专业开荒,一个专业收尾。
fopen(文件名,打开方式),fclose(文件)。
使用示例:
1 #include <stdio.h>
2
3 int main(int argc, char *argv[])
4 {
5 FILE *fp;
6 fp = fopen("test.txt", "r");
7
8 if(fp == NULL){
9 printf("开了个寂寞!\n");
10
11 }else{
12 printf("呦,厉害啊!你咋知道要提前新建一个这样的文件咧?!\n");
fclose(fp);
}
13
14 return 0;
15 }
16
~
打开方式 | 含义 |
r | 只读,打开文本文件,文件不存在时不会创建文件 |
r+ | 可读可写,打开文本文件,文件不存在时不会创建 |
w | 只写,打开文本文件,文件不存在时自动创建文件 |
w+ | 可读可写,打开文本文件,文件不存在时自动创建文件 |
a | 只追加,接着文本文件末尾增加数据,文件不存在时自动创建文件。 |
a+ | 可读可追加,接着文件末尾写,文本文件不存在时自动创建文件。 |
rb | 只读,打开二进制文件,文件不存在时不会创建文件 |
rb+ | 可读可写,打开二进制文件,文件不存在时不会创建 |
wb | 只写,打开二进制文件,文件不存在时自动创建文件 |
wb+ | 可读可写,打开二进制文件,文件不存在时自动创建文件 |
ab | 只追加,打开二进制文件,接着文件末尾增加数据,文件不存在时自动创建文件 |
ab+ | 可读可追加,接着文件末尾写,文件不存在时自动创建文件 |
- 注意:有人“r” 的地方,不创建文件,没人了,就嘿嘿……(只是一种记忆法,不喜勿喷啊!)
- 读取文件时打开文件的方式一定要与写入文件时打开文件的方式相同。(是不是感觉很绕?之所以这样,据说是因为C的神兽指针决定的。也许多用用会感受更深!)
读文件 | 写文件 |
r+ | w+ |
rb | wb |
rb+ | wb+ |
文件的基本读写操作
名称 | 原型 | 使用格式 | 含义 | 备注 |
---|---|---|---|---|
fputc( ) | int fputc(int c, FILE *stream) | fputc(ch, fp) | 将字符写到文件指针所指向的文件中(要求文件可写) | 出错返回EOF |
fgetc( ) | int fgetc(FILE *stream) | fgetc(fp) | 从文件指向的指针中读取一个字符(要求文件可读) | 出错与读到末尾返回EOF |
feof( ) | int feof(FILE *stream) | feof(fp) | 到达文件末尾返回非0值,否则返回0。 | 天生反骨,程序正常结束返回0,它非不正常返回0。所以一般前面加个非来用,如:if(!feof(fp)) |
fscanf( ) | inti fscanf(FILE *stream, "输出控制符“, 输出参数) | fscanf(fp,“%d”, i) | 要想从文件读取数据,先用rewind,然后再读 | |
fprintf( ) | int fprintf(FILE *stream,“输出控制符”, 输出参数) | fprintf(fp,“%d”,i) | 向文件写完数据之后,指针在末尾 | |
fgets( ) | char *fgets(char *s, int size, FILE *stream) | fgets(str, n, fp) | 从fp所指向的文件中一次读取n-1个字符赋给字符数组str (要求文件可读)直到读完。 | 1.只认文本文件,2.读到文件末尾或失败返回NULL |
fputs() | int fputs(const char *s , FILE *stream) | fputs(str, fp) | 将str中的内容写到fp所指向的文件中(要求文件可写) | str还可以直接写字符串,fputs(“字符串”, fp) |
fread( ) | size_t fread(const void *ptr, size_t size, size_t count, FILE *stream) | fread(&stud, sizeof(struct STUDENT), count, fp); | 数据快读写函数 | |
fwrite( ) | size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream) | fwrite(&stud, sizeof(struct STUDENT), count, fp); | 数据块读写函数,出错或末尾不等于count(!=count) | |
fseek( ) | int fseek(FILE *stream, long offset, int base); | fseek(fp,0,0) | base可以为0、1、2或SEEK_SET (开头)SEEK_CUR (当前位置)SEEK_END(末尾) | 控制位置的"int"函数,成功返回0,失败返回-1。(控制系) |
rewind( ) | void rewind(FILE *stream) | rewind(fp) | 控制系极端,单一功能,指向文件开头 | |
ftell( ) | long ftell(FILE *stream); | ftell(fp) | 首先将指针移到末尾,然后用ftell就能得出该文件总的字节数。 |
- fprintf与fscanf了解一下就好。
- fputcfgetc与fputsfgets相比,后者效率更高,如果遇到图片类似二进制文件就是前者。
- fread与fwrite很重要,所以下一篇开单章笔记。
fgets和fputs代码示例:
1 #include <stdio.h>
2 #include "dbg.h"
3 #define N 10
4
5 int main(int argc, char *argv[])
6 {
7 FILE *fp1, *fp2;
8 char str[N];
9
10 check((fp1 = fopen("hello.txt", "r")), "Failed open :hello");
11 check((fp2 = fopen("world.txt", "a+")), "Failed open");
12 while(!(feof(fp1)))
13 {
14 fgets(str, N, fp1);
15 fputs(str, fp2);
16 }
17
18 rewind(fp2);
19 while(! feof(fp2))
20 {
21 fgets(str, N, fp2);
22 fputs(str, stdout);//out the stream = printf("%s", str)
23 }
24
25 printf("\n");
26 fclose(fp1);
27 fclose(fp2);
28 return 0;
29 error:
30 return -1;
31 }
32
fgetc与fputc代码示例:
#include <stdio.h>
#include <stdlib.h>
#include "dbg.h"
int main(int argc, char *argv[])
{
FILE *fp1, *fp2;
char ch;
check(fp1 = fopen("qjf.jpg", "rb"), "Can't open the qjf.jpg!");
check(fp2 = fopen("copy.jpg", "wb+"), "Can't open the copy.jpg");
while(!feof(fp1))
{
ch = fgetc(fp1);
fputc(ch, fp2);//这边读来那边写吗,漂亮!
}
fclose(fp1);
fclose(fp2);
return 0;
error:
return -1;
}
看完以上两个代码,有什么感想,妥妥的磁带播放器啊,有木有?为什么好像更喜欢fgetc与fputc的组合。
看过上面两个程序,指针好像都不香了。
#include <stdio.h>
#include <stdlib.h>
#include "dbg.h"
void putC(FILE *pp, char *name);
void get_C(FILE *gp, char *name);
int main(int argc, char *argv[])
{
FILE *fp;
char filename[20];
putC(fp,filename);
// fgetc read
get_C(fp,filename);
return 0;
}
void putC(FILE *pp, char *name)
{
char ch;
printf("please input the filename you want to write:");
scanf("%s", name);
getchar();//clear '\n'
check((pp = fopen(name, "w+")), "Can't open the file!\n")
printf("Please input the sentences you want to write:");
while((ch = getchar()) != '\n')
{
fputc(ch, pp);
}
fclose(pp);
return 0;
error:
return -1;
}
void get_C(FILE *gp, char *name)
{
char ch;
gp = fopen(name, "r+");
printf("Your sentences is:");
while(( ch = fgetc(gp)) != EOF )
{
printf("%c", ch);
}
printf("\n");
check(feof(gp), "read error.");//dbg: if(!(A)),that is 0 ="read error".
return 0;
error:
return -1;
}
fgetc判断二进制是否为空的代码:
1 #include <stdio.h>
2 #include "dbg.h"
3
4 int main(int argc, char *argv[])
5 {
6 FILE *fp;
7 check(fp=fopen("test", "wb+"), "Can't open the file!");
8
9 fputs("0123write somestuff!",fp);
10 rewind(fp);
11 if (fgetc(fp) == EOF)
12 {
13 if(feof(fp)){ //到达文件末尾返回非0, 否则返回0
14 printf("文件为空\n");
15 }else{
16 printf("error\n");
17 }
18 }else{
19 printf("文件不为空\n");
20 }
21
22 fclose(fp);
23 printf("Congratulations!\n");
24 return 0;
25 error:
26 return -1;
27 }
后语
- 前言是《手把手教你C语言》的部分原话摘抄,所以本章学习笔记以之为重点。
- 不知是天气的燥热,还是学习本身自带的一种属性,学了一半左右的练习后,内心又开始浮躁了,想跳过,一跳就想跳个好几个练习。(从ex24跳到27)扪心自问,我急啥呢?我跳过的不重要吗?哦,我着急的想证明自己我程序员入门了,我觉得我懂指针了,我想感受一下大项目。然而我却想将文件操作跳过去,有个理由是前面提到过一些 一起笨笨的学C——007-CSDN博客(抑或是“笨C”此篇很简洁,很枯燥?)但是事实上我并没有深度学习及掌握。
- 所以,特开此篇,静下心来学习文件操作。笨的ex24也的确是个记忆练习。
- 回想初心:首先我想入门一个“程序员”,其次我想帮上我的朋友抑或有更多的共同语言,最后当一名程序猿是不可能了,但是有这么个一技之长应该不是坏事吧!
-
文件分类:
经常用的是ASCII码文件与和二进制文件。
-
scanf很逊?
“笨办法”里这么说的,也许作为菜鸟考虑这有点多,现在应该熟练使用文件函数,熟练编程,哪个好不好,暂时了解,以后再回头看看吧!
-
scanf函数在一些特定情况下可能会存在一些问题,导致被认为相对较差。以下是一些常见的问题和限制:
-
输入缓冲区溢出:在使用scanf时,输入的数据会被存储在内部缓冲区中,如果输入的数据长度超过了缓冲区的大小,会导致缓冲区溢出,可能导致程序崩溃或发生安全漏洞。
-
错误处理困难:scanf函数无法提供有效的错误处理机制。如果用户输入的数据类型与格式字符串不匹配,scanf函数会返回错误码但不会清空输入缓冲区,导致输入缓冲区中的错误数据仍然存在,可能会导致后续输入出现问题。
-
缺乏灵活性:scanf函数的格式字符串限制了输入的格式,无法处理一些复杂的输入场景,如动态分配空间的字符串输入等。
-
字符串输入的问题:scanf函数在读取字符串时,会在遇到空格或换行符时停止读取,这可能导致输入不完整或无法读取包含空格的字符串。
-
"scanf很逊"是一种主观的评价,因为它存在一些局限性和问题。然而,scanf函数在某些简单的输入场景下仍然是一个方便的选择。下面是一个示例代码,演示了scanf的基本用法:
#include <stdio.h> int main() { int num; printf("请输入一个整数: "); scanf("%d", &num); printf("您输入的整数是: %d\n", num); return 0; }
在上述示例中,程序会提示用户输入一个整数,然后使用scanf函数将输入的整数存储到变量num中,并最终将其打印出来。
然而,需要注意的是,scanf函数在处理字符串、浮点数等复杂的输入时可能会有一些问题。为了更好地处理输入,可以考虑使用fgets函数读取一行文本,然后使用sscanf函数从读取的字符串中提取所需的数据。这种组合能够提供更好的错误处理和字符串输入的灵活性。
以下是一个使用fgets和sscanf的示例代码:
#include <stdio.h> int main() { char input[100]; int num; printf("请输入一个整数: "); fgets(input, sizeof(input), stdin); sscanf(input, "%d", &num); printf("您输入的整数是: %d\n", num); return 0; }
在上述示例中,首先使用fgets函数从标准输入中读取一行文本,然后使用sscanf函数将文本中的整数提取出来,并存储到变量num中。这种方式可以更好地处理输入错误和字符串输入的问题。
-