У 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 ++ ??
Сагар Пател

що, якщо текстовий файл містить цілі значення, розділені комами? ніж який би був код, ви можете відредагувати свою відповідь із цим також у ньому.
Мохсін,

Вищезазначене підходить для будь-якого типу текстових файлів. Якщо ви хочете проаналізувати номери з файлу CSV, це інша проблема.
Alok Singhal

1
@overexchange Питання не говорить про рядки - це про читання файлу та копіювання його вмісту stdout.
Alok Singhal

1
@shjeff Файл не може містити символ EOF. Зверніть увагу, що cце int, а C гарантуватиме, що EOFвоно не дорівнює жодному дійсному символу.
Alok Singhal

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

4
Використовувати ftellта fseekзнаходити розмір файлу небезпечно: securecoding.cert.org/confluence/display/seccode/…
Йоакім

1
Цей код містить витік пам'яті, ви ніколи не закриваєте файл. Існує відсутнюfclose(handle)
Йоаким

1
Існує помилка, при якій ви називаєте fclose (дескриптор), це має бути 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://cmagic.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.