22、一文带你掌握C语言文件操作-创新互联
- 文件操作
- 1 文件相关
- 1.1 文件名
- 1.2 文件类型
- 1.3 文件缓冲区
- 1.4 文件指针
- 2 文件的打开和关闭
- 3 文件的顺序读写
- 3.1 字符输入输出
- 3.2 文本输入输出
- 3.3 读取有格式的文件
- 3.4 二进制输入输出
- 4 文件的随机读写
- `1、fseek`
- `2、ftell`
- `3、rewind`
- 5 文件结束判定
- 被错误使用的feof
一个文件要有一个唯一的标识,以便用户识别和引用。
文件名包含3个部分:文件路径 + 文件名主干 + 文件后缀
c:\code\test.txt
根据数据的组织形式,对于数据文件来说,一般分为二进制文件和文本文件
二进制文件
数据在内存中以二进制的形式存储
文本文件
要求在外存上以ASCII码的形式存储,则需要在存储前转换。
系统会自动的在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”
从内存向磁盘输出数据,会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。
从磁盘向计算机读入数据,则从磁盘文件中读取数据到内存缓冲区,然后再从缓冲区逐个将数据送到程序数据区(程序变量等)
缓冲区的大小根据编译系统确定
1.4 文件指针文件类型指针:简称文件指针
每个被使用的文件,都在内存中开辟了一个相应的文件信息区,用来存放文件的相关(如文件名,文件状态,文件当前位置等),这些信息保存在一个结构体变量中。
该结构体类型是有系统声明的,取名FILE
typdef struct _iobuf FILE
每当打开一个文件时,系统会根据文件的情况,自动创建一个FILE类型的变量,并填充里面的信息,使用者不必关注细节。
一般都是通过一个FILE类型的指针,来维护这个FILE结构的变量,这样使用起来会很方便。
创建一个FILE*的指针变量
FILE* pf; // 文件指针变量
也就是说,通过文件指针,能够找到与它相关联的文件。
2 文件的打开和关闭- 文件在读写之前应该先打开文件
- 在使用结束之后应该关闭文件
在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系
FILE* fopen(const char* filename, const char* mode);
int flcose(FILE* stream);
文件的打开方式有下面几种
文件使用方式 | 含义 | 如果指定文件不存在 |
---|---|---|
“r”(只读) | 为了输入数据,打开一个已经存在的文本文件 | 出错 |
“w”(只写) | 为了输出数据,打开一个文本 | 建立一个新文件 |
“a”(追加) | 向文本文件尾添加数据 | 出错 |
“rb”(只读) | 为了输入数据,打开一个二进制文件 | 出错 |
“wb”(只写) | 为了输出数据,打开一个二进制文件 | 建立一个新文件 |
“ab”(追加) | 向一个二进制文件尾添加数据 | 出错 |
“r+”(读写) | 为了读和写,打开一个文本文件 | 出错 |
“w+”(读写) | 为了读和写,建立一个新的文件 | 建立一个新文件 |
“a+”(读写) | 打开一个文件,在文件尾进行读写 | 建立一个新文件 |
“rb+”(读写) | 为了读和写打开一个二进制文件 | 出错 |
“rw+”(读写) | 为了读和写,新建一个二进制文件 | 建立一个新文件 |
“ab+”(读写) | 打开一个二进制文件,在文件尾进行读写 | 建立一个新文件 |
在写文件名的时候,可以是相对路径,也可以是绝对路径
注意点
3 文件的顺序读写
w
如果存在文件会清空,如果文件不存在,就建立一个新文件。
功能 | 函数名 | 适用于 |
---|---|---|
字符输入函数 | fgetc | 所有输入流 |
字符输出函数 | fputc | 所有输出流 |
文本行输入函数 | fgets | 所有输入流 |
文本上输出函数 | fputs | 所有输出流 |
格式化输入函数 | fscanf | 所有输入流 |
格式化输出函数 | fprintf | 所有输出流 |
二进制输入 | fread | 文件 |
二进制输出 | fwrite | 文件 |
读文件(字符输入)*
int main()
{FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{// 打开失败
printf("%s\n",strerror(errno));
return 0;
}
// 打开成功
// 读文件
printf("%c ", fgetc(pf));
printf("%c ", fgetc(pf));
printf("%c ", fgetc(pf));
printf("%c ", fgetc(pf));
printf("%c ", fgetc(pf));
// 关闭文件
fclose(pf);
pf = NULL; // 相当于free一样
}
写文件(字符输出)
int main()
{FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{// 打开失败
printf("%s\n",strerror(errno));
return 0;
}
// 打开成功
// 写文件
fputc('h', pf);
fputc('e', pf);
fputc('l', pf);
fputc('l', pf);
fputc('o', pf);
// 关闭文件
fclose(pf);
pf = NULL; // 相当于free一样
}
小知识点!!!
键盘和屏幕这两个输入输出流
- 从键盘输入
- 输出到屏幕
键盘和屏幕都是外部设备
- 键盘–标准输入设备
- 屏幕–标准输出设备
这两个是程序默认打开的两个流设备
分别对应着stdin/stdout/stderr
这三个流的类型都是FILE*
也就是说,键盘其实就是对应着stdin
屏幕对应着stdout
因此我们可以这样设计程序
// 因为 fgetc/foutc 可以处理所有的输入输出流,因此键盘和屏幕也可以处理
3.2 文本输入输出char* fgets(char *string, int n, FILE *stream);
string
:从stream读的内容,放到string中去。就是一个缓冲的字符数组(字符串)
n
:最多读取多少个字符注意的是 string 在读取之后,本身就有一个换行,这个换行本身就是文件里面的,只不过换行是空白的隐藏起来了。
读文件
int main()
{char buf[1024] = {0};
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{printf("%s\n", strerror(errno));
return 0;
}
// 读文件
fgets(buf,1024, pf);
printf("%s", buf);
// puts(buf); 如果这里使用 puts,puts天生就会在末尾加换行,会出现多个空行的情况
fgets(buf, 1024, pf);
printf("%s", buf);
fclose(pf);
pf = NULL;
return 0;
}
写文件
int fputs(const char* string, FILE * stream);
int main()
{char buf[1024] = {0};
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{printf("%s\n", strerror(errno));
return 0;
}
// 写文件
fputs("hello 你好\n", pf);
fputs("hello world", pf);
fclose(pf);
pf = NULL;
return 0;
}
操作键盘和屏幕读取输出一行
int main()
{char buf[1024];
fgets(buf,1024,stdin);// 从标准输入流读取
fputs(buf, stdout);// 输出到标准输出流
return 0;
}
这种写法相当于
gets(buf); puts(buf);
int printf(const char* format [,argument]...);
// 其实这里默认是针对 stdout的
int fprintf(FILE* stream, const char* format[,argument]...);
除了比printf
多了一个文件流参数之外,其他都一样。
其实printf
默认是针对stdout
的,也就是说它的流是stdout
fprintf输出函数 写文件
struct S
{int n;
float score;
char arr[10];
};
int main()
{struct S s[2] = {{100, 3.14f, "hello" }, {200,8.88f,"world"} };
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{return 0;
}
// 格式化写文件
fprintf(pf, "%d %f %s\n", s[0].n, s[0].score, s[0].arr);
fprintf(pf, "%d %f %s\n", s[1].n, s[1].score, s[1].arr);
// 关闭
fclose(pf);
pf = NULL;
}
fscanf 输出读文件
struct S
{int n;
float score;
char arr[10];
};
int main()
{struct S s[2] = {0};
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{return 0;
}
// 格式化读文件 -- 首先要设置好存这个数据的变量 或者 结构体
// 一定注意到在 scanf时,变量要用其地址来接收
fscanf(pf, "%d %f %s", &s[0].n, &s[0].score, s[0].arr);
printf( "%d %f %s", s[0].n, s[0].score, s[0].arr);
fscanf(pf, "%d %f %s", &s[1].n, &s[1].score, s[1].arr);
printf("%d %f %s", s[1].n, s[1].score, s[1].arr);
// 关闭
fclose(pf);
pf = NULL;
}
标准输入输出流中读取
struct S
{int n;
float score;
char arr[10];
};
int main()
{struct S s[2] = {0};
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{return 0;
}
// 格式化读文件 -- 首先要设置好存这个数据的变量 或者 结构体
// 一定注意到在 scanf时,变量要用其地址来接收
fscanf(stdin, "%d %f %s", &s[0].n, &s[0].score, s[0].arr);
fprintf(stdout, "%d %f %s", s[0].n, s[0].score, s[0].arr);
// 关闭
fclose(pf);
pf = NULL;
}
scanf/printf是针对标准输入输出流的格式化输入输出语句
fscanf/fprintf是针对所有输入输出流的格式化输入输出语句
sscanf/sprintf 是针对字符串的格式化输入输出,从字符串中读,输出到字符串中去
int main()
{struct S s = {100, 3.14f, "hello"};
struct S tmp = {0};
char buf[1024] = {0};
// 把格式化的数据转换成字符串,存储到buf
sprintf(buf, "%d %f %s", s.n, s.score, s.arr);
// 从buf中读取格式化的数据到tmp中
sscanf(buf, "%d %f %s", &(tmp.n), &(tmp.float), tmp.arr);
}
3.4 二进制输入输出把结构体学生的信息存入到文件中去,怎么写呢?
size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
buffer: 要写的内容
size:元素的大小
count:元素的个数
struct S
{char name[20];
int age;
double score;
};
int main()
{struct S s = {"张三", 20, 64.5};
FILE* pf = fopen("test.txt", "wb");
if (pf == NULL)
{return 0;
}
// 二进制写文件
fwrite(&s, sizeof(struct S), 1, pf);
// 关闭
fclose(pf);
pf = NULL;
}
fread 读二进制文件
size_t fread(const void* buffer, size_t size, size_t count, FILE* stream);
int main()
{//struct S s = {"张三", 20, 64.5};
struct S tmp = {0};
FILE* pf = fopen("test.txt", "rb");
if (pf == NULL)
{return 0;
}
// 二进制写文件
//fwrite(&s, sizeof(struct S), 1, pf);
// 二进制读文件
fread(&tmp, sizeof(struct S), 1, pf); // 二进制读的文件,直接读给这么大的内存空间
printf("%s %d %f\n", tmp.name, tmp.age, tmp.score);
// 关闭
fclose(pf);
pf = NULL;
}
4 文件的随机读写1、fseek
根据文件指针的位置和偏移量来定位文件指针
int fseek(FILE * stream, long int offset, int origin);
offset
:偏移量。单位是字节
- 正值代表向后偏移
- 负值代表向前偏移
origin
:文件指针的当前位置
SEEK_CUR
:文件指针当前位置SEEK_END
:文件末尾(在最后一个字符的后面,而不是最后一个字符)SEEK_SET
:文件起始位置
通过调整文件指针,以当前位置为起点,根据偏移量来调整。
int main()
{ FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{return 0;
}
// 定位文件指针
// 我们默认的 test.txt 里面存放的数据是 abcdef
fseek(pf, 2, SEEK_CUR); // 从当前位置向后偏移 2 字节,就是从 c 开始
//fseek(pf, 2, SEEK_CUR); 从末尾向前偏移 2 字节,e
// 读取文件
int ch = fgetc(pf);
printf("%c\n", ch); // c
// 关闭
fclose(pf);
pf = NULL;
}
2、ftell
返回文件指针相对于起始位置的偏移量
long int ftell(FILE* stream);
int main()
{ FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{return 0;
}
// 定位文件指针
// 我们默认的 test.txt 里面存放的数据是 abcdef
fseek(pf, -2, SEEK_END);
int pos = ftell(pf);
printf("%d\n", pos); // 4
// 读取文件
// 关闭
fclose(pf);
pf = NULL;
}
3、rewind
让文件指针的位置回到文件的起始位置
void rewind(FILE* stream);
int ch = fgetc(pf);
printf("%c\n", ch);
rewind(pf);
printf("%c\n", ch); // 这里依然是a
5 文件结束判定
被错误使用的feof在文件读取过程中,不能用feof
函数的返回值直接用来判断文件是否结束
这个是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束
- 文本文件读取是否结束,判断返回值是否为
EOF
或者NULL
fgetc
判断是否为EOFfgets
判断是否为NULL
- 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数
fread
判断返回值是否小于实际要读的个数
while((ch = fgetc(pf)) != EOF)
{putchar(ch);
}
if (ferror(pf)) // 判断是否遇到错误而退出
{printf("error\n");
}else if(feof(of)) // 判断是否遇到 EOF而退出
{printf("end of file \n");
}
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
文章标题:22、一文带你掌握C语言文件操作-创新互联
URL链接:http://scyanting.com/article/eedoh.html