В C, как мне читать текстовый файл и печатать все строки


94

У меня есть текстовый файл с именем test.txt

Я хочу написать программу на C, которая может читать этот файл и выводить содержимое на консоль (предположим, что файл содержит только текст ASCII).

Я не знаю, как получить размер моей строковой переменной. Как это:

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

Размер 999не работает, потому что строка, возвращаемая функцией, fscanfможет быть больше. Как я могу это решить?

Ответы:


134

Самый простой способ - прочитать символ и распечатать его сразу после прочтения:

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

cнаходится intвыше, так как EOFотрицательное число, а простой charможет быть unsigned.

Если вы хотите читать файл по частям, но без динамического выделения памяти, вы можете:

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

Второй метод выше, по сути, заключается в том, как вы будете читать файл с динамически распределенным массивом:

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 */
}

Ваш метод fscanf()с %sформатом as теряет информацию о пробелах в файле, поэтому это не точное копирование файла в stdout.


Можно читать данные из файла, не открывая этот файл в c / c ++ ??
Сагар Патель,

что, если текстовый файл содержит целые числа, разделенные запятыми? чем то, что было бы кодом, вы можете отредактировать свой ответ, тоже в нем.
Mohsin

Вышеупомянутое работает для любого текстового файла. Если вы хотите проанализировать числа из файла CSV, это другая проблема.
Алок Сингхал,

1
@overexchange Вопрос не касается строк - речь идет о чтении файла и копировании его содержимого в stdout.
Алок

1
@shjeff Файл не может содержать символ EOF. Обратите внимание, что cэто int, и C гарантирует, что EOFон не равен ни одному допустимому символу.
Алок

60

Здесь есть много хороших ответов о чтении его по частям, я просто покажу вам небольшой трюк, который считывает весь контент сразу в буфер и печатает его.

Я не говорю, что лучше. Это не так, и, как и у Рикардо, иногда это может быть плохо, но я считаю, что это хорошее решение для простых случаев.

Я посыпал это комментариями, потому что там много всего происходит.

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

Дайте мне знать, если это будет полезно или вы могли бы чему-то научиться :)


2
Разве не следует читать buffer[string_size] = '\0';вместо string_size+1? Afaik фактическая строка идет от 0к string_size-1и \0символ, таким образом, должен быть в string_size, верно?
aepsil0n 03

4
Использование ftellи fseekдля определения размера файла небезопасно: securecoding.cert.org/confluence/display/seccode/…
Joakim

1
Этот код содержит утечку памяти, вы никогда не закрываете файл. Пропавший без вестиfclose(handle)
Йоаким

1
Произошла опечатка, когда вы вызываете fclose (handle), это должно быть fclose (обработчик)
Эдуардо Кобучи

3
Вы можете использовать, calloc(2)а не malloc(1)пропустить установку нулевого терминатора.

14

Вместо этого просто напечатайте символы прямо на консоли, потому что текстовый файл может быть очень большим, и вам может потребоваться много памяти.

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

6

Используйте "read ()" вместо fscanf:

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

ОПИСАНИЕ

Функция read () должна попытаться прочитать nbyteбайты из файла, связанного с дескриптором открытого файла fildes, в буфер, на который указывает buf.

Вот пример:

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

Рабочая часть из этого примера:

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

Альтернативный подход - использовать getc/ putcдля чтения / записи по 1 символу за раз. Намного менее эффективен. Хороший пример: http://www.eskimo.com/~scs/cclass/notes/sx13.html


readпозволит вам прочитать определенное количество символов. Прочтите достаточно, чтобы заполнить буфер, затем выгрузите буфер на экран, очистите его и повторяйте, пока не дойдете до конца файла.
bta

1

На ум приходят два подхода.

Во-первых, не используйте scanf. Используйте fgets()который принимает параметр, чтобы указать размер буфера, и оставляет любые символы новой строки нетронутыми. Простой цикл над файлом, который печатает содержимое буфера, должен, естественно, скопировать файл без изменений.

Во- вторых, использование fread()или общий C идиомы с fgetc(). Они будут обрабатывать файл кусками фиксированного размера или по одному символу за раз.

Если вы должны обработать файл поверх строк, разделенных пробелами, используйте либо, fgetsлибо, freadчтобы прочитать файл, и что-то вроде strtokразделения буфера на пробелы. Не забывайте обрабатывать переход от одного буфера к другому, так как ваши целевые строки, вероятно, будут выходить за границы буфера.

Если есть внешнее требование для использования scanfдля чтения, ограничьте длину строки, которую она может прочитать, с помощью поля точности в описателе формата. В вашем случае с 999-байтовым буфером скажите, scanf("%998s", str);который будет записывать не более 998 символов в буфер, оставляя место для нулевого терминатора. Если разрешены отдельные строки длиннее, чем ваш буфер, вам придется обрабатывать их двумя частями. Если нет, у вас есть возможность вежливо сообщить пользователю об ошибке, не создавая бреши в защите от переполнения буфера.

В любом случае всегда проверяйте возвращаемые значения и думайте о том, как обрабатывать неверный, злонамеренный или просто искаженный ввод.


1

Вы можете использовать fgetsи ограничивать размер читаемой строки.

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

Вы можете изменить whileв своем коде на:

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

0

Вы можете прочитать весь файл с динамическим распределением памяти, но это не лучшая идея, потому что, если файл слишком большой, у вас могут быть проблемы с памятью.

Так что лучше читать короткие части файла и распечатывать.

#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;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.