🦄个人主页:小米里的大麦-优快云博客
🎏所属专栏:C语言_小米里的大麦的博客-优快云博客
🎁代码托管:小米里的大麦 - Gitee.com
⚙️操作环境:Visual Studio 2022
目录
一、引言
在 C 语言编程中,处理错误是非常重要的,尤其是当涉及到文件操作时。本文将详细介绍三个常用的错误处理函数:
strerror
,perror
, 和fopen
的使用方法。这些函数可以帮助开发者更好地理解和处理程序中发生的错误。
错误处理是编程中不可或缺的一部分。当程序遇到问题时,如文件无法打开或内存分配失败,程序需要能够捕捉这些错误并给出适当的反馈。C 语言提供了多种机制来处理这类错误,其中包括
strerror
和perror
函数,它们可以提供详细的错误信息。
此外,我们还将讨论
fopen
函数,这是 C 语言中用于打开文件的标准函数。当fopen
函数无法成功打开文件时,我们需要能够正确地处理这种失败,并向用户或开发者提供清晰的错误信息。
二、fopen
函数
解释:
fopen
是一个用于打开文件的标准库函数。它可以读取、写入或同时读写文件。如果文件不存在,fopen
可以创建新文件;如果文件存在,fopen
可以打开并重用它。即:fopen
是一个用于打开文件的函数,它可以在不同的模式下打开文件。如果我们尝试打开一个不存在的文件,或者因为权限问题无法打开文件,fopen
会返回NULL
。因此,我们通常会检查fopen
的返回值是否为NULL
来判断文件是否成功打开。
注意:
- 文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
- 在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。
- ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件。
还是先来看看fopen - C++ Reference上的介绍:
1. 函数原型
#include <stdio.h>
FILE *fopen(const char *filename, const char *mode);
2. 参数
filename
: 要打开的文件的路径和名称。mode
: 指定了打开文件的方式,常见的模式有"r"
(只读)、"w"
(清空并写入新的数据)、"a"
(追加到文件末尾)和"r+"
(读写)等。
文件使用方式
|
含义
|
如果指定文件不存在
|
“ r ”(只读)
|
为了输入数据,打开一个已经存在的文本文件
|
出错
|
“ w ”(只写)
|
为了输出数据,打开一个文本文件
| 建立一个新的文件 |
“ a ”(追加)
|
向文本文件尾添加数据
|
建立一个新的文件
|
“ rb ”(只读)
|
为了输入数据,打开一个二进制文件
|
出错
|
“ wb ”(只写)
|
为了输出数据,打开一个二进制文件
|
建立一个新的文件
|
“ ab ”(追加)
|
向一个二进制文件尾添加数据
|
出错
|
“ r+ ”
(读写)
|
为了读和写,打开一个文本文件
|
出错
|
“ w+ ”
(读写)
|
为了读和写,建议一个新的文件
|
建立一个新的文件
|
“ a+ ”
(读写)
|
打开一个文件,在文件尾进行读写
|
建立一个新的文件
|
“ rb+ ”
(读写)
|
为了读和写打开一个二进制文件
|
出错
|
“ wb+ ”
(读写)
|
为了读和写,新建一个新的二进制文件
|
建立一个新的文件
|
“ ab+ ”
(读写)
|
打开一个二进制文件,在文件尾进行读和写
|
建立一个新的文件
|
3. 代码示例
#include <stdio.h>
int main() {
FILE* fp = fopen("test.txt", "r");
if (fp == NULL) {
// 处理打开失败的情况
return 1;
}
// 文件成功打开后的操作...
fclose(fp);//关闭文件
return 0;
}
三、strerror
函数
strerror
函数是用来获取错误描述字符串的。当fopen
或者其他函数失败时,它们通常会设置全局变量errno
来表示错误的原因。strerror
可以将errno
的值转换成一个描述性的字符串,从而帮助我们了解具体的错误信息。
1. 函数原型
#include <string.h>
const char *strerror(int errnum);
2. 参数
errnum
: 错误码,通常直接使用errno
。
3. 代码示例
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main() {
FILE* fp = fopen("test.txt", "r");
if (fp == NULL) {
fprintf(stderr, "Failed to open file: %s\n", strerror(errno));
return 1;
}
// 文件成功打开后的操作...
fclose(fp);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main() {
FILE *file;
int errorNumber;
file = fopen("example.txt", "r");
if (file == NULL) {
// 获取错误编号
errorNumber = errno;
printf("无法打开文件: %s\n", strerror(errorNumber));
exit(EXIT_FAILURE);
}
// 如果文件成功打开,我们可以继续做其他事情...
fclose(file);
return 0;
}
在这段代码中,我们尝试打开名为 "example.txt" 的文件。
如果文件打开失败,我们将获取当前的 errno 值,
然后使用 strerror 函数将该值转换成描述性的错误字符串,并将其打印到标准输出。
请注意,上述代码使用了标准 C 库中的头文件和函数,确保在编译时链接相应的库。
此外,strerror 函数返回的字符串可能因操作系统而异。
四、perror
函数
perror
函数与strerror
类似,但它会自动将错误描述打印到标准错误流(stderr
)。此外,perror
还会在错误描述前加上一个自定义的消息,通常用来指定出错的函数名称。也就是主要用于打印错误信息,通常是在系统调用失败后使用,比如文件操作、内存分配等失败时。它会在标准错误输出(通常是终端)上打印一个描述性的错误消息。
1. 函数原型
#include <stdio.h>
void perror(const char *msg);
2. 参数
msg
: 这是一个字符串,通常表示出错的函数名称或其他相关信息,你可以传递任何描述性文字给它,比如"无法创建文件"
。
perror
函数的参数
perror
函数接受一个字符串参数。这个字符串是用来描述发生了什么错误的。例如:
"无法打开文件"
"内存分配失败"
参数可以是:
- 任何字符串:可以是一个固定的字符串,比如
"无法打开文件"
。- 动态生成的字符串:可以是根据程序运行时的状态动态生成的字符串,比如结合变量使用,如
"无法打开文件 %s"
。
3. 代码示例
#include <stdio.h>
#include <errno.h>
int main() {
FILE* fp = fopen("test.txt", "r");
if (fp == NULL) {
perror("fopen");
return 1;
}
// 文件成功打开后的操作...
fclose(fp);
return 0;
}
直接使用字符串:
perror("无法打开文件");
使字符串变量:
char *errorMessage = "内存分配失败";
perror(errorMessage);
动态生成字符串:
char *filename = "example.txt";
fprintf(stderr, "无法打开文件 %s: ", filename);
perror("");
包含文件名的错误消息
在这个示例中,我们将使用 fprintf 向 stderr 输出文件名,然后调用 perror 打印出实际的错误描述。
#include <stdio.h>
#include <stdlib.h>
void openFileWithMessage(const char *filename) {
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
fprintf(stderr, "无法打开文件 %s: ", filename);
perror(""); // 打印错误描述
exit(EXIT_FAILURE); // 终止程序执行
}
fclose(fp);
}
int main() {
openFileWithMessage("不存在的文件.txt");
return 0;
}
包含错误代码的错误消息
在这个示例中,我们将构造一个包含错误代码的错误消息。
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // 用于使用 strerror
void openFileWithErrorNumber(const char *filename) {
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
fprintf(stderr, "错误 %d: 无法打开文件 %s: ", errno, filename);
perror(""); // 打印错误描述
exit(EXIT_FAILURE); // 终止程序执行
}
fclose(fp);
}
int main() {
openFileWithErrorNumber("不存在的文件.txt");
return 0;
}
小结
perror
函数括号里需要填写的是一个描述性的字符串,用来说明发生了什么错误。- 字符串可以是任何你想要的描述性文本。
- 字符串可以是固定的字符串,也可以是动态生成的字符串。
五、总的来说(可直接跳转至此处开始)
1. 错误处理的基础
- 在编程时,我们经常会遇到一些错误或异常情况。例如,打开文件失败、内存分配不足等。当这些错误发生时,程序通常会返回一个错误码,以便开发者能够识别出发生了什么问题。
- 在 C 语言中,错误码通常是通过全局变量
errno
来传递的。errno
是一个整数变量,它会在系统调用或者库函数失败时被设置为特定的值来表示不同的错误类型。
errno
变量
errno
是一个预定义的全局变量,在<errno.h>
头文件中定义。每次系统调用或库函数失败后,它会被设置成一个表示错误原因的整数值。每个错误码都有一个对应的描述字符串,可以帮助理解错误的具体含义。
strerror
函数
strerror
函数的作用是将errno
的值转换成相应的错误描述字符串。这样你就可以通过打印这个字符串来告诉用户发生了什么错误。
注意事项
strerror
返回的字符串可能被其他调用覆盖,因此如果需要保留错误消息,应该复制到另一个字符串中。- 不同的操作系统可能有不同的错误码和错误消息。
2. 这里数据太多,所以我整理了常见的错误码及其描述:
strerror
函数返回与给定错误码 (errno
) 相对应的错误描述字符串。每个错误码都有一个特定的意义,并且与之对应有一个描述性的字符串。这些错误码及其描述字符串是由操作系统定义的,并且在不同平台上可能会有所不同。然而,大多数常见的错误码和它们的含义在各种 Unix 和类 Unix 系统(包括 Linux)中都是通用的。
请注意,下面列出的错误码和描述并不是所有可能的错误码,而且具体的错误码数值和描述可能会根据不同的系统和平台有所不同。在实际使用中,你可以查阅你所使用的系统的文档来获取更准确的信息。
3. 代码示例
3.1 示例一
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main() {
int error_number = 2; // 假设这里发生了错误 2: No such file or directory
const char *error_string = strerror(error_number);
printf("Error description: %s\n", error_string);
return 0;
}
运行上述代码会输出:
Error description: No such file or directory
3.2 示例二
int main() {
printf("%s\n", strerror(0)); // No error
printf("%s\n", strerror(1)); // Operation not permitted
printf("%s\n", strerror(2)); // No such file or directory
printf("%s\n", strerror(3)); // No such process
printf("%s\n", strerror(4)); // Interrupted function call
printf("%s\n", strerror(5)); // Input/output error
return 0;
}
strerror(0)
:No error (没有错误)strerror(1)
:Operation not permitted (操作未被允许)strerror(2)
:No such file or directory (没有这样的文件或目录)strerror(3)
:No such process (没有这样的进程)strerror(4)
:Interrupted function call (中断的函数调用)strerror(5)
:Input/output error (输入/输出错误)
4. 日常使用示例
#include <stdio.h> // 包含标准输入输出头文件
#include <stdlib.h> // 包含标准库函数头文件,如 calloc 和 free
int main()
{
int *p = (int*)calloc(10, sizeof(int)); // 分配足够的内存来存储10个整数,并初始化为0
if (NULL == p) // 如果内存分配失败(p为NULL)
{
perror("calloc"); // 打印错误信息
return 1; // 返回非零值,表示程序异常终止
}
// 使用
int i = 0; // 初始化循环变量
for (i = 0; i < 10; i++) // 循环遍历数组中的每个元素
{
printf("%d ", *(p + i)); // 输出第i个元素的值
}
// 释放内存
free(p); // 释放之前分配的内存
p = NULL; // 将指针设置为NULL以避免悬挂指针问题
return 0; // 正常退出程序
}
这段代码的主要功能如下:
使用 calloc 函数分配足够空间来存放10个整数,并且将这些整数初始化为0。
检查内存是否成功分配,如果没有分配成功,则打印错误信息并退出程序。
使用一个循环来遍历这个整数数组,并打印每个整数的值。
使用 free 函数释放之前分配的内存。
将指针 p 设置为 NULL 以避免后续使用该指针时产生悬挂指针的问题。
最后返回0表示程序正常结束。
六、总结
综合使用的重要性
- 错误处理的最佳实践:在文件操作中,结合使用
fopen
,strerror
, 和perror
是一种很好的错误处理策略。- 提高程序健壮性:通过检查错误条件并提供清晰的错误信息,可以提高程序的稳定性和可靠性。
- 易于维护:良好的错误处理机制使得代码更容易理解和维护,尤其是在大型项目中。
处理文件错误是一项基本但又复杂的任务。
fopen
,strerror
, 和perror
这些工具的正确使用能够显著提升程序的质量,使开发者能够更加高效地解决问题。通过掌握这些技巧,开发者可以构建更加健壮、可靠的应用程序。