123

用 C 语言打开文件并将其内容读入字符串(char*、char[] 等)最简单的方法是什么(最不容易出错,最少的代码行,但是你想解释它)?

4

12 回答 12

167

我倾向于将整个缓冲区作为原始内存块加载到内存中并自己进行解析。这样我就可以最好地控制标准库在多个平台上的作用。

这是我用于此的存根。您可能还想检查 fseek、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...
}
于 2008-10-06T14:37:00.520 回答
34

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

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()and提供的MapViewOfFile()

于 2008-10-06T15:37:28.153 回答
17

如果“将其内容读入字符串”意味着文件不包含代码为 0 的字符,您还可以使用 getdelim() 函数,该函数要么接受一块内存并在必要时重新分配它,要么只为整个缓冲区分配您,并将文件读入其中,直到遇到指定的分隔符或文件结尾。只需传递 '\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 */
于 2008-10-06T15:24:23.233 回答
8

如果您正在读取标准输入或管道等特殊文件,您将无法使用 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;
}
于 2013-11-24T19:40:34.630 回答
5

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

char buffer[100];
FILE *fp = fopen("filename", "r");                 // do not use "rb"
while (fgets(buffer, sizeof(buffer), fp)) {
... do something
}
fclose(fp);
于 2008-10-08T21:02:07.590 回答
5

注意:这是对上面接受的答案的修改。

这是一种方法,包括错误检查。

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

#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);
}
于 2019-01-06T00:48:52.780 回答
3

如果您正在使用glib,那么您可以使用g_file_get_contents

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);
  }
}
于 2016-10-07T10:24:20.787 回答
2

刚刚从上面接受的答案修改。

#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);
}
于 2017-11-09T07:10:35.283 回答
1
// 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 进行检查。

于 2016-05-15T17:49:49.693 回答
0

我将根据这里的答案添加我自己的版本,仅供参考。我的代码考虑了 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);
于 2019-07-07T16:59:00.283 回答
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;
  ...
} 
于 2021-12-19T06:48:24.927 回答
-3

简单而整洁(假设文件中的内容少于 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;
}
于 2019-08-15T14:40:45.610 回答