Close Menu
    Ciekawe

    Jak podłączyć telefon do monitora? Przewodowe i bezprzewodowe sposoby

    2025-12-08

    Co można wrzucić w koszty firmy jednoosobowej? Lista i praktyczne przykłady

    2025-12-03

    Jak podłączyć okulary VR do PS4? Poradnik podłączenia i konfiguracji

    2025-12-02
    Facebook X (Twitter) Instagram
    CPP Polska
    Facebook X (Twitter) Instagram
    • Biznes

      Co można wrzucić w koszty firmy jednoosobowej? Lista i praktyczne przykłady

      2025-12-03

      Jak zapobiec wyciekom danych firmowych?

      2025-11-28

      Ile kosztuje prowadzenie jednoosobowej działalności gospodarczej? Przegląd opłat

      2025-11-10

      Jak wziąć samochód w leasing bez firmy? Poradnik dla osób fizycznych

      2025-10-29

      Jak założyć firmę jednoosobową krok po kroku – koszty, formalności i czas trwania

      2025-10-23
    • Technologie

      Jak podłączyć telefon do monitora? Przewodowe i bezprzewodowe sposoby

      2025-12-08

      Jak podłączyć okulary VR do PS4? Poradnik podłączenia i konfiguracji

      2025-12-02

      Jak zapobiec wyciekom danych firmowych?

      2025-11-28

      Jak sprawdzić rozdzielczość monitora w Windows i macOS?

      2025-11-26

      Jak zresetować laptopa Acer do ustawień fabrycznych? Poradnik krok po kroku

      2025-11-25
    • Programowanie

      Maszyna stanów oparta o std::variant

      2025-10-07

      std::deque w C++ – kiedy wybrać dwukierunkową kolejkę zamiast vectora

      2025-10-07

      Tablice w C++ od podstaw – deklaracja, inicjalizacja, iteracja i typowe pułapki

      2025-10-07

      itoa i std::to_chars – konwersja liczb na tekst bez narzutu wydajności

      2025-10-07

      strcpy vs strncpy vs std::string – bezpieczne kopiowanie łańcuchów w C++

      2025-10-07
    • Inne

      Jak prowadzić blog programistyczny i dzielić się wiedzą?

      2025-06-28
    CPP Polska
    Home»C++»fopen, fread, fwrite – niskopoziomowy dostęp do plików w C/C++ z praktycznymi przykładami
    C++

    fopen, fread, fwrite – niskopoziomowy dostęp do plików w C/C++ z praktycznymi przykładami

    Oskar KlimkiewiczBy Oskar KlimkiewiczBrak komentarzy6 Mins Read
    Share Facebook Twitter LinkedIn Email Copy Link
    Follow Us
    RSS
    A group of people sitting in a room with a checkered floor
    Share
    Facebook Twitter LinkedIn Email Copy Link

    Niskopoziomowy dostęp do plików w języku C/C++ z wykorzystaniem funkcji fopen, fread i fwrite – kompleksowy przewodnik z praktycznymi przykładami

    W niniejszym artykule przedstawiono szczegółowe omówienie niskopoziomowych mechanizmów obsługi plików w języku C/C++ z wykorzystaniem kluczowych funkcji biblioteki standardowej. Skupiamy się na praktycznym zastosowaniu funkcji fopen(), fread() i fwrite(), które stanowią fundament operacji wejścia-wyjścia na plikach binarnych i tekstowych. Analiza obejmuje aspekty techniczne, praktyczne implementacje oraz kwestie wydajnościowe, poparte licznymi przykładami kodu.

    1. Wprowadzenie do obsługi plików w języku C

    Operacje na plikach w języku C realizowane są za pośrednictwem struktury FILE zdefiniowanej w nagłówku <stdio.h>. Dostęp do plików możliwy jest w dwóch trybach: tekstowym (automatyczna konwersja znaków) i binarnym (bezpośredni zapis/odczyt bajtów). Funkcja fopen() inicjuje strumień plikowy, zwracając wskaźnik do struktury FILE, który następnie wykorzystywany jest przez fread() i fwrite(). Podstawową zaletą niskopoziomowego dostępu jest możliwość operowania dużymi blokami danych bez pośrednich konwersji, co znacząco wpływa na wydajność.

    1.1 Kluczowe pojęcia i modele dostępu

    • Deskryptor pliku – identyfikator reprezentujący otwarty plik w systemie operacyjnym;
    • Buforowanie – automatyczne przechowywanie danych w pamięci przed zapisem na dysk, co redukuje liczbę operacji I/O;
    • Tryby operacji –
    • "r" (read) – otwiera do odczytu, plik musi istnieć,
    • "w" (write) – tworzy lub nadpisuje plik,
    • "a" (append) – dopisuje na końcu istniejącego pliku,
    • "b" (binary) – tryb binarny, np. "wb".

    2. Funkcja fopen() i fclose()

    FILE *fopen(const char *filename, const char *mode) odpowiada za poprawne otwarcie pliku. Parametr filename określa ścieżkę, a mode – tryb operacji. Funkcja zwraca NULL w przypadku błędu (np. brak pliku przy odczycie). Zawsze należy sprawdzać tę wartość:

    FILE *plik = fopen("dane.bin", "rb");
    if (plik == NULL) {
        perror("Błąd otwarcia pliku");
        return 1;
    }
    

    Zamknięcie pliku przy użyciu fclose(plik) jest obowiązkowe – zwalnia zasoby systemowe i wymusza zapis bufora. Zaniedbanie powoduje wycieki pamięci i potencjalną utratę danych.

    3. Funkcja fwrite() – zapis danych

    size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) zapisuje do strumienia nmemb elementów o rozmiarze size bajtów każdy, z obszaru pamięci ptr. Zwraca liczbę pomyślnie zapisanych elementów. Przykład zapisu tablicy liczb:

    int liczby[] = {10, 20, 30, 40, 50};
    size_t zapisane = fwrite(liczby, sizeof(int), 5, plik);
    if (zapisane != 5) {
        perror("Błąd zapisu");
    }
    

    W powyższym kodzie sizeof(int) określa rozmiar pojedynczego elementu (4 bajty), a 5 – liczbę elementów. Całkowity zapis: 20 bajtów.

    3.1 Zapis struktur

    Funkcja fwrite() szczególnie efektywna jest przy zapisie bloków danych, takich jak struktury:

    typedef struct {
        int id;
        char nazwa[32];
        float cena;
    } Produkt;
    
    Produkt p = {1, "Monitor", 799.99};
    fwrite(&p, sizeof(Produkt), 1, plik);
    

    W tej sytuacji cała struktura zapisywana jest jednym wywołaniem, co eliminuje narzut wielokrotnych wywołań funkcji.

    4. Funkcja fread() – odczyt danych

    size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) odczytuje nmemb elementów o rozmiarze size do bufora ptr. Zwraca rzeczywistą liczbę odczytanych elementów, która może być mniejsza od oczekiwanej z powodu końca pliku (EOF) lub błędu. Przykład odczytu struktury:

    Produkt odczytany;
    size_t odczytane = fread(&odczytany, sizeof(Produkt), 1, plik);
    if (odczytane != 1) {
        if (feof(plik))
            printf("Koniec pliku!");
        else if (ferror(plik))
            perror("Błąd odczytu");
    }
    

    Kluczowe jest sprawdzenie zwracanej wartości oraz użycie feof() i ferror() do diagnozowania przyczyn.

    4.1 Odczyt dużych zbiorów danych

    Do efektywnego przetwarzania dużych plików zaleca się odczyt blokowy:

    #define BUFFER_SIZE 1024
    char bufor[BUFFER_SIZE];
    size_t odczytane;
    while ((odczytane = fread(bufor, 1, BUFFER_SIZE, plik)) > 0) {
        przetwarzaj_dane(bufor, odczytane);
    }
    

    W tym przypadku fread() odczytuje do BUFFER_SIZE bajtów na iterację, minimalizując liczbę operacji systemowych.

    5. Obsługa błędów i dobre praktyki

    Spójność trybów – nie należy łączyć operacji binarnych (fread/fwrite) z formatowanymi (fprintf/fscanf), gdyż prowadzi to do nieprzewidywalnych zachowań;
    Weryfikacja operacji – każde wywołanie fread/fwrite musi być poprzedzone sprawdzeniem zwracanej wartości:

    if (fwrite(dane, sizeof(int), 100, plik) != 100) {
        // Obsługa częściowego zapisu
    }
    

    Pozycjonowanie – funkcja fseek(plik, offset, SEEK_SET) przemieszcza wskaźnik pliku, co umożliwia dostęp do dowolnych fragmentów bez konieczności sekwencyjnego odczytu.

    6. Praktyczne zastosowania

    6.1 Zapis i odczyt tablicy struktur

    Pełny przykład tworzenia bazy produktów:

    #include <stdio.h>
    
    typedef struct {
        int id;
        char nazwa[32];
        float cena;
    } Produkt;
    
    int main() {
        const int N = 3;
        Produkt produkty[N] = {
            {1, "Klawiatura", 150.0},
            {2, "Mysz", 80.5},
            {3, "Słuchawki", 200.0}
        };
    
        // Zapis do pliku
        FILE *zapis = fopen("produkty.bin", "wb");
        fwrite(produkty, sizeof(Produkt), N, zapis);
        fclose(zapis);
    
        // Odczyt z pliku
        Produkt odczyt[N];
        FILE *odczyt_plik = fopen("produkty.bin", "rb");
        fread(odczyt, sizeof(Produkt), N, odczyt_plik);
        fclose(odczyt_plik);
    
        for (int i = 0; i < N; i++) {
            printf("ID: %d, Nazwa: %s, Cena: %.2f\n", odczyt[i].id, odczyt[i].nazwa, odczyt[i].cena);
        }
        return 0;
    }
    

    Ten kod demonstruje kompleksowy przepływ pracy: zapis całej tablicy struktur jednym wywołaniem fwrite(), a następnie jej odczyt do nowej tablicy.

    6.2 Kopiowanie plików binarnych

    Efektywny mechanizm kopiowania dużych plików:

    #include <stdio.h>
    
    int main() {
        FILE *zrodlo = fopen("duzy_plik.iso", "rb");
        FILE *cel = fopen("kopia.iso", "wb");
        if (!zrodlo || !cel) {
            /* Obsługa błędów */
        }
        unsigned char bufor[4096];
        size_t odczytane;
        while ((odczytane = fread(bufor, 1, sizeof(bufor), zrodlo)) > 0) {
            size_t zapisane = fwrite(bufor, 1, odczytane, cel);
            if (zapisane != odczytane) {
                /* Błąd zapisu */
            }
        }
        fclose(zrodlo);
        fclose(cel);
        return 0;
    }
    

    Wybór rozmiaru bufora (np. 4 KB) optymalizuje wykorzystanie pamięci podręcznej dysku.

    7. Porównanie z innymi funkcjami I/O

    Funkcja Zastosowanie Wydajność
    fread()/fwrite() Operacje blokowe na danych binarnych Wysoka
    fgetc()/fputc() Przetwarzanie bajt po bajcie Niska
    fgets()/fputs() Operacje na liniach tekstu Średnia
    fscanf()/fprintf() Formatowany zapis/odczyt Najniższa
    Główną zaletą fread/fwrite jest minimalny narzut przetwarzania – dane kopiowane są bezpośrednio między pamięcią a plikiem, bez konwersji formatów.

    8. Zagadnienia zaawansowane

    8.1 Buforowanie strumieni

    Domyślnie operacje plikowe są buforowane przez system. Wyłączenie buforowania:

    setvbuf(plik, NULL, _IONBF, 0); // Tryb bez buforowania
    

    Tryb _IONBF (no buffering) wymusza natychmiastowy zapis, co jest kosztowne, ale niezbędne w systemach czasu rzeczywistego.

    8.2 Bezpośredni dostęp (fseek)

    Funkcja fseek(plik, offset, origin) zmienia pozycję wskaźnika pliku:

    • SEEK_SET – początek pliku,
    • SEEK_CUR – bieżąca pozycja,
    • SEEK_END – koniec pliku
      przykład odczytu ostatniego rekordu.
    fseek(plik, -sizeof(Produkt), SEEK_END);
    fread(&produkt, sizeof(Produkt), 1, plik);
    

    To podejście jest kluczowe w systemach bazodanowych.

    9. Optymalizacja wydajności

    • Rozmiar bloku – operacje na dużych blokach (np. 4–64 KB) redukują liczbę wywołań systemowych;
    • Wyrównanie danych – struktury powinny być wyrównane do granic słów maszynowych (#pragma pack(1) w Windows);
    • Buforowanie w pamięci – dla skrajnej wydajności, dane krytyczne przechowywane w RAM z okresowym snapshotem na dysk.
      Testy porównawcze wykazują nawet 10-krotny wzrost prędkości przy użyciu fread()/fwrite() zamiast funkcji bajtowych dla plików >100 MB.

    10. Podsumowanie

    Funkcje fopen(), fread() i fwrite() stanowią trzon niskopoziomowej obsługi plików w C/C++. Ich prawidłowe stosowanie wymaga:

    1. Rygorystycznej kontroli błędów po każdym wywołaniu;
    2. Ścisłego zarządzania zasobami (zawsze zamykać pliki);
    3. Świadomości trybów dostępu (binarny vs tekstowy);
    4. Doboru rozmiaru bufora do charakterystyki danych.

    Kluczowe zalety to: wydajność, bezpośredni dostęp do danych binarnych oraz atomowość operacji na blokach. Ograniczenia obejmują brak wsparcia dla operacji asynchronicznych oraz konieczność ręcznego zarządzania buforami w aplikacjach wielowątkowych.

    Praktyczne implementacje (bazy danych, systemy plików w pamięci flash, transmisja sieciowa) potwierdzają przewagę tych mechanizmów nad funkcjami wysokopoziomowymi w scenariuszach wymagających maksymalnej przepustowości I/O.

    Polecane:

    • Wczytywanie danych z pliku operatorem >> – obsługa strumieni, formatowanie i błędy I/O
    • EOF w C/C++ – poprawna detekcja końca pliku i bezpieczne pętle wczytywania
    • fstream w C++ – czytanie i zapis plików tekstowych oraz binarnych z obsługą błędów
    • calloc, realloc i free – zarządzanie pamięcią w stylu C w nowoczesnym projekcie C++
    • Makefile od podstaw – składnia, najczęstsze pułapki, automatyzacja i przyspieszanie budowania
    Share. Facebook Twitter LinkedIn Email Copy Link
    Oskar Klimkiewicz
    • Website

    Inżynier oprogramowania specjalizujący się w C++, absolwent Wydziału Elektroniki i Technik Informacyjnych Politechniki Warszawskiej. Od ponad 8 lat projektuje i rozwija systemy o wysokiej dostępności, głównie dla branży fintech i IoT. PS. Zdjęcie wyretuszowane przez AI :)

    Podobne artykuły

    Maszyna stanów oparta o std::variant

    8 Mins Read

    Tablice w C++ od podstaw – deklaracja, inicjalizacja, iteracja i typowe pułapki

    4 Mins Read

    std::deque w C++ – kiedy wybrać dwukierunkową kolejkę zamiast vectora

    4 Mins Read
    Leave A Reply Cancel Reply

    Oglądaj, słuchaj, ćwicz - zdobywaj nowe umiejętności online
    Nie przegap

    Jak podłączyć telefon do monitora? Przewodowe i bezprzewodowe sposoby

    Oskar Klimkiewicz6 Mins Read

    Podłączenie telefonu do monitora to jedna z najistotniejszych innowacji ery mobilnej, umożliwiająca przeniesienie doświadczeń z…

    Co można wrzucić w koszty firmy jednoosobowej? Lista i praktyczne przykłady

    2025-12-03

    Jak podłączyć okulary VR do PS4? Poradnik podłączenia i konfiguracji

    2025-12-02

    Jak zapobiec wyciekom danych firmowych?

    2025-11-28
    Social media
    • Facebook
    • Twitter
    • LinkedIn
    O nas
    O nas

    CPP Polska to serwis internetowy poświęcony technologii, programowaniu, IT, biznesowi i finansom. Znajdziesz tu porady, wskazówki i instrukcje dla wszystkich czytelników IT & Tech & Biz.

    Facebook X (Twitter) LinkedIn RSS
    Najnowsze

    Jak podłączyć telefon do monitora? Przewodowe i bezprzewodowe sposoby

    2025-12-08

    Co można wrzucić w koszty firmy jednoosobowej? Lista i praktyczne przykłady

    2025-12-03

    Jak podłączyć okulary VR do PS4? Poradnik podłączenia i konfiguracji

    2025-12-02
    Popularne

    Skrajnie niepotrzebne, skrajne przypadki w C++

    2025-06-28

    Wyszukiwanie testów w Google Test – metody i narzędzia

    2025-06-28

    Czy C jest wolniejszy od C++? Zero-cost abstraction w praktyce

    2025-06-28
    © 2025 CPP Polska. Wszelkie prawa zastrzeżone.
    • Lista publikacji
    • Współpraca
    • Kontakt

    Type above and press Enter to search. Press Esc to cancel.