原文: File Handling in C — How to Open, Close, and Write to Files
如果你以前写过 C 语言的 helloworld
程序,你已经知道 C 语言里的基本文件 I/O:
/* C 语言里的简单的 hello world */
#include <stdlib.h>
// 导入 IO 函数
#include <stdio.h>
int main() {
// This printf is where all the file IO magic happens!
// How exciting!
printf("Hello, world!\n");
return EXIT_SUCCESS;
}
文件处理是编程中最重要的部分之一。在 C 语言中,我们使用一个文件类型的结构指针来声明一个文件:
FILE *fp;
C 语言提供了一些内建函数来执行基本的文件操作:
fopen()
- 创建一个新文件或打开一个现有文件fclose()
- 关闭一个文件getc()
- 从文件中读取一个字符putc()
- 将一个字符写到一个文件中fscanf()
- 从一个文件中读取一组数据fprintf()
- 将一组数据写到一个文件中getw()
- 从文件中读取一个整数putw()
- 将一个整数写到一个文件中fseek()
- 将位置设置为期望的点ftell()
- 给出文件中的当前位置rewind()
- 将位置设置为起始点
打开一个文件
fopen()
函数用于创建一个文件或打开一个现有文件:
fp = fopen(const char filename,const char mode);
有许多模式可以打开一个文件:
r
- 以读模式打开文件w
- 以写模式打开或创建一个文本文件a
- 以附加模式打开一个文件r+
- 以读写模式打开一个文件a+
- 以读写模式打开一个文件w+
- 以读写模式打开一个文件
下面是一个从文件中读取数据和向文件中写入数据的例子:
#include<stdio.h>
#include<conio.h>
main()
{
FILE *fp;
char ch;
fp = fopen("hello.txt", "w");
printf("Enter data");
while( (ch = getchar()) != EOF) {
putc(ch,fp);
}
fclose(fp);
fp = fopen("hello.txt", "r");
while( (ch = getc(fp)! = EOF)
printf("%c",ch);
fclose(fp);
}
现在你可能在想,“这只是把文本打印到屏幕上。这个文件怎么会是 IO?”
答案起初并不明显,需要对 UNIX 系统有一些了解。在 UNIX 系统中,一切都被视为文件,这意味着你可以从文件中读取和写入文件。
这意味着你的打印机可以被抽象为一个文件,因为你对打印机所做的就是对它进行写入。把这些文件看作是流也是很有用的,因为你将在后面看到,你可以用 shell 重定向它们。
那么,这与 helloworld
和文件 IO 有什么关系?
当你调用 printf
时,你实际上只是在向一个叫作 stdout
的特殊文件写东西,这是 standard output(标准输出)的简称。stdout
代表由你的 shell 决定的标准输出,通常是终端。这就解释了为什么它会打印到你的屏幕上。
有两个流(即文件)可供你使用,即 stdin
和 stderr
。stdin
代表 standard output(标准输出),你的 shell 通常将其连接到键盘上。stderr
代表 standard error output(标准错误输出),你的 shell 通常将其连接到终端。
基本文件 IO,或我如何学习创建管道
理论够多了,让我们写点代码吧!写入文件的最简单方法是使用输出重定向工具 >
来重定向输出流。
如果你想追加,你可以使用 >>
:
# This will output to the screen...
./helloworld
# ...but this will write to a file!
./helloworld > hello.txt
毫不奇怪,hello.txt
的内容将是:
Hello, world!
假设我们有另一个叫作 greet
的程序,类似于 helloworld
,它有一个给定的 name
:
#include <stdio.h>
#include <stdlib.h>
int main() {
// 初始化一个数组,存有 name
char name[20];
// 读取一个字符串,并把它存到 name
scanf("%s", name);
// 打印 greeting
printf("Hello, %s!", name);
return EXIT_SUCCESS;
}
我们可以使用 <
工具将 stdin
重定向为从文件中读取,而不是从键盘上读取:
# 写一个包含 name 的文件
echo Kamala > name.txt
# 这将从文件读取 name,并把 greeting 打印到屏幕上
./greet < name.txt
# ==> Hello, Kamala!
# 如果你想也把 greeting 写入一个文件,你可以使用 “>”
注意:这些重定向操作符是在 bash
和类似的 shell 中。
重要内容
上面的方法只适用于最基本的情况。如果你想做更大、更好的事情,你可能想从 C 语言中而不是通过 shell 来处理文件。
为了达到这个目的,你将使用一个叫作 fopen
的函数。这个函数需要两个字符串参数,第一个是文件名,第二个是模式。
模式基本上是权限,所以 r
代表读,w
代表写,a
代表追加。你也可以把它们结合起来,所以 rw
意味着你可以读和写该文件。还有更多的模式,但这些是最常用的。
在你有了 FILE
指针之后,你可以使用基本上与你原来使用的相同的 IO 命令,只是你必须在它们前面加上 f
,并且第一个参数将是文件指针。例如,printf
的文件版本是 fprintf
。
下面是一个名为 greetings
的程序,它从一个包含名字列表的文件中读取问候语,并将问候语写到另一个文件中:
#include <stdio.h>
#include <stdlib.h>
int main() {
// 创建文件指针
FILE *names = fopen("names.txt", "r");
FILE *greet = fopen("greet.txt", "w");
// 检查是否一切没问题
if (!names || !greet) {
fprintf(stderr, "File opening failed!\n");
return EXIT_FAILURE;
}
// Greetings time!
char name[20];
// 持续读取所有内容
while (fscanf(names, "%s\n", name) > 0) {
fprintf(greet, "Hello, %s!\n", name);
}
// 到达末尾时,打印一条信息到终端以通知用户
if (feof(names)) {
printf("Greetings are done!\n");
}
return EXIT_SUCCESS;
}
names.txt
包含:
Kamala
Logan
Carol
然后,在运行 greetings
之后,文件 greet.txt
将包含:
Hello, Kamala!
Hello, Logan!
Hello, Carol!