C'de, bir metin dosyasını nasıl okuyup tüm dizeleri nasıl yazdırmalıyım


94

Adlı bir metin dosyam var test.txt

Bu dosyayı okuyabilen ve içeriği konsola yazdırabilen bir C programı yazmak istiyorum (dosyanın yalnızca ASCII metni içerdiğini varsayın).

Dize değişkenimin boyutunu nasıl elde edeceğimi bilmiyorum. Bunun gibi:

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

Boyut 999çalışmaz çünkü döndürülen dize fscanfbundan daha büyük olabilir. Bunu Nasıl Çözebilirim?

Yanıtlar:


134

En basit yol, bir karakteri okumak ve okuduktan hemen sonra yazdırmaktır:

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

colduğu intiçin, yukarıda belirtilen EOFnegatif bir sayı olan, ve bir düz charolabilir unsigned.

Dosyayı parçalar halinde, ancak dinamik bellek ayırmadan okumak istiyorsanız, şunları yapabilirsiniz:

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

Yukarıdaki ikinci yöntem, dinamik olarak ayrılmış bir diziye sahip bir dosyayı nasıl okuyacağınızdır:

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

Formatı fscanf()ile yönteminiz %sdosyadaki boşluklarla ilgili bilgileri kaybeder, bu nedenle bir dosyayı tam olarak kopyalamıyor stdout.


Dosyadaki verileri c / c ++ 'da açmadan okumak mümkündür.
Sagar Patel

metin dosyası virgülle ayrılmış tamsayı değerleri içeriyorsa ne olur? kodun ne olacağından ziyade cevabınızı içinde bununla birlikte düzenleyebilirsiniz.
Mohsin

Yukarıdakiler her tür metin dosyası için çalışır. Sayıları bir CSV dosyasından ayrıştırmak istiyorsanız, bu farklı bir sorundur.
Alok Singhal

1
@overexchange Soru satırlardan bahsetmiyor - bir dosyayı okumak ve içeriğini kopyalamakla ilgili stdout.
Alok Singhal

1
@shjeff Bir dosya EOF karakteri içeremez. Bunun cint olduğunu ve C'nin EOFherhangi bir geçerli karaktere eşit olmadığını garanti edeceğini unutmayın.
Alok Singhal

60

Burada parçalar halinde okumakla ilgili pek çok iyi cevap var, size tüm içeriği aynı anda bir ara belleğe okuyan ve yazdıran küçük bir numara göstereceğim.

Daha iyi olduğunu söylemiyorum. Değil ve Ricardo gibi bazen kötü olabilir, ancak basit durumlar için güzel bir çözüm buluyorum.

Yorumlarla serptim çünkü çok şey oluyor.

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

Faydalı olup olmadığını bana bildirin yoksa ondan bir şeyler öğrenebilirsin :)


2
Bunun buffer[string_size] = '\0';yerine okunması gerekmez string_size+1mi? Afaik gerçek bir dize gider 0etmek string_size-1ve \0karakter böylece de olması gerekiyor string_size, değil mi?
aepsil0n

4
Kullanılması ftellve fseekbir dosyanın boyutunu bulmak için güvensiz: securecoding.cert.org/confluence/display/seccode/...
Joakim

1
Bu kod bir bellek sızıntısı içeriyor, dosyayı asla kapatmazsınız. Bir kayıp varfclose(handle)
Joakim

1
Fclose (tanıtıcı) olarak adlandırdığınız bir yazım hatası var, bu fclose (işleyici) olmalıdır
Eduardo Cobuci

3
Boş sonlandırıcıyı ayarlamak zorunda kalmadan atlamak calloc(2)yerine kullanabilirsiniz malloc(1).

14

Bunun yerine, karakterleri doğrudan konsola yazdırın çünkü metin dosyası çok büyük olabilir ve çok fazla belleğe ihtiyaç duyabilirsiniz.

#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

Fscanf yerine "read ()" kullanın:

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

AÇIKLAMA

Read () işlevi, nbyteaçık dosya tanımlayıcısıyla ilişkili dosyadaki baytları ile fildesgösterilen arabelleğe okumaya çalışacaktır buf.

İşte bir örnek:

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

Bu örnekten çalışma kısmı:

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

Alternatif bir yaklaşım, bir seferde 1 karakter okumak / yazmak için getc/ kullanmaktır putc. Çok daha az verimli. İyi bir örnek: http://www.eskimo.com/~scs/cclass/notes/sx13.html


readbelirli sayıda karakter okumanıza izin verir. Arabelleğinizi dolduracak kadar okuyun, ardından arabelleğinizi ekrana boşaltın, temizleyin ve dosyanın sonuna gelene kadar tekrarlayın.
bta

1

Akla iki yaklaşım gelir.

İlk önce kullanmayın scanf. Kullanım fgets()tampon boyutunu belirtmek için bir parametre alır ve bozulmamış herhangi satır karakterleri bırakır. Arabellek içeriğini yazdıran dosya üzerindeki basit bir döngü, dosyayı doğal olarak bozulmadan kopyalamalıdır.

İkincisi, ile fread()veya ortak C deyimini kullanın fgetc(). Bunlar, dosyayı sabit boyutlu parçalar halinde veya bir seferde tek bir karakter halinde işler.

Dosyayı boşlukla sınırlanmış dizeler üzerinde işlemeniz gerekiyorsa, dosyayı okumak için ya fgetsda freadkullanın strtokve arabelleği beyaz boşlukta bölmek gibi bir şey yapın . Bir arabellekten diğerine geçişi işlemeyi unutmayın, çünkü hedef dizelerinizin arabellek sınırını aşması muhtemeldir.

scanfOkumayı yapmak için harici bir gereksinim varsa , o zaman okuyabileceği dizenin uzunluğunu biçim belirticisinde bir hassas alanla sınırlayın. 999 baytlık bir arabelleğe scanf("%998s", str);sahip durumunuzda, nul sonlandırıcı için yer bırakan arabelleğe en fazla 998 karakter yazacağını söyleyin . Tamponunuzdan daha uzun tek dizelere izin veriliyorsa, bunları iki parça halinde işlemeniz gerekir. Değilse, bir arabellek taşması güvenlik açığı oluşturmadan kullanıcıya nazikçe bir hatayı anlatma fırsatınız vardır.

Ne olursa olsun, her zaman dönüş değerlerini doğrulayın ve kötü, kötü niyetli veya sadece hatalı biçimlendirilmiş girdilerin nasıl ele alınacağını düşünün.


1

fgetsOkuma dizesinin boyutunu kullanabilir ve sınırlayabilirsiniz.

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

whileKodunuzu şu şekilde değiştirebilirsiniz :

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

0

Dosyanın tamamını dinamik bellek ayırma ile okuyabilirsiniz, ancak bu iyi bir fikir değildir çünkü dosya çok büyükse bellek sorunlarınız olabilir.

Bu yüzden dosyanın kısa kısımlarını okuyup yazdırmak daha iyidir.

#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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.