En C, ¿cómo debo leer un archivo de texto e imprimir todas las cadenas?

94

Tengo un archivo de texto llamado test.txt

Quiero escribir un programa en C que pueda leer este archivo e imprimir el contenido en la consola (suponga que el archivo contiene solo texto ASCII).

No sé cómo obtener el tamaño de mi variable de cadena. Me gusta esto:

char str[999];
FILE * file;
file = fopen( "test.txt" , "r");
if (file) {
    while (fscanf(file, "%s", str)!=EOF)
        printf("%s",str);
    fclose(file);
}

El tamaño 999no funciona porque la cadena devuelta por fscanfpuede ser más grande que eso. ¿Como puedo resolver esto?

Ricardo
fuente

Respuestas:

134

La forma más sencilla es leer un carácter e imprimirlo inmediatamente después de leerlo:

int c;
FILE *file;
file = fopen("test.txt", "r");
if (file) {
    while ((c = getc(file)) != EOF)
        putchar(c);
    fclose(file);
}

cestá por intencima, ya que EOFes un número negativo y un simple charpuede ser unsigned.

Si desea leer el archivo en trozos, pero sin asignación de memoria dinámica, puede hacer lo siguiente:

#define CHUNK 1024 /* read 1024 bytes at a time */
char buf[CHUNK];
FILE *file;
size_t nread;

file = fopen("test.txt", "r");
if (file) {
    while ((nread = fread(buf, 1, sizeof buf, file)) > 0)
        fwrite(buf, 1, nread, stdout);
    if (ferror(file)) {
        /* deal with error */
    }
    fclose(file);
}

El segundo método anterior es esencialmente cómo leerá un archivo con una matriz asignada dinámicamente:

char *buf = malloc(chunk);

if (buf == NULL) {
    /* deal with malloc() failure */
}

/* otherwise do this.  Note 'chunk' instead of 'sizeof buf' */
while ((nread = fread(buf, 1, chunk, file)) > 0) {
    /* as above */
}

Su método de formato fscanf()con %sas pierde información sobre los espacios en blanco en el archivo, por lo que no es exactamente copiar un archivo a stdout.

Alok Singhal
fuente
¿Es posible leer datos de un archivo sin abrir ese archivo en c / c ++?
Sagar Patel
¿Qué pasa si el archivo de texto contiene valores enteros separados por comas? de lo que sería el código, puede editar su respuesta con eso también.
Mohsin
Lo anterior funciona para cualquier tipo de archivo de texto. Si desea analizar los números de un archivo CSV, ese es un problema diferente.
Alok Singhal
1
@overexchange La pregunta no habla de líneas, se trata de leer un archivo y copiar su contenido en stdout.
Alok Singhal
1
@shjeff Un archivo no puede contener caracteres EOF. Tenga en cuenta que ces int, y C garantizará que EOFno es igual a ningún carácter válido.
Alok Singhal
60

Aquí hay muchas buenas respuestas sobre cómo leerlo en partes, solo te mostraré un pequeño truco que lee todo el contenido a la vez en un búfer y lo imprime.

No digo que sea mejor. No lo es, y como Ricardo, a veces puede ser malo, pero encuentro que es una buena solución para los casos simples.

Lo espolvoreé con comentarios porque están pasando muchas cosas.

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

char* ReadFile(char *filename)
{
   char *buffer = NULL;
   int string_size, read_size;
   FILE *handler = fopen(filename, "r");

   if (handler)
   {
       // Seek the last byte of the file
       fseek(handler, 0, SEEK_END);
       // Offset from the first to the last byte, or in other words, filesize
       string_size = ftell(handler);
       // go back to the start of the file
       rewind(handler);

       // Allocate a string that can hold it all
       buffer = (char*) malloc(sizeof(char) * (string_size + 1) );

       // Read it all in one operation
       read_size = fread(buffer, sizeof(char), string_size, handler);

       // fread doesn't set it so put a \0 in the last position
       // and buffer is now officially a string
       buffer[string_size] = '\0';

       if (string_size != read_size)
       {
           // Something went wrong, throw away the memory and set
           // the buffer to NULL
           free(buffer);
           buffer = NULL;
       }

       // Always remember to close the file.
       fclose(handler);
    }

    return buffer;
}

int main()
{
    char *string = ReadFile("yourfile.txt");
    if (string)
    {
        puts(string);
        free(string);
    }

    return 0;
}

Déjame saber si es útil o podrías aprender algo de él :)

lfzawacki
fuente
2
¿No debería leerse en buffer[string_size] = '\0';lugar de string_size+1? Afaik, la cadena real va de 0a string_size-1y, por lo \0tanto, el personaje debe estar en string_size, ¿verdad?
aepsil0n
4
Usar ftelly fseekpara encontrar el tamaño de un archivo no es seguro: securecoding.cert.org/confluence/display/seccode/…
Joakim
1
Este código contiene una pérdida de memoria, nunca cierra el archivo. Hay un desaparecidofclose(handle)
Joakim
1
Hay un error tipográfico en el que llamas a fclose (handle), debería ser fclose (handler)
Eduardo Cobuci
3
Podría usar en calloc(2)lugar de malloc(1)omitir tener que configurar el terminador nulo.
14

En su lugar, imprima directamente los caracteres en la consola porque el archivo de texto puede ser muy grande y es posible que necesite mucha memoria.

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

int main() {

    FILE *f;
    char c;
    f=fopen("test.txt","rt");

    while((c=fgetc(f))!=EOF){
        printf("%c",c);
    }

    fclose(f);
    return 0;
}
Sagar Shah
fuente
6

Utilice "read ()" en lugar de fscanf:

ssize_t read(int fildes, void *buf, size_t nbyte);

DESCRIPCIÓN

La función read () intentará leer nbytebytes del archivo asociado con el descriptor de archivo abierto fildes, en el búfer al que apunta buf.

Aquí hay un ejemplo:

http://cmagical.blogspot.com/2010/01/c-programming-on-unix-implementing-cat.html

Parte de trabajo de ese ejemplo:

f=open(argv[1],O_RDONLY);
while ((n=read(f,l,80)) > 0)
    write(1,l,n);

Un enfoque alternativo es usar getc/ putcpara leer / escribir 1 carácter a la vez. Mucho menos eficiente. Un buen ejemplo: http://www.eskimo.com/~scs/cclass/notes/sx13.html

DVK
fuente
readle permitirá leer una cierta cantidad de caracteres. Lea lo suficiente para llenar su búfer, luego descargue su búfer en la pantalla, bórrelo y repita hasta que llegue al final del archivo.
bta
1

Me vienen a la mente dos enfoques.

Primero, no lo use scanf. Utilice el fgets()que toma un parámetro para especificar el tamaño del búfer y que deja intactos los caracteres de nueva línea. Un simple bucle sobre el archivo que imprime el contenido del búfer naturalmente debería copiar el archivo intacto.

En segundo lugar, use fread()o el modismo común de C con fgetc(). Estos procesarían el archivo en trozos de tamaño fijo o un solo carácter a la vez.

Si debe procesar el archivo sobre cadenas delimitadas por espacios en blanco, utilice fgetso freadpara leer el archivo y algo comostrtok dividir el búfer en espacios en blanco. No olvide manejar la transición de un búfer al siguiente, ya que es probable que sus cadenas de destino abarquen el límite del búfer.

Si hay un requisito externo para usar scanfpara realizar la lectura, limite la longitud de la cadena que podría leer con un campo de precisión en el especificador de formato. En su caso con un búfer de 999 bytes, entonces diga scanf("%998s", str);cuál escribirá como máximo 998 caracteres en el búfer dejando espacio para el terminador nulo. Si se permiten cadenas individuales más largas que su búfer, deberá procesarlas en dos partes. De lo contrario, tiene la oportunidad de informar al usuario sobre un error cortésmente sin crear un agujero de seguridad de desbordamiento del búfer.

Independientemente, siempre valide los valores devueltos y piense en cómo manejar la entrada incorrecta, maliciosa o simplemente mal formada.

RBerteig
fuente
1

Puedes usar fgets y limitar el tamaño de la cadena de lectura.

char *fgets(char *str, int num, FILE *stream);

Puede cambiar el whileen su código a:

while (fgets(str, 100, file)) /* printf("%s", str) */;
Edu
fuente
0

Puede leer el archivo completo con asignación de memoria dinámica, pero no es una buena idea porque si el archivo es demasiado grande, podría tener problemas de memoria.

Por lo tanto, es mejor leer partes cortas del archivo e imprimirlo.

#include <stdio.h>
#define BLOCK   1000

int main() {
    FILE *f=fopen("teste.txt","r");
    int size;
    char buffer[BLOCK];
    // ...
    while((size=fread(buffer,BLOCK,sizeof(char),f)>0)
            fwrite(buffer,size,sizeof(char),stdout);
    fclose(f);
    // ...
    return 0;
}
rigon
fuente