如何读取一个文件的内容到一个字符串在 C?

用 C 打开一个文件并将其内容读入一个字符串(char * 、 char []等等)的最简单方法是什么?

199895 次浏览

我倾向于将整个缓冲区作为原始内存块加载到内存中,然后自己解析。通过这种方式,我可以最好地控制标准库在多个平台上的作用。

这是我用来做这个的存根。您可能还需要检查 ffind、 ftell 和 fread 的错误代码。(为了清楚起见略去)。

char * buffer = 0;
long length;
FILE * f = fopen (filename, "rb");


if (f)
{
fseek (f, 0, SEEK_END);
length = ftell (f);
fseek (f, 0, SEEK_SET);
buffer = malloc (length);
if (buffer)
{
fread (buffer, 1, length, f);
}
fclose (f);
}


if (buffer)
{
// start to process your data / extract strings here...
}

如果“将其内容读入字符串”意味着文件不包含代码为0的字符,那么您也可以使用 getdelm ()函数,该函数接受一块内存并在必要时重新分配它,或者只为您分配整个缓冲区,并将文件读入其中,直到遇到指定的分隔符或文件结束。只需传递’0’作为分隔符来读取整个文件。

这个函数可以在 GNU C 库 http://www.gnu.org/software/libc/manual/html_mono/libc.html#index-getdelim-994中找到

示例代码看起来可能像下面这样简单

char* buffer = NULL;
size_t len;
ssize_t bytes_read = getdelim( &buffer, &len, '\0', fp);
if ( bytes_read != -1) {
/* Success, now the entire file is in the buffer */

不幸的是,另一个高度依赖于操作系统的解决方案是内存映射文件。其好处通常包括读取的性能,以及减少内存使用,因为应用程序视图和操作系统文件缓存实际上可以共享物理内存。

POSIX 代码如下:

int fd = open("filename", O_RDONLY);
int len = lseek(fd, 0, SEEK_END);
void *data = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);

另一方面,Windows 有点棘手,不幸的是我没有一个编译器在我面前测试,但功能是由 CreateFileMapping()MapViewOfFile()提供的。

如果文件是文本,并且希望逐行获取文本,那么最简单的方法是使用 fgets ()。

char buffer[100];
FILE *fp = fopen("filename", "r");                 // do not use "rb"
while (fgets(buffer, sizeof(buffer), fp)) {
... do something
}
fclose(fp);

如果您正在读取像 stdin 或管道这样的特殊文件,则无法事先使用 fstat 获取文件大小。此外,如果您正在读取一个二进制文件,fgets 将会因为嵌入’0’字符而丢失字符串大小信息。读取文件的最佳方法是使用 read 和 realloc:

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>


int main () {
char buf[4096];
ssize_t n;
char *str = NULL;
size_t len = 0;
while (n = read(STDIN_FILENO, buf, sizeof buf)) {
if (n < 0) {
if (errno == EAGAIN)
continue;
perror("read");
break;
}
str = realloc(str, len + n + 1);
memcpy(str + len, buf, n);
len += n;
str[len] = '\0';
}
printf("%.*s\n", len, str);
return 0;
}
// Assumes the file exists and will seg. fault otherwise.
const GLchar *load_shader_source(char *filename) {
FILE *file = fopen(filename, "r");             // open
fseek(file, 0L, SEEK_END);                     // find the end
size_t size = ftell(file);                     // get the size in bytes
GLchar *shaderSource = calloc(1, size);        // allocate enough bytes
rewind(file);                                  // go back to file beginning
fread(shaderSource, size, sizeof(char), file); // read each char into ourblock
fclose(file);                                  // close the stream
return shaderSource;
}

这是一个相当粗糙的解决方案,因为没有对 null 进行任何检查。

如果您使用的是 glib,那么您可以使用 G _ file _ get _ content;

gchar *contents;
GError *err = NULL;


g_file_get_contents ("foo.txt", &contents, NULL, &err);
g_assert ((contents == NULL && err != NULL) || (contents != NULL && err == NULL));
if (err != NULL)
{
// Report error to user, and free error
g_assert (contents == NULL);
fprintf (stderr, "Unable to read file: %s\n", err->message);
g_error_free (err);
}
else
{
// Use file contents
g_assert (contents != NULL);
}
}

只是根据上述已接受的答案进行了修改。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>


char *readFile(char *filename) {
FILE *f = fopen(filename, "rt");
assert(f);
fseek(f, 0, SEEK_END);
long length = ftell(f);
fseek(f, 0, SEEK_SET);
char *buffer = (char *) malloc(length + 1);
buffer[length] = '\0';
fread(buffer, 1, length, f);
fclose(f);
return buffer;
}


int main() {
char *content = readFile("../hello.txt");
printf("%s", content);
}

注意: 这是对上述公认答案的修改。

这里有一个方法,通过错误检查来完成。

我已经添加了一个大小检查器退出时,文件大于1GiB。我这样做是因为程序把整个文件放入一个字符串,这可能使用太多的内存和崩溃的计算机。但是,如果您不关心这一点,可以直接从代码中删除它。

#include <stdio.h>
#include <stdlib.h>


#define FILE_OK 0
#define FILE_NOT_EXIST 1
#define FILE_TOO_LARGE 2
#define FILE_READ_ERROR 3


char * c_read_file(const char * f_name, int * err, size_t * f_size) {
char * buffer;
size_t length;
FILE * f = fopen(f_name, "rb");
size_t read_length;
    

if (f) {
fseek(f, 0, SEEK_END);
length = ftell(f);
fseek(f, 0, SEEK_SET);
        

// 1 GiB; best not to load a whole large file in one string
if (length > 1073741824) {
*err = FILE_TOO_LARGE;
            

return NULL;
}
        

buffer = (char *)malloc(length + 1);
        

if (length) {
read_length = fread(buffer, 1, length, f);
            

if (length != read_length) {
free(buffer);
*err = FILE_READ_ERROR;


return NULL;
}
}
        

fclose(f);
        

*err = FILE_OK;
buffer[length] = '\0';
*f_size = length;
}
else {
*err = FILE_NOT_EXIST;
        

return NULL;
}
    

return buffer;
}

为了检查错误:

int err;
size_t f_size;
char * f_data;


f_data = c_read_file("test.txt", &err, &f_size);


if (err) {
// process error
}
else {
// process data
free(f_data);
}

我将添加我自己的版本,基于这里的答案,只是作为参考。我的代码考虑到 sizeof (char)并添加了一些注释。

// Open the file in read mode.
FILE *file = fopen(file_name, "r");
// Check if there was an error.
if (file == NULL) {
fprintf(stderr, "Error: Can't open file '%s'.", file_name);
exit(EXIT_FAILURE);
}
// Get the file length
fseek(file, 0, SEEK_END);
long length = ftell(file);
fseek(file, 0, SEEK_SET);
// Create the string for the file contents.
char *buffer = malloc(sizeof(char) * (length + 1));
buffer[length] = '\0';
// Set the contents of the string.
fread(buffer, sizeof(char), length, file);
// Close the file.
fclose(file);
// Do something with the data.
// ...
// Free the allocated string space.
free(buffer);

简单和整洁(假设文件中的内容小于10000) :

void read_whole_file(char fileName[1000], char buffer[10000])
{
FILE * file = fopen(fileName, "r");
if(file == NULL)
{
puts("File not found");
exit(1);
}
char  c;
int idx=0;
while (fscanf(file , "%c" ,&c) == 1)
{
buffer[idx] = c;
idx++;
}
buffer[idx] = 0;
}

用 C 打开一个文件并将其内容读入一个字符串中,最简单的方法是什么(最不容易出错,代码行最少,不管您希望如何解释它) ?

遗憾的是,即使经过多年,答案也很容易出错,许多答案缺乏正确的 绳子构成和错误检查。

#include <stdio.h>
#include <stdlib.h>


// Read the file into allocated memory.
// Return NULL on error.
char* readfile(FILE *f) {
// f invalid? fseek() fail?
if (f == NULL || fseek(f, 0, SEEK_END)) {
return NULL;
}


long length = ftell(f);
rewind(f);
// Did ftell() fail?  Is the length too long?
if (length == -1 || (unsigned long) length >= SIZE_MAX) {
return NULL;
}


// Convert from long to size_t
size_t ulength = (size_t) length;
char *buffer = malloc(ulength + 1);
// Allocation failed? Read incomplete?
if (buffer == NULL || fread(buffer, 1, ulength, f) != ulength) {
free(buffer);
return NULL;
}
buffer[ulength] = '\0'; // Now buffer points to a string


return buffer;
}

注意,如果文本文件包含 空字符,那么分配的数据将包含所有文件数据,但是字符串看起来是短的。更好的代码还会返回长度信息,这样调用方就可以处理这些信息。

char* readfile(FILE *f, size_t *ulength_ptr) {
...
if (ulength_ptr) *ulength_ptr == *ulength;
...
}