C语言文件操作、错误处理及UNIX系统调用详解
立即解锁
发布时间: 2025-08-22 00:24:53 阅读量: 3 订阅数: 6 


C语言编程基础与实践
# C语言文件操作、错误处理及UNIX系统调用详解
## 1. 文件指针释放与错误处理
在进行文件操作时,当文件指针不再需要时,释放它们是个不错的做法。同时,对于输出文件使用 `fclose` 还有另一个原因,它会刷新 `putc` 收集输出的缓冲区。当程序正常终止时,会为每个打开的文件自动调用 `fclose`。若不需要 `stdin` 和 `stdout`,也可以将它们关闭,还能通过库函数 `freopen` 重新分配。
### 1.1 错误处理
在处理文件操作时,错误处理至关重要。以 `cat` 程序为例,其错误处理并不理想。若某个文件因某种原因无法访问,诊断信息会打印在拼接输出的末尾。若输出到屏幕,这或许可以接受,但如果输出到文件或通过管道传输到另一个程序,就不太合适了。
为了更好地处理这种情况,引入了第二个输出流 `stderr`,它和 `stdin`、`stdout` 一样被分配给程序。即使标准输出被重定向,写入 `stderr` 的输出通常也会显示在屏幕上。
下面是修改后的 `cat` 程序,将错误信息写入标准错误:
```c
#include <stdio.h>
/* cat: concatenate files, version 2 */
main(int argc, char *argv[])
{
FILE *fp;
void filecopy(FILE *, FILE *);
char *prog = argv[0]; /* program name for errors */
if (argc == 1 ) /* no args; copy standard input */
filecopy(stdin, stdout);
else
while (--argc > 0)
if ((fp = fopen(*++argv, "r")) == NULL) {
fprintf(stderr, "%s: can't open %s\n",
prog, *argv);
exit(1);
} else {
filecopy(fp, stdout);
fclose(fp);
}
if (ferror(stdout)) {
fprintf(stderr, "%s: error writing stdout\n", prog);
exit(2);
}
exit(0);
}
```
该程序通过两种方式发出错误信号:
- 由 `fprintf` 产生的诊断输出会发送到 `stderr`,这样它会显示在屏幕上,而不会消失在管道或输出文件中。消息中包含了程序名(来自 `argv[0]`),若该程序与其他程序一起使用,就能识别错误的来源。
- 程序使用标准库函数 `exit`,调用时会终止程序执行。`exit` 的参数可供调用该程序的任何进程使用,因此使用该程序作为子进程的另一个程序可以测试其成功或失败。通常,返回值 0 表示一切正常,非零值通常表示异常情况。`exit` 会为每个打开的输出文件调用 `fclose`,以刷新任何缓冲输出。
在 `main` 函数中,`return expr` 等同于 `exit(expr)`。`exit` 的优势在于它可以从其他函数调用,并且可以使用像第 5 章中的模式搜索程序找到对它的调用。
函数 `ferror` 若在流 `fp` 上发生错误,则返回非零值:
```c
int ferror(FILE *fp)
```
虽然输出错误很少见,但确实会发生(例如,磁盘已满),因此生产程序也应该检查这一点。
函数 `feof(FILE *)` 与 `ferror` 类似,若指定文件已到达文件末尾,则返回非零值:
```c
int feof(FILE *fp)
```
在小型示例程序中,我们通常不担心退出状态,但任何严肃的程序都应注意返回合理、有用的状态值。
### 1.2 行输入和输出
标准库提供了输入输出例程 `fgets`,它类似于我们在前面章节中使用的 `getline` 函数:
```c
char *fgets(char *line, int maxline, FILE *fp)
```
`fgets` 从文件 `fp` 读取下一个输入行(包括换行符)到字符数组 `line` 中,最多读取 `maxline - 1` 个字符。结果行以 `'\0'` 结尾。通常 `fgets` 返回 `line`,在文件末尾或出错时返回 `NULL`。(我们的 `getline` 返回行长度,这是一个更有用的值,零表示文件结束。)
对于输出,函数 `fputs` 将一个字符串(不一定包含换行符)写入文件:
```c
int fputs(char *line, FILE *fp)
```
若发生错误,它返回 `EOF`,否则返回非负值。
库函数 `gets` 和 `puts` 与 `fgets` 和 `fputs` 类似,但操作的是 `stdin` 和 `stdout`。令人困惑的是,`gets` 会删除终止的 `'\n'`,而 `puts` 会添加它。
下面是 `fgets` 和 `fputs` 的实现:
```c
/* fgets: get at most n chars from iop */
char *fgets(char *s, int n, FILE *iop)
{
register int c;
register char *cs;
cs = s;
while (--n > 0 && (c = getc(iop)) != EOF)
if ((*cs++ = c) == '\n')
break;
*cs = '\0';
return (c == EOF && cs == s) ? NULL : s;
}
/* fputs: put string s on file iop */
int fputs(char *s, FILE *iop)
{
int c;
while (c = *s++)
putc(c, iop);
return ferror(iop) ? EOF : 0;
}
```
标准为 `ferror` 和 `fputs` 指定了不同的返回值,原因并不明显。
使用 `fgets` 实现 `getline` 很容易:
```c
/* getline: read a line, return length */
int getline(char *line, int max)
{
if (fgets(line, max, stdin) == NULL)
return 0;
else
return strlen(line);
}
```
### 1.3 杂项函数
标准库提供了各种各样的函数,下面简要介绍一些最有用的函数。
#### 1.3.1 字符串操作
| 函数名 | 功能 |
| ---- | ---- |
| `strcat(s,t)` | 将 `t` 连接到 `s` 的末尾 |
| `strncat(s,t,n)` | 将 `t` 的 `n` 个字符连接到 `s` 的末尾 |
| `strcmp(s,t)` | 若 `s < t` 返回负值,`s == t` 返回零,`s > t` 返回正值 |
| `strncmp(s,t,n)` | 与 `strcmp` 类似,但只比较前 `n` 个字符 |
| `strcpy(s,t)` | 将 `t` 复制到 `s` |
| `strncpy(s,t,n)` | 最多将 `t` 的 `n` 个字符复制到 `s` |
| `strlen(s)` | 返回 `s` 的长度 |
| `strchr(s,c)` | 返回指向 `s` 中第一个 `c` 的指针,若不存在则返回 `NULL` |
| `strrchr(s,c)` | 返回指向 `s` 中最后一个 `c` 的指针,若不存在则返回 `NULL` |
#### 1.3.2 字符类测试和转换
| 函数名 | 功能 |
| ---- | ---- |
| `isalpha(c)` | 若 `c` 是字母,则返回非零值,否则返回 0 |
| `isupper(c)` | 若 `c` 是大写字母,则返回非零值,否则返回 0 |
| `islower(c)` | 若 `c` 是小写字母,则返回非零值,否则返回 0 |
| `isdigit(c)` | 若 `c` 是数字,则返回非零值,否则返回 0 |
| `isalnum(c)` | 若 `isalpha(c)` 或 `isdigit(c)` 为真,则返回非零值,否则返回 0 |
| `isspace(c)` | 若 `c` 是空格、制表符、换行符、回车符、换页符、垂直制表符,则返回非零值,否则返回 0 |
| `toupper(c)` | 将 `c` 转换为大写字母 |
| `tolower(c)` | 将 `c` 转换为小写字母 |
#### 1.3.3 Ungetc
标准库提供了一个功能受限的 `ungetch` 函数版本,称为 `ungetc`:
```c
int ungetc(int c, FILE *fp)
```
它将字符 `c` 推回到文件 `fp` 上,若成功则返回 `c`,出错则返回 `EOF`。每个文件保证只能回推一个字符。`ungetc` 可与任何输入函数(如 `scanf`、`getc` 或 `getchar`)一起使用。
#### 1.
0
0
复制全文
相关推荐









