当您使用 C 编程语言时,您可能会遇到一些需要将文件读入字符数组的问题,例如分析每个字符的频率,或者将所有句子的每个起始词从小写转换为大写,反之亦然。该解决方案非常简单,但对于不太了解文件读取或写入的人来说可能并不那么简单。因此,在这篇文章中,您可以逐步学习如何在 C 中将文件读入字符数组。
在 C 中打开文件
在 C 语言中打开文件的最简单和最流行的方法是使用以下格式的"fopen"函数:
file = fopen(file_name, "open_mode"); //open the "file-name" file and store the file pointer in the variable 'file'
"fopen"函数的模式参数指定打开文件的模式。该模式可以是以下之一:
- " r ":打开文件进行读取。
- " w ":将文件截断为零长度或创建一个用于写入的文件。
- " a ":追加到文件或创建一个文件(如果不存在)进行写入。
- " r+ ":打开文件进行读写。
- " w+ ":将文件截断为零长度或创建一个用于读写的文件。
- " a+ ":追加到文件或创建文件以供读写。
但由于某种原因,该文件可能无法正常打开。为了在发生此类情况时做好准备,您应该始终检查"fopen"函数的返回值,以确保在尝试读取或写入文件之前已成功打开文件。像这样:
// If 'fopen' returns NULL, print an error message and exit the program
if (file == NULL) {
printf("Error: Failed to open file '%s'.\n", file_name);
return 1;
}
逐字符读取文件内容
在读取文件之前,您必须有一个字符数组来存储文件内容。让我们这样做吧
char buffer[1000]; //Initialize a char array named 'buffer' with size of 1000
现在,是时候使用"fgetc"读取文件了。该函数每次调用都会读取文件中的一个字符,如果重复调用,它将读取后续的每个字符,直到结束。因此,我们可以使用 while 循环来使过程变得更容易。
int i = 0, c; //c is the intermediate variable, i is the increment variable
while ((c = fgetc(file)) != EOF) {//Read contents until it reach the end of the file
buffer[i] = c;
i++;
}
上面的示例假设文件仅包含 ASCII 字符,并且文件大小小于 1000 个字符。
可调整大小的缓冲区
我们之前定义的缓冲区数组最多包含 1000 个字符。但在许多情况下,文件大小远大于此。我们可以通过将缓冲区变成可调整大小的缓冲区来解决这个问题。您可以通过 C 标准库提供的"malloc"、"realloc"和"realloc"函数使用动态内存分配。
char *buffer = NULL; // initialize buffer to NULL
int buffer_size = 0;
/*Open the file here*/
// Read file character by character
int c, i = 0;
while ((c = fgetc(file)) != EOF) {
// If buffer is full, resize it
if (i >= buffer_size) {
buffer_size += 1000; // increase buffer size by 1000 bytes
buffer = realloc(buffer, buffer_size); // resize buffer
if (buffer == NULL) {
printf("Error: Memory allocation failed.\n");
return 1;
}
}
buffer[i] = c;
i++;
}
我们在上面的代码片段中使用"realloc"函数,这被证明是有用的,因为文件大小通常是事先未知的。对于"malloc"和"calloc"函数,它们可用于将指定大小的内存块分配给变量。在此示例中,您可以像下面这样使用:
buffer = (char*)malloc(1000); //is the same as define char buffer[1000]
在此示例中,您可能不需要使用"malloc"和"calloc"。我们稍后会再次见到他们。
文件包含非 ASCII 字符
在 C 中,字符串表示为字节序列,这些字节的解释取决于字符编码。如果文件包含非 ASCII 字符,则需要使用支持这些字符的字符编码,例如 UTF-8 或 UTF-16。
对于这个问题,您应该使用可以处理多字节字符的函数,例如"fgetwc"和"fgetws"。这些函数一次分别读取一个宽字符 (wchar_t) 或一个宽字符串 (wchar_t*)。
以下是对代码的一些修改,以使其在文件包含非 ASCII 字符时正常工作:
wchar_t buffer[100];
// Open file for reading
file = fopen(filename, "r,ccs=UTF-8");
// Read file contents
wchar_t c;
int i = 0;
while ((c = fgetwc(file)) != WEOF) {
buffer[i] = c;
i++;
}
另外,请确保输入和输出流设置为正确的编码,以正确显示或操作字符。在 MacOS 和 Linux 等 Unix 操作系统上,为了确保输出编码为 UTF-8,您可以使用 'setlocale' 函数:
#include
int main()
{
setlocale(LC_ALL, "en_US.utf8");
// your code here
return 0;
}
在 Windows 上,您可以使用 '_setmode' 和 '_O_U8TEXT' 函数将输出编码设置为 UTF-8:
#include //_O_U8TEXT
#include //_setmode()
int main()
{
_setmode(_fileno(stdout), _O_U8TEXT);
// your code here
return 0;
}
以下是包含越南语单词"Xin chào!"的文件示例 (Hello) 带重音符号(非 ASCII 字符),以 UTF-8 编码保存:
Xin chào!
这是我们的程序在在线 C 编译器上运行后的输出:
Xin chào!
...Program finished with exit code 0
Press ENTER to exit console.
读取整个文件内容
如果您不熟悉 C,那么您可以跳过此步骤,但我仍然建议将其作为高级练习来阅读。我想介绍另一种方法来解决"如何在 C 中将文件读入字符数组"问题。新的思路是不再逐个字符地读取文件,而是整体读取文件,在读取之前确定文件大小。这是一个更复杂的解决方案,但也更有效。
首先,您应该定义常用变量:用于打开文件的文件指针和用于包含字符数组的缓冲区。请记住,您还需要文件大小:
FILE *fp;
long file_size;
char *buffer;
然后就可以打开文件来读取:
fp = fopen("example.txt", "r");
要了解文件的大小,可以使用"ftell"函数。它将告诉文件指针中当前位置的字节位置:
current_byte = ftell(fp);
但是等等,文件读取总是从文件的开头开始。没问题,"fseek"函数会将读取控件移动到文件中的不同位置:
fseek(fp, 0, SEEK_END);
您现在可以正确获取文件大小。之后,我们再次将读取控件设置为开头,开始读取文件内容:
file_size = ftell(fp);
rewind(fp); move the control to the file's beginning
// Allocate memory for the char array
buffer = (char*) malloc(file_size + 1);
这里"malloc"函数的使用非常简单:分配内存来创建一个未初始化的 char 数组,其大小为 (file_size+1) 乘以 1 字节(char 类型的大小)。
如果您想使用"calloc"函数,请按以下步骤操作:
buffer = (char*) calloc(file_size + 1, sizeof(char));
"malloc"和"calloc"之间的主要区别在于,"malloc"仅分配内存而不初始化其内容,而"calloc"既分配内存又将内存初始化为零。使用"calloc"的主要优点是分配的内存已经被清零,如果您打算稍后将 char 数组用作字符串,这会很有帮助。
// Read the file into the char array
fread(buffer, file_size, 1, fp);
创建缓冲区后,您可以使用"fread"函数读取整个文件,该函数获取文件指针、要读取的每个元素的大小、要读取的元素的数量以及目标数组。
// Add a null terminator at the end of the char array
buffer[file_size] = '\0';
您可能想知道为什么需要为"缓冲区"分配额外的字节。为什么不只是(file_size)而是(file_size + 1)?在这里,将在 char 数组的末尾添加 null 终止符,以指示字符串的结束。实际上,如果您唯一的任务是将文件读入数组,那么这一步是不必要的。但稍后如果您想将此数组打印为字符串,那么这是一个要求。C 中的字符串被定义为将最后一个字符作为空终止符"\0"。
清理你的代码
您已经打开并使用了该文件,因此请记住随后将其关闭。只需使用"fclose"函数来释放您分配的"文件"指针变量。
fclose(file);
谈到释放指针,还记得用来存储字符的"缓冲区"数组吗?如果您将其定义为已分配的内存(指针),那么最好立即释放它以避免内存泄漏。
free(buffer);
以下是您的解决方案的概述:
#include
#include
int main() {
FILE *file;
char filename[] = "example.txt";
char *buffer = NULL; // initialize buffer to NULL
int buffer_size = 0;
int i = 0;
//Open file for reading
file = fopen(filename, "r");
//Check if file opened successfully
if (file == NULL) {
printf("Error: Failed to open file '%s'.\n", filename);
return 1;
}
// Read file character by character
int c;
while ((c = fgetc(file)) != EOF) {
// If buffer is full, resize it
if (i >= buffer_size) {
buffer_size += 1000; // increase buffer size by 1000 bytes
buffer = realloc(buffer, buffer_size); // resize buffer
if (buffer == NULL) {
printf("Error: Memory allocation failed.\n");
return 1;
}
}
buffer[i] = c;
i++;
}
// Close file
fclose(file);
// Print the character array
printf("%s", buffer);
// Free the dynamically allocated buffer
free(buffer);
return 0;
}