Fread / fwrite'ın boyutu almasının ve argüman olarak sayılmasının mantığı nedir?


96

Fread ve fwrite'ın neden üye başına bir boyut alıp, sadece bir tampon ve boyut almak yerine okunan / yazılan üye sayısını sayıp geri döndürdüğüne dair işte burada bir tartışma yaptık. Bunun için bulabileceğimiz tek kullanım, platform hizalamasıyla eşit olarak bölünemeyen ve dolayısıyla doldurulmuş bir dizi yapı okumak / yazmak istiyorsanız, ancak bu seçimi garanti edecek kadar yaygın olamaz. tasarımda.

Gönderen Fread (3) :

Fread () işlevi, akış tarafından gösterilen akıştan her boyut bayt uzunluğunda nmemb veri öğelerini okur ve bunları ptr tarafından verilen konumda depolar.

Fwrite () işlevi, akış tarafından gösterilen akışa her boyut bayt uzunluğunda nmemb veri öğelerini yazar ve bunları ptr tarafından verilen konumdan elde eder.

fread () ve fwrite () başarıyla okunan veya yazılan öğelerin sayısını döndürür (yani, karakter sayısını değil). Bir hata oluşursa veya dosyanın sonuna ulaşılırsa, dönüş değeri kısa bir öğe sayısıdır (veya sıfırdır).


10
hey bu iyi bir soru. her zaman merak etmişimdir
Johannes Schaub - litb

1
Lütfen şu konu
Franken

Yanıtlar:


22

Fread'in nasıl uygulandığına dayanır .

Tek UNIX Spesifikasyonu diyor ki

Her nesne için, boyut çağrıları fgetc () işlevine yapılmalı ve sonuçlar, okunma sırasına göre, nesneyi tam olarak kaplayan işaretsiz karakter dizisinde depolanmalıdır.

fgetc ayrıca şu nota sahiptir:

Fgetc () baytlar üzerinde çalıştığından, birden çok bayttan (veya "çok baytlı bir karakterden") oluşan bir karakteri okumak, fgetc () için birden çok çağrı gerektirebilir.

Tabii ki, bu UTF-8 gibi süslü değişken bayt karakter kodlamalarından önce gelir.

SUS, bunun aslında ISO C belgelerinden alındığını not eder.


72

Fread (buf, 1000, 1, stream) ve fread (buf, 1, 1000, stream) arasındaki fark, ilk durumda dosya daha küçükse ve İkinci durumda, dosyadaki her şeyi 1000 bayttan daha az ve en fazla olanı alırsınız.


4
Doğru olmasına rağmen, bu hikayenin sadece küçük bir bölümünü anlatıyor. Örneğin bir int değerleri dizisi veya bir yapı dizisi okuyan bir şeyi karşılaştırmak daha iyi olacaktır.
Jonathan Leffler

3
Gerekçelendirme tamamlanırsa bu harika bir cevap olur.
Matt Joiner

13

Bu saf bir spekülasyondur, ancak günlerde (Bazıları hala ortalıktadır) birçok dosya sistemi bir sabit sürücüdeki basit bayt akışları değildi.

Birçok dosya sistemi kayıt tabanlıdır, bu nedenle bu tür dosya sistemlerini verimli bir şekilde karşılamak için, fwrite / fread'in sadece bayt akışları değil, kayıtlar olarak depolamada çalışmasına izin veren öğe sayısını ("kayıtlar") belirtmeniz gerekir.


1
Birisinin bu konuyu açmasına sevindim. Dosya sistemi özellikleri ve FTP ile çok çalıştım ve kayıtlar / sayfalar ve diğer engelleme kavramları artık hiç kimse teknik özelliklerin bu kısımlarını kullanmasa da çok sıkı bir şekilde destekleniyor.
Matt Joiner

9

Burada, bu işlevleri düzeltmeme izin verin:

size_t fread_buf( void* ptr, size_t size, FILE* stream)
{
    return fread( ptr, 1, size, stream);
}


size_t fwrite_buf( void const* ptr, size_t size, FILE* stream)
{
    return fwrite( ptr, 1, size, stream);
}

Parametreler için bir gerekçe gelince fread()/ fwrite()ben uzun zaman önce sadece tahmin edebilirsiniz böylece K & R kopyamı kaybettim. Bence olası bir yanıt, Kernighan ve Ritchie'nin basitçe ikili G / Ç gerçekleştirmenin en doğal olarak nesne dizileri üzerinde yapılacağını düşünmüş olmalarıdır. Ayrıca, blok G / Ç'nin daha hızlı / daha kolay uygulanacağını veya bazı mimarilerde herhangi bir şekilde olacağını düşünmüş olabilirler.

Hatta bu C standart belirttiği olsa fread()ve fwrite()bakımından uygulanacak fgetc()ve fputc()standart C K & R tarafından ve standart kudretini belirtilen şeyler değildir orijinal tasarımcıları fikirleri olduklarını tanımlandı çok sonra ortaya çıktığını hatırlıyorum. K & R'nin "C Programlama Dili" nde söylenenlerin, dilin ilk tasarlandığı zamandaki ile aynı olmaması bile mümkündür.

Son olarak, PJ Plauger'ın fread()"The Standard C Library" de söyleyecekleri :

Eğer size(ikinci) bağımsız değişken birden fazla olduğu, işlevin de kadar okuma olmadığını belirlemek edemez size - 1o raporları ötesinde ek karakterler. Kural olarak, işlevi fread(buf, 1, size * n, stream);yerine işlev olarak çağırmanız daha iyidirfread(buf, size, n, stream);

Temelde, fread()arayüzünün bozuk olduğunu söylüyor . Çünkü fwrite(), "Yazma hataları genellikle nadirdir, bu yüzden bu büyük bir eksiklik değildir" - hemfikir olmadığım bir ifade.


17
Aslında bunu genellikle başka bir şekilde yapmaktan hoşlanırım: fread(buf, size*n, 1, stream);Eğer eksik okumalar bir hata durumuysa, freadokunan bayt sayısı yerine sadece 0 veya 1 döndürmeyi ayarlamak daha kolaydır . Ardından if (!fread(...)), sonucu istenen bayt sayısıyla karşılaştırmak yerine (ekstra C kodu ve ekstra makine kodu gerektirir) gibi şeyler yapabilirsiniz .
R .. GitHub

1
@R .. Sadece! Fread (...) 'e ek olarak * count! = 0 boyutunu kontrol ettiğinizden emin olun. Size * count == 0 ise, başarılı bir okumada (sıfır baytlık) sıfır dönüş değeri alıyorsunuz , feof () ve ferror () ayarlanmayacak ve errno ENOENT gibi saçma bir şey olacak veya daha kötüsü , EAGAIN gibi yanıltıcı (ve muhtemelen kritik bir şekilde kırıcı) bir şey - çok kafa karıştırıcı, özellikle de temelde hiçbir belge size bu yakalamayı haykırmadığı için.
Pegasus Epsilon

3

Muhtemelen dosya G / Ç'nin uygulanma şekline geri dönüyor. (eski gün) Dosyaları bloklar halinde yazmak / okumak, sonra her şeyi bir kerede yazmak daha hızlı olabilirdi.


Pek sayılmaz. Fwrite
Powerlord

1

Boyut ve sayı için ayrı bağımsız değişkenlere sahip olmak, herhangi bir kısmi kaydı okumaktan kaçınabilen bir uygulamada avantajlı olabilir. Sabit biçimli veriler kullanılıyor olsa bile, boru gibi bir şeyden tek baytlık okumalar kullanılacaksa, bir kaydın iki okumaya bölünmesi olasılığına izin verilmesi gerekirdi. Bunun yerine, örneğin mevcut 293 bayt varken her biri 10 baytlık 40 kayıttan bloke olmayan bir okuma talep edebilir ve sistem bir sonraki okuma için 3 baytı hazır bırakırken 290 bayt (29 tam kayıt) döndürebilirse, çok daha rahat olun.

Fread uygulamalarının bu tür anlambilimle ne ölçüde başa çıkabileceğini bilmiyorum, ancak onları desteklemeyi vaat edebilecek uygulamalarda kesinlikle kullanışlı olabilirler.


@PegasusEpsilon: Örneğin bir program yaparsa fread(buffer, 10000, 2, stdin)ve kullanıcı 18.000 bayt yazdıktan sonra newline-ctrl-D yazarsa , işlevin ilk 10.000 baytı döndürürken kalan 8.000'i gelecekteki daha küçük okuma istekleri için beklemede bırakması güzel olurdu, ancak orada bunun olacağı herhangi bir uygulama var mı? Gelecekteki talepleri beklerken 8.000 bayt nerede saklanacak?
2019

Henüz test ettikten sonra, fread () bu konuda en uygun şekilde düşündüğüm şekilde çalışmıyor, ancak kısa bir okuma belirledikten sonra baytları okuma arabelleğine geri doldurmak muhtemelen beklediğimizden biraz daha fazla. yine de standart kütüphane fonksiyonları. fread () kısmi kayıtları okuyacak ve onları arabelleğe aktaracak, ancak dönüş değeri kaç tane tam kaydın okunduğunu belirleyecek ve stdin'den alınan kısa okumalar hakkında size hiçbir şey söylemeyecek (ki bu benim için oldukça rahatsız edici).
Pegasus Epsilon

... devamı ... Yapabileceğiniz en iyi şey, okuma arabelleğinizi fread'den önce boş değerlerle doldurmak ve fread () herhangi bir boş olmayan bayt için bittiğini söylediği andan sonra kaydı kontrol etmektir. Kayıtlarınız null içerdiğinde özellikle size yardımcı olmaz, ancak size1'den büyük kullanacaksanız , şey ... Kayıt için ioctl'ler veya başka saçmalıklar da olabilir, bunu yapmak için akışa başvurabilirsiniz farklı davranıyorum, o kadar derine inmedim.
Pegasus Epsilon

Ayrıca yanlışlık nedeniyle daha önceki yorumumu da sildim. Oh iyi.
Pegasus Epsilon

@PegasusEpsilon: C, farklı davranışları barındıran pek çok platformda kullanılmaktadır. Programcıların tüm uygulamalarda aynı özellikleri ve garantileri kullanmayı beklemesi gerektiği fikri, C'nin en iyi özelliğini görmezden geliyor: tasarımının, programcıların mevcut oldukları platformlarda özellikleri ve garantileri kullanmasına izin vereceği. Bazı akış türleri, isteğe bağlı boyuttaki geri itmeleri kolayca destekleyebilir ve freadbu şekilde çalışan akışları tanımlamanın bir yolu varsa, bu tür akışlar üzerinde tanımladığınız şekilde çalışmak yararlı olacaktır.
supercat

0

Bence C'nin aşırı fonksiyon yüklemesinden yoksun olması. Bazıları olsaydı, boyut gereksiz olurdu. Ancak C'de bir dizi elemanının boyutunu belirleyemezsiniz, bir tane belirtmeniz gerekir.

Bunu düşün:

int intArray[10];
fwrite(intArray, sizeof(int), 10, fd);

Fwrite kabul edilen bayt sayısı ise, aşağıdakileri yazabilirsiniz:

int intArray[10];
fwrite(intArray, sizeof(int)*10, fd);

Ama sadece verimsiz. Sizeof (int) kat daha fazla sistem çağrısına sahip olacaksınız.

Dikkate alınması gereken bir diğer nokta, genellikle bir dizi elemanının bir kısmının bir dosyaya yazılmasını istememenizdir. Tam sayıyı ya da hiçbir şeyi istiyorsun. fwrite, başarıyla yazılmış bir dizi öğe döndürür. Öyleyse, bir elemanın yalnızca 2 düşük baytının yazıldığını keşfederseniz ne yapardınız?

Bazı sistemlerde (hizalama nedeniyle) bir kopya oluşturmadan ve kaydırmadan bir tamsayının bir baytına erişemezsiniz.

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.