2008-10-06 10 views
51

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

+2

"modo más simple" y "menos propenso a errores" son a menudo opuestos entre sí. –

+7

"forma más simple" y "menos propenso a errores" también son sinónimos en mi libro. Por ejemplo, la respuesta en C# es 'string s = File.ReadAllText (filename);'.¿Cómo podría ser eso más simple y más propenso a errores? –

Respuesta

75

Tiendo a simplemente cargar todo el búfer como un trozo de memoria en bruto en la memoria y hacer el análisis por mi cuenta. De esta forma, tengo el mejor control sobre lo que hace la lib estándar en múltiples plataformas.

Este es un stub que utilizo para esto. es posible que también desee verificar 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... 
} 
+0

Impresionante, que funcionó como un amuleto (y es bastante simple de seguir). ¡Gracias! –

+2

También verificaría el valor de retorno de fread, ya que podría no leer todo el archivo debido a errores y otras cosas. – freespace

+1

En la línea de lo que Freespace dijo, es posible que desee comprobar para asegurarse de que el archivo no es enorme. Supongamos, por ejemplo, que alguien decide alimentar un archivo de 6GB en ese programa ... – rmeador

7

Si "leer su contenido en una cadena" significa que el archivo no contiene caracteres con código 0, también se puede utilizar getdelim() función, que, o bien acepta un bloque de memoria y reasigna si es necesario, o simplemente asigna todo el buffer para usted, y lee el archivo en él hasta que encuentra un delimitador o un final de archivo especificados. Simplemente pase '\ 0' como el delimitador para leer todo el archivo.

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

El código de ejemplo podría ser 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 */ 
+1

¡Lo he usado antes! Funciona muy bien, suponiendo que el archivo que estás leyendo es texto (no contiene \ 0). – ephemient

+0

NICE! Guarda muchos problemas al sorber en archivos de texto completo. ¡Ahora si hubiera una manera ultra simple similar de leer una secuencia de archivos binarios hasta EOF sin necesitar ningún carácter de delimitación! – anthony

17

Otro, por desgracia altamente dependiente del sistema operativo, la solución es la cartografía de la memoria en el archivo. Los beneficios generalmente incluyen el rendimiento de la lectura y el uso reducido de la memoria, ya que la vista de aplicaciones y la memoria caché de los sistemas operativos pueden compartir la memoria física.

código de 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 en el otro lado es poco más difícil, y por desgracia no tengo un compilador frente a mí para probar, pero la funcionalidad es proporcionada por CreateFileMapping() y MapViewOfFile().

+0

¡No olvide verificar los valores devueltos de esas llamadas al sistema! –

6

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); 
5

Si está leyendo archivos especiales como entrada estándar o una tubería, que no van a ser capaces de utilizar fstat para obtener el tamaño del archivo de antemano. Además, si estás leyendo un archivo binario, fgets va a perder la información del tamaño de cadena debido a los caracteres incrustados '\ 0'. Mejor manera de leer un archivo, entonces, para su utilización Lea 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; 
} 
+0

Esto es O (n^2), donde n es la longitud de su archivo. Todas las soluciones con más votos ascendentes que esto son O (n). No use esta solución en la práctica, o use una versión modificada con crecimiento multiplicativo. –

+1

realloc() puede ampliar la memoria existente al nuevo tamaño sin copiar la memoria anterior en una nueva pieza de memoria más grande. solo si hay llamadas intermedias a malloc() necesitará mover la memoria y hacer que esta solución sea O (n^2). aquí, no hay llamadas a malloc() que ocurren entre las llamadas a realloc(), por lo que la solución debería estar bien. – Jake

+2

Puede leer directamente en el búfer "str" ​​(con un desplazamiento apropiado), sin necesidad de copiar desde un "buf" intermedio. Sin embargo, esa técnica generalmente sobrecargará la memoria necesaria para el contenido del archivo. También tenga cuidado con los archivos binarios, el printf no los manejará correctamente, ¡y probablemente no quiera imprimir binarios de todos modos! – anthony

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; 
} 

Esta es una solución bastante crudo porque nada se compara con nula.

+0

Esto solo se aplicará con los archivos basados ​​en disco. Fallará para tuberías con nombre, entrada estándar o flujos de red. – anthony

+0

¡Ha, también por qué vine aquí! Pero creo que es necesario anular el final de la cadena, o devolver la longitud que 'glShaderSource' toma opcionalmente. –

1

Si está utilizando glib, puede 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); 
    } 
} 
0

Apenas modificado de la respuesta aceptada anteriormente.

#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

Este no es un código C. La pregunta no está etiquetada como C++. – Gerhardh

+0

@Gerhardh ¡Respuesta tan rápida a la pregunta hace nueve años cuando estoy editando! Aunque la parte de la función es pura C, lamento mi respuesta will-not-run-on-c. – BaiJiFeiLong

+0

Esta antigua pregunta figuraba en la parte superior de las preguntas activas. No lo busqué. – Gerhardh

Cuestiones relacionadas