Apa alasan untuk fread / fwrite mengambil ukuran dan menghitung sebagai argumen?

96

Kami berdiskusi di sini tentang mengapa fread dan fwrite mengambil ukuran per anggota dan menghitung serta mengembalikan jumlah anggota yang dibaca / ditulis daripada hanya mengambil buffer dan ukuran. Satu-satunya penggunaan untuk itu yang bisa kami hasilkan adalah jika Anda ingin membaca / menulis array struct yang tidak dapat dibagi secara merata oleh perataan platform dan karenanya telah empuk tetapi itu tidak bisa begitu umum untuk menjamin pilihan ini dalam desain.

Dari FREAD (3) :

Fungsi fread () membaca nmemb elemen data, masing-masing berukuran panjang byte, dari aliran yang ditunjuk oleh aliran, menyimpannya di lokasi yang diberikan oleh ptr.

Fungsi fwrite () menulis elemen nmemb data, masing-masing berukuran panjang byte, ke aliran yang ditunjuk oleh aliran, memperolehnya dari lokasi yang diberikan oleh ptr.

fread () dan fwrite () mengembalikan jumlah item yang berhasil dibaca atau ditulis (yaitu, bukan jumlah karakter). Jika terjadi kesalahan, atau akhir file tercapai, nilai yang dikembalikan adalah hitungan item pendek (atau nol).

David Holm
sumber
10
hei ini pertanyaan yang bagus. Saya selalu bertanya-tanya tentang itu
Johannes Schaub - litb

Jawaban:

22

Ini didasarkan pada bagaimana fread diterapkan.

Spesifikasi UNIX Tunggal mengatakan

Untuk setiap objek, pemanggilan ukuran harus dilakukan ke fungsi fgetc () dan hasilnya disimpan, dalam urutan baca, dalam larik karakter yang tidak bertanda tangan yang persis menutupi objek.

fgetc juga memiliki catatan ini:

Karena fgetc () beroperasi pada byte, membaca karakter yang terdiri dari beberapa byte (atau "karakter multi-byte") mungkin memerlukan beberapa panggilan ke fgetc ().

Tentu saja, ini mendahului pengkodean karakter byte variabel yang mewah seperti UTF-8.

SUS mencatat bahwa ini sebenarnya diambil dari dokumen ISO C.

Powerlord
sumber
72

Perbedaan fread (buf, 1000, 1, stream) dan fread (buf, 1, 1000, stream) adalah, dalam kasus pertama Anda hanya mendapatkan satu potongan 1000 byte atau nuthin, jika file lebih kecil dan di kasus kedua Anda mendapatkan semua yang ada di file kurang dari dan hingga 1000 byte.

Peter Miehle
sumber
4
Meski benar, itu hanya menceritakan sebagian kecil dari cerita. Akan lebih baik untuk membandingkan sesuatu yang membaca, katakanlah, array nilai int, atau array struktur.
Jonathan Leffler
3
Ini akan menjadi jawaban yang bagus jika pembenaran diselesaikan.
Matt Joiner
13

Ini adalah spekulasi murni, namun di masa lalu (Beberapa masih ada) banyak sistem file bukanlah aliran byte sederhana pada hard drive.

Banyak sistem file yang berbasiskan catatan, sehingga untuk memenuhi sistem file tersebut dengan cara yang efisien, Anda harus menentukan jumlah item ("catatan"), yang memungkinkan fwrite / fread untuk beroperasi pada penyimpanan sebagai catatan, bukan hanya aliran byte.

no
sumber
1
Saya senang seseorang mengungkit hal ini. Saya melakukan banyak pekerjaan dengan spesifikasi sistem file dan FTP dan catatan / halaman serta konsep pemblokiran lainnya didukung dengan sangat kuat, meskipun tidak ada yang menggunakan bagian-bagian spesifikasi itu lagi.
Matt Joiner
9

Di sini, izinkan saya memperbaiki fungsi tersebut:

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

Adapun alasan untuk parameter ke fread()/ fwrite(), saya telah kehilangan salinan K&R saya sejak lama jadi saya hanya bisa menebak. Saya pikir jawaban yang mungkin adalah bahwa Kernighan dan Ritchie mungkin hanya berpikir bahwa melakukan I / O biner akan paling alami dilakukan pada array objek. Juga, mereka mungkin berpikir bahwa blok I / O akan lebih cepat / lebih mudah untuk diimplementasikan atau apapun pada beberapa arsitektur.

Meskipun standar C menentukan itu fread()dan fwrite()diimplementasikan dalam istilah fgetc()dan fputc(), ingatlah bahwa standar muncul lama setelah C didefinisikan oleh K&R dan bahwa hal-hal yang ditentukan dalam standar mungkin tidak ada dalam gagasan perancang asli. Bahkan mungkin saja hal-hal yang dikatakan dalam "The C Programming Language" K & R mungkin tidak sama dengan saat bahasa pertama kali dirancang.

Terakhir, inilah yang dikatakan PJ Plauger tentang fread()"The Standard C Library":

Jika sizeargumen (detik) lebih besar dari satu, Anda tidak dapat menentukan apakah fungsi juga membaca hingga size - 1karakter tambahan di luar laporannya. Sebagai aturan, Anda lebih baik memanggil fungsi sebagai fread(buf, 1, size * n, stream);gantinya fread(buf, size, n, stream);

Pada dasarnya, dia mengatakan bahwa fread()antarmuka rusak. Karena fwrite()dia mencatat bahwa, "Kesalahan penulisan umumnya jarang, jadi ini bukan kekurangan utama" - pernyataan yang tidak saya setujui.

Michael Burr
sumber
17
Sebenarnya saya sering suka melakukannya dengan cara lain: fread(buf, size*n, 1, stream);Jika pembacaan yang tidak lengkap adalah kondisi kesalahan, lebih mudah untuk mengatur freadagar hanya mengembalikan 0 atau 1 daripada jumlah byte yang dibaca. Kemudian Anda dapat melakukan hal-hal seperti if (!fread(...))daripada harus membandingkan hasil dengan jumlah byte yang diminta (yang memerlukan kode C ekstra dan kode mesin tambahan).
R .. GitHub STOP HELPING ICE
1
@R .. Pastikan untuk memeriksa ukuran * hitung! = 0 selain! Fread (...). Jika size * count == 0, Anda mendapatkan nilai pengembalian nol pada pembacaan yang berhasil (dari nol byte), feof () dan ferror () tidak akan disetel, dan errno akan menjadi sesuatu yang tidak masuk akal seperti ENOENT, atau lebih buruk , sesuatu yang menyesatkan (dan mungkin sangat merusak) seperti EAGAIN - sangat membingungkan, terutama karena pada dasarnya tidak ada dokumentasi yang meneriakkan hal ini pada Anda.
Pegasus Epsilon
3

Kemungkinan itu kembali ke cara file I / O diimplementasikan. (dahulu kala) Mungkin lebih cepat untuk menulis / membaca ke file dalam blok kemudian menulis semuanya sekaligus.

dolch
sumber
Tidak juga. Spesifikasi C untuk catatan fwrite yang membuat panggilan berulang ke fputc: opengroup.org/onlinepubs/009695399/functions/fwrite.html
Powerlord
1

Memiliki argumen terpisah untuk ukuran dan jumlah dapat bermanfaat pada implementasi yang dapat menghindari pembacaan catatan parsial apa pun. Jika seseorang menggunakan pembacaan byte tunggal dari sesuatu seperti pipa, bahkan jika seseorang menggunakan data format tetap, ia harus memungkinkan kemungkinan rekaman terbagi menjadi dua pembacaan. Jika sebaliknya dapat meminta misalnya pembacaan non-pemblokiran hingga 40 catatan masing-masing 10 byte ketika ada 293 byte tersedia, dan memiliki sistem mengembalikan 290 byte (29 seluruh catatan) sementara meninggalkan 3 byte siap untuk pembacaan berikutnya, itu akan jauh lebih nyaman.

Saya tidak tahu sejauh mana implementasi fread dapat menangani semantik seperti itu, tetapi mereka pasti bisa berguna pada implementasi yang menjanjikan untuk mendukungnya.

supercat
sumber
@PegasusEpsilon: Jika misalnya sebuah program melakukannya fread(buffer, 10000, 2, stdin)dan pengguna mengetik newline-ctrl-D setelah mengetik 18.000 byte, alangkah baiknya jika fungsi tersebut dapat mengembalikan 10.000 byte pertama sementara sisanya 8.000 menunggu untuk permintaan baca yang lebih kecil di masa mendatang, tetapi apakah ada implementasi apapun yang akan terjadi? Di mana 8.000 byte akan disimpan sambil menunggu permintaan di masa mendatang?
supercat
Setelah mengujinya, ternyata fread () tidak beroperasi dengan cara yang saya anggap paling nyaman dalam hal ini, tetapi kemudian memasukkan byte kembali ke buffer baca setelah menentukan pembacaan singkat mungkin sedikit lebih dari yang seharusnya kita harapkan dari fungsi perpustakaan standar. fread () akan membaca sebagian catatan dan mendorongnya ke buffer, tetapi nilai yang dikembalikan akan menentukan berapa banyak catatan lengkap yang telah dibaca, dan tidak memberi tahu Anda apa pun (yang cukup mengganggu saya) tentang pembacaan singkat yang dilakukan stdin.
Pegasus Epsilon
... lanjutan ... Yang terbaik yang dapat Anda lakukan adalah mengisi buffer baca Anda dengan nulls before fread, dan memeriksa record setelah di mana fread () mengatakan itu selesai untuk byte non-null. Tidak terlalu membantu Anda ketika catatan Anda mungkin berisi nol, tetapi jika Anda akan menggunakan sizelebih dari 1, yah ... Sebagai catatan, mungkin juga ada ioctl atau omong kosong lainnya yang dapat Anda terapkan ke aliran untuk membuatnya berperilaku berbeda, saya belum mendalami itu.
Pegasus Epsilon
Juga saya telah menghapus komentar saya sebelumnya karena ketidakakuratan. Baiklah.
Pegasus Epsilon
@PegasusEpsilon: C digunakan di banyak platform, yang mengakomodasi berbagai perilaku. Gagasan bahwa programmer harus berharap untuk menggunakan fitur dan jaminan yang sama pada semua implementasi mengabaikan apa yang telah menjadi fitur terbaik dari C: bahwa desainnya akan memungkinkan programmer untuk menggunakan fitur dan jaminan pada platform yang tersedia. Beberapa jenis aliran dapat mendukung pushback berukuran arbitrer dengan mudah, dan memiliki freadpekerjaan seperti yang Anda jelaskan pada aliran tersebut akan berguna jika ada beberapa cara untuk mengidentifikasi aliran yang berfungsi dengan cara tersebut.
supercat
0

Saya pikir itu karena C kekurangan fungsi overloading. Jika ada, ukuran akan menjadi mubazir. Tetapi di C Anda tidak dapat menentukan ukuran elemen array, Anda harus menentukannya.

Pertimbangkan ini:

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

Jika fwrite jumlah byte yang diterima, Anda dapat menulis sebagai berikut:

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

Tapi itu tidak efisien. Anda akan memiliki ukuran (int) kali lebih banyak panggilan sistem.

Hal lain yang harus dipertimbangkan adalah Anda biasanya tidak ingin bagian dari elemen array ditulis ke file. Anda ingin keseluruhan integer atau tidak sama sekali. fwrite mengembalikan sejumlah elemen yang berhasil ditulis. Jadi jika Anda menemukan bahwa hanya 2 byte rendah dari sebuah elemen yang ditulis, apa yang akan Anda lakukan?

Pada beberapa sistem (karena penyelarasan) Anda tidak dapat mengakses satu byte integer tanpa membuat salinan dan pergeseran.

Vanuan
sumber