Cómo leer el contenido de un archivo en una cadena en C?


¿Cuál es la forma más sencilla (menos propensa a errores, menos líneas de código, como quiera interpretarlo) de abrir un archivo en C y leer su contenido en una cadena (char*, char [], lo que sea)?

9 answers

Tiendo a cargar todo el búfer como un fragmento de memoria raw en la memoria y hacer el análisis por mi cuenta. De esa manera tengo el mejor control sobre lo que hace la lib estándar en múltiples plataformas.

Este es un trozo que uso para esto. también es posible que desee comprobar los códigos de error para fseek, ftell y fread. (omitido para mayor claridad).

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...
}
 91
Author: Nils Pipenbrinck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-06 14:37:00

Otra solución, desafortunadamente altamente dependiente del sistema operativo, es el mapeo de memoria del archivo. Los beneficios generalmente incluyen el rendimiento de la lectura y la reducción del uso de memoria, ya que la vista de aplicaciones y la caché de archivos de los sistemas operativos pueden compartir la memoria física.

El código POSIX se vería así:

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, por otro lado, es un poco más complicado, y desafortunadamente no tengo un compilador frente a mí para probar, pero la funcionalidad es proporcionada por CreateFileMapping() y MapViewOfFile().

 20
Author: Jeff Mc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-05 12:28:33

Si "leer su contenido en una cadena" significa que el archivo no contiene caracteres con código 0, también puede usar la función getdelim (), que acepta un bloque de memoria y lo reasigna si es necesario, o simplemente asigna todo el búfer por usted, y lee el archivo en él hasta que encuentre un delimitador especificado o el final del archivo. Simplemente pase '\0 ' como delimitador para leer el archivo completo.

Esta función está disponible en la Biblioteca C de GNU, http://www.gnu.org/software/libc/manual/html_mono/libc.html#index-getdelim-994

El código de ejemplo podría parecer tan simple como

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 */
 7
Author: dmityugov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-25 19:27:26

Si el archivo es texto, y desea obtener el texto línea por línea, la forma más fácil es usar fgets().

char buffer[100];
FILE *fp = fopen("filename", "r");                 // do not use "rb"
while (fgets(buffer, sizeof(buffer), fp)) {
... do something
}
fclose(fp);
 6
Author: selwyn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-08 21:02:07

Si está leyendo archivos especiales como stdin o una tubería, no podrá usar fstat para obtener el tamaño del archivo de antemano. Además, si está leyendo un archivo binario, fgets perderá la información del tamaño de la cadena debido a los caracteres '\0' incrustados. La mejor manera de leer un archivo entonces es usar read y 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;
}
 5
Author: Jake,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-25 03:16:33

Si estás usando glib, entonces puedes usar 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);
  }
}
 1
Author: sleepycal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-07 10:24:20
// 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;
}

Esta es una solución bastante cruda porque nada se compara con null.

 0
Author: Entalpi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-15 17:49:49

Solo modificado de la respuesta aceptada arriba.

#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);
}
 0
Author: BaiJiFeiLong,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-09 07:20:12

Si desea una solución basada en pila:

  FILE* f = fopen(file, "r");
  if (!f) {
    fprintf(stderr, "fopen failed\n");
  }

  if (fseek(f, 0, SEEK_END)) {
    fprintf(stderr, "fseek failed\n");
  }

  const long len = ftell(f);
  if (len < 0) {
    fprintf(stderr, "ftell failed\n");
  }

  if (fseek(f, 0, SEEK_SET)) {
    fprintf(stderr, "fseek failed\n");
  }

  char buf[len + 1];
  if (fread(buf, sizeof(char), len, f) != len) {
    fprintf(stderr, "fread failed\n");
  }

  buf[len] = '\0';

  if (fclose(f)) {
    fprintf(stderr, "fclose failed\n");
  }
 0
Author: ericcurtin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-14 12:11:28