C语言实验6:文件
目录
一、实验要求
1.文件基本操作:
目的:?熟悉文件的基本概念,学会如何打开、关闭文件。
任务:?编写程序实现文件的创建、打开、关闭等基本文件操作,理解文件指针的概念。
2.读写文件操作:
目的:?掌握从文件读取数据和向文件写入数据的基本技能。
任务:?编写程序读取文件内容并在屏幕上显示,然后将一些数据写入文件,检验文件的读写操作是否成功。
3.错误处理和高级操作:
目的:?提高对文件操作的鲁棒性,了解一些高级的文件操作技巧。
任务:?添加错误处理机制,处理文件打开失败等异常情况。尝试使用?fseek?进行文件定位,学会处理二进制文件,实现格式化输入输出。
二、实验原理
1. 文件的基本概念
文件是计算机存储设备上的一组数据,可以是文本、图像、音频等形式的信息。
分为文本文件和二进制文件
文本文件:文本文件包含纯文本,由字符组成,可以用文本编辑器打开查看。在C语言中,文本文件的处理通常涉及到字符的读写。
二进制文件:二进制文件包含以二进制形式编码的数据,可以包含任意类型的数据,如图像、音频、可执行文件(exe)等。在C语言中,处理二进制文件通常需要使用二进制读写操作。
2. 文件指针
文件指针用于跟踪文件位置,通过文件指针,可以确定文件中当前读写的位置。
文件指针通常通过 FILE类型的结构体来表示。
FILE* 指针名;
3. 文件的打开与关闭
3.1 fopen
fopen函数位于C语言中的stdio.h库内和C++中的fstream库,用于打开一个文件,并返回一个指向 FILE
结构体的指针,该指针将被用于后续的文件操作。
函数原型:
FILE *fopen(const char *filename, const char *mode);
如果文件成功打开,则返回一个指向?FILE?结构体的指针。
如果打开失败,则返回?NULL。
参数:
filename
:字符串,指定要打开的文件的路径和名称。mode
:字符串,指定文件的打开模式。常见的模式包括:
"r"
:只读,文件必须存在。"w"
:写入,如果文件存在则截断文件,如果文件不存在则创建新文件。"a"
:追加,写入数据到文件末尾,如果文件不存在则创建新文件。"rb"
,"wb"
,"ab"
:以二进制模式打开文件。"r+"
:读写,文件必须存在。"w+"
:读写,如果文件存在则截断文件,如果文件不存在则创建新文件。"a+"
:读写,写入数据到文件末尾,如果文件不存在则创建新文件。"r+b"
,"w+b"
,"a+b"
:以二进制模式打开文件,允许读写。
3.2 fclose
fclose函数位于C语言中的stdio.h库内和C++中的fstream库,用于关闭之前通过?fopen?函数打开的文件。
函数原型:
int fclose(FILE *stream);
如果文件成功关闭,则返回零(0)。
如果关闭失败,则返回非零值。?
如果文件关闭成功,则返回值为零。非零值通常表示关闭失败,可能是因为文件已经被关闭或者其他原因。
参数:
一个指向已打开文件的指针,该文件是通过 fopen
打开的。
例如:
#define _CRT_SECURE_NO_WARNINGS
#include<fstream>
#include<iostream>
using namespace std;
int main() {
FILE* filePointer = fopen("C:\\Users\\Administrator\\Desktop\\csdn.txt", "r");
if (filePointer == NULL) {
cout<<"无法打开文件"<<endl;
}
else {
cout << "可以打开文件" << endl;
}
FILE* filePointer1= fopen("C:\\Users\\Administrator\\Desktop\\csdn.txt", "w");
cout << filePointer1;
/*
if (fclose(filePointer) == 0) {
cout << "文件成功关闭" << endl;
}
else {
cout << "文件关闭失败"<<endl;
}
*/
if (fclose(filePointer1) == 0) {
cout << "文件成功关闭";
}
else {
cout<<"文件关闭失败";
}
return 0;
}
?由于csdn文件之前并不存在,但在w模式下的fopen作用后结果如下
至于加上注释的那部分代码,由于第一个fopen返回的是NULL,则无法关闭一个不存在的文件,会报错
切记写文件路径时要用"\\"
养成用fclose的好习惯
4. 文件的读写操作
4.1 fread
fread函数用于从文件中读取数据。
函数原型:
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
参数:
ptr
:指向存储读取数据的内存块的指针。size
:每个数据项的字节数。count
:要读取的数据项的数量。stream
:指向FILE
结构体的指针,表示要从中读取数据的文件。
返回实际读取的数据项数量。如果出现错误或到达文件末尾,可能返回一个小于 count
的值,通过 feof
和 ferror
函数可以检查是因为文件末尾还是因为错误。?
feof函数:
- 如果到达了文件末尾,
feof
返回非零值(true)。- 如果未到达文件末尾,
feof
返回零值(false)。
ferror函数:
- 如果发生错误,
ferror
返回非零值(true)。- 如果没有发生错误,
ferror
返回零值(false)。
例如:以上面创建的csdn.txt为例,由于刚创建时该txt中没有内容
则
#define _CRT_SECURE_NO_WARNINGS
#include<fstream>
#include<iostream>
using namespace std;
int main() {
FILE* filePointer;
char buffer[100];
// 以只读方式打开文件
filePointer = fopen("C:\\Users\\Administrator\\Desktop\\csdn.txt", "r");
// 检查文件是否成功打开
if (filePointer == NULL) {
cout<<"无法打开文件";
return 1; // 退出程序
}
// 从文件中读取数据,请求读取100个数据,读入到buffer数组中
size_t bytesRead = fread(buffer, sizeof(char), sizeof(buffer), filePointer);
// 检查读取是否成功
if (bytesRead > 0) {
cout<<"成功读取"<<bytesRead<<"字节的数据";
// 处理读取的数据
for (int i = 0; i < bytesRead; i++) {
cout << buffer[i];
}
}
else {
if (feof(filePointer)) {
cout<<"已达到文件末尾";
}
else if (ferror(filePointer)) {
cout<<"读取错误";
}
}
// 关闭文件
fclose(filePointer);
return 0;
}
?结果为
当我们在文件中手动添加"xiaoming"进去时再调用上述代码
结果为
注意事项:
1. fread?通常用于读取二进制数据,如果读取文本数据,可以使用?fgets?或者其他文本读取函数。
2. 通过计算?size?*?count?可以得到总共读取的字节数。
3. fread?可能在读取的数据量小于请求的数据量时,返回小于?count?的值。这可能是因为到达文件末尾或发生了错误。
4. 在使用?fread?之前,应该确保文件已经被成功打开。5.在文件读取过程中,如果
fread
成功读取的字节数小于请求的字节数,不一定表示文件已经到达末尾,可能是由于文件中数据不足请求的数量,或者发生了一些错误。上例就是,所以只有bytesRead等于0时,才用到feof和ferror。
4.2 fwrite
fwrite函数用于将数据写入文件。
函数原型:
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
参数:
ptr
:指向包含要写入的数据的内存块的指针。size
:每个数据项的字节数。count
:要写入的数据项的数量。stream
:指向FILE
结构体的指针,表示要写入数据的文件。
返回实际写入的数据项数量。如果出现错误,可能返回一个小于 count
的值。?
?还是以上面的csdn.txt为例(不过此时csdn.txt内容为空)
#define _CRT_SECURE_NO_WARNINGS
#include<fstream>
#include<iostream>
using namespace std;
int main() {
FILE* filePointer;
char buffer[] = "Hello, world!";
// 以只写方式打开文件
filePointer = fopen("C:\\Users\\Administrator\\Desktop\\csdn.txt", "w");
// 检查文件是否成功打开
if (filePointer == NULL) {
cout<<"无法打开文件";
return 1; // 退出程序
}
// 将数据写入文件
size_t itemsWritten = fwrite(buffer, sizeof(char), sizeof(buffer), filePointer);
// 检查写入是否成功
if (itemsWritten == sizeof(buffer) / sizeof(char)) {//只能读取整个字符
cout<<"成功写入" <<itemsWritten<<"个数据项";
}
else {
cout<<"写入错误";
}
// 关闭文件
fclose(filePointer);
return 0;
}
结果为:?
注意事项:
1. fwrite?通常用于写入二进制数据,如果写入文本数据,可以使用?fputs?或者其他文本写入函数。
2. 通过计算?size?*?count?可以得到总共写入的字节数。
3. fwrite?可能在写入的数据量小于请求的数据量时,返回小于?count?的值。这可能是因为文件空间不足或者发生了错误。
4. 在使用?fwrite?之前,应该确保文件已经被成功打开,以及数据的来源内存块合法。
5. 如果文件不存在,fwrite?会尝试创建文件;如果文件存在,fwrite?会覆盖文件内容。
?5. 文件的定位
fseek函数用于在文件中定位文件指针的位置。
函数原型:
int fseek(FILE *stream, long int offset, int whence);
参数:
stream
:指向FILE
结构体的指针,表示要定位文件指针的文件。offset
:要移动的字节数,可以为正数、负数或零。whence
:定位的起始位置,可以是SEEK_SET
、SEEK_CUR
或SEEK_END
。
SEEK_SET
:从文件开头计算偏移。SEEK_CUR
:从文件指针当前位置计算偏移。SEEK_END
:从文件末尾计算偏移。
如果成功,返回零。
如果发生错误,返回非零值。
以上述csdn.txt为例,此时里面内容为Hello,world!
#define _CRT_SECURE_NO_WARNINGS
#include<fstream>
#include<iostream>
using namespace std;
int main() {
FILE* filePointer;
char buffer[100],buffer1[100],buffer2[100];
// 以只读方式打开文件
filePointer = fopen("C:\\Users\\Administrator\\Desktop\\csdn.txt", "r");
// 检查文件是否成功打开
if (filePointer == NULL) {
cout<<"无法打开文件";
return 1; // 退出程序
}
// 将文件指针定位到文件的第 5 个字节处(从文件开头计算)
if (fseek(filePointer, 5, SEEK_SET) == 0) {
// 读取文件指针当前位置后的数据
fread(buffer,sizeof(char), sizeof(buffer), filePointer);
cout<<"定位后的数据:"<<buffer<<endl;
}
else {
cout<<"定位失败";
}
// 将文件当前指针定位到文件的第 2 个字节处(从文件开头计算)
//当前指针已经到文件末尾,应归零
fseek(filePointer,0,SEEK_SET );
if (fseek(filePointer, 2, SEEK_CUR) == 0) {
// 读取文件指针当前位置后的数据
fread(buffer1, sizeof(char), sizeof(buffer1), filePointer);
cout << "定位后的数据:" << buffer1<<endl;
}
else {
cout << "定位失败";
}
//读取文件最后两个字节的数据
if (fseek(filePointer, -2, SEEK_END) == 0) {
// 读取文件指针当前位置后的数据
fread(buffer2, sizeof(char), sizeof(buffer2), filePointer);
cout << "定位后的数据:" << buffer2;
}
else {
cout << "定位失败";
}
// 关闭文件
fclose(filePointer);
return 0;
}
结果为:
?
三、实验内容
3.1 文件复制
编写一个程序,要求从一个输入文件中读取内容,并将其复制到一个输出文件中。用户应该能够提供输入文件名和输出文件名。
代码
#define _CRT_SECURE_NO_WARNINGS
#include<fstream>
#include<iostream>
using namespace std;
int main() {
FILE* filePointer,*filePointer1;
char buffer[100],input_name[100],output_name[100];
cout << "请输入输入文件名:";
cin >> input_name;
cout << "请输入输出文件名:";
cin >> output_name;
// 以只读方式打开输入文件
filePointer = fopen(input_name, "r");
// 检查文件是否成功打开
if (filePointer == NULL) {
cout<<"无法打开文件";
return 1; // 退出程序
}
//把输入文件数据读取到buffer中
size_t bytesRead = fread(buffer, sizeof(char), sizeof(buffer), filePointer);
// 检查读取是否成功
if (bytesRead > 0) {
cout << "成功读取" << bytesRead << "字节的数据";
}
else {
if (feof(filePointer)) {
cout << "已达到文件末尾";
}
else if (ferror(filePointer)) {
cout << "读取错误";
}
}
// 关闭文件
fclose(filePointer);
// 以只写方式打开文件
filePointer1 = fopen(output_name, "w");
// 检查文件是否成功打开
if (filePointer1 == NULL) {
cout << "无法打开文件";
return 1; // 退出程序
}
// 将数据写入文件
//至于为什么不是size_t itemsWritten = fwrite(buffer, sizeof(char), sizeof(buffer), filePointer1),是由于buffer的大小并不是实际的大小,只是预分配空间的大小
size_t itemsWritten = fwrite(buffer, sizeof(char), sizeof(char)*bytesRead, filePointer1);
// 检查写入是否成功
if (itemsWritten == bytesRead) {//只能读取整个字符
cout << "成功写入" << itemsWritten << "个数据项";
}
else {
cout << "写入错误";
}
// 关闭文件
fclose(filePointer1);
return 0;
}
截图
分析
文件处理:
- 用户被要求输入输入文件名和输出文件名。
- 打开输入文件以只读方式,检查是否成功打开。
- 读取文件内容到缓冲区中。
- 关闭输入文件。
- 打开输出文件以只写方式,检查是否成功打开。
文件读写:
- 使用
fread
从输入文件读取数据到缓冲区。- 使用
fwrite
将缓冲区的数据写入输出文件。输出:
- 打印读取和写入的字节数。
分析:
- 使用
FILE*
和fopen
进行C风格的文件处理。- 输入文件的内容被读取到固定大小的缓冲区中(大小为100个字符),这可能导致截断文件内容,特别是对于大文件。
- 写入文件时,使用
sizeof(char)*bytesRead
来确保仅写入已读取的字节数。- 如果写入的字节数与已读取的字节数相等,程序认为写入成功。
- 程序中没有检查输入文件是否为空,如果为空,可能导致意外行为。
3.2 单词统计
编写一个程序,要求从一个文本文件中读取内容,并统计文件中每个单词出现的次数。程序应该输出每个单词和其出现的次数,并且不区分大小写。你可以使用一个简单的空格分隔单词。用户应该能够提供输入文件名。
示例:
输入文件内容:
This is a simple example. This example demonstrates word counting.输出:
"this"出现2次
"is"出现1次
"a"出现1次
"simple"出现1次
"example"出现2次
"demonstrates"出现1次
"word"出现1次
"counting"出现1次
本题的思路是,遍历文件内容,如果遇到不符合大写字母和小写字母ASCII码范围的,表明一个单词的结束,如果这个单词第一次出现,则保存到单词数组中,否则该单词次数加1,且全以小写字母为标准。
代码
#define _CRT_SECURE_NO_WARNINGS
#include<fstream>
#include<iostream>
using namespace std;
int main() {
FILE* filePointer;
char buffer[100],input_name[100];
char word_list[100][100] = {'\0'};//单词表
int word_count[100] = { 0 };//单词出现次数
int word_length[100] = { 0 };//单词长度
int count = 0;//单词个数(不包括重复单词个数)
cout << "请输入输入文件名:";
cin >> input_name;
// 以只读方式打开输入文件
filePointer = fopen(input_name, "r");
// 检查文件是否成功打开
if (filePointer == NULL) {
cout<<"无法打开文件";
return 1; // 退出程序
}
//把输入文件数据读取到buffer中
size_t bytesRead = fread(buffer, sizeof(char), sizeof(buffer), filePointer);
// 检查读取是否成功
if (bytesRead > 0) {
cout << "成功读取" << bytesRead << "字节的数据";
}
else {
if (feof(filePointer)) {
cout << "已达到文件末尾";
}
else if (ferror(filePointer)) {
cout << "读取错误";
}
}
//关闭文件
fclose(filePointer);
int index = 0;//每个单词的起始索引
char word[15] = {'\0'};
for (int i = 0; i < bytesRead; i++) {
if ((65 <= buffer[i] && buffer[i] <= 90) || (97 <= buffer[i] && buffer[i] <= 122)) {//如果字符合法
if (65 <= buffer[i] && buffer[i] <= 90) {
word[index++] = buffer[i]+32;
}
else {
word[index++] = buffer[i];
}
}
else {
if (strlen(word) == 0) {//如果字符串为空,跳出循环进行下一轮
continue;
}
//判断此单词第一次出现还是重复出现
int flag = 1;//第一次出现
for (int k = 0; k < count; k++) {
//使用 strcmp 比较字符串时,确保两个字符串都以空字符结尾。
if (strcmp(word, word_list[k]) == 0) {//如果出现重复单词
word_count[k]++;
flag = 0;
break;
}
}
if (flag == 1) {//第一次出现,加入单词表中
for (int j = 0; j < index; j++) {
word_list[count][j] = word[j];
}
word_length[count] = index;
word_count[count]++;
count++;
}
for (int j = 0; j < index; j++) {//字符串清零
word[j] = '\0';
}
index = 0;//重新回到起始点
}
}
for (int i = 0; i < count; i++) {
for (int j = 0; j < word_length[i]; j++) {
cout << word_list[i][j];
}
cout << "出现次数:" << word_count[i] << endl;
}
return 0;
}
截图
?
分析?
文件处理:
- 打开由用户指定的文件进行读取。
- 检查文件是否成功打开。
- 将文件内容读入一个缓冲区。
单词处理:
- 程序使用循环迭代缓冲区中的每个字符。
- 从缓冲区提取单词,将大写字母转换为小写。
- 维护一个数组
word_list
以存储唯一单词,一个数组word_count
以存储每个单词的计数,以及一个数组word_length
以存储每个单词的长度。- 使用嵌套循环检查提取的单词是新单词还是重复单词,并相应地更新数组。
输出:
- 程序打印唯一单词以及它们的计数。
分析:
- 使用
FILE*
和fopen
表明使用了C风格的文件处理,但与C++的iostream混合使用。- 代码似乎使用ASCII值来检查字母字符。
- 单词处理部分采用手动方法,而不是使用C++的字符串功能。
- 如果单词超过15个字符,可能会导致缓冲区溢出。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!