Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: Piotr Szawdyński
Kurs C++

Poruszanie się po pliku w trybie do odczytu

[lekcja] Rozdział 35. Rozdział opisuje, jak poruszać się po pliku w trybie do odczytu.

Wprowadzenie

W niniejszym rozdziale dowiesz się w jaki sposób można odczytywać wybrane fragmenty pliku bez konieczności wczytywania jego całej zawartości. Zanim jednak to nastąpi, warto najpierw poświęcić trochę czasu na teorię, a konkretniej na to, jak są przechowywane informacje w komputerze.

Logiczna budowa informacji przechowywanych w komputerze

Wszystko jest bajtem

W świecie informatyki i sprzętu komputerowego wszystko kręci się wokół jednostki bajta. Za jej pomocą wyraża się rozmiar dysków twardych, rozmiar pamięci RAM, architekturę procesorów oraz wiele innych parametrów sprzętu komputerowego. Przyjęło się, że bajt jest najmniejszą logiczną paczką informacji na której pracuje komputer, zatem wszelkie niskopoziomowe narzędzia są przystosowane do pracy z jednym bajtem danych. Odczytując więc jakiekolwiek dane z dysku twardego/pamięci RAM czy też jakiegokolwiek innego nośnika informacji, zawsze będziesz musiał odczytać jeden bajt, nawet jeśli interesuje Ciebie tylko jeden bit do niego należący.

Dysk twardy, a jego logiczna struktura

Dysk twardy, płytę DVD czy pendrive-a możesz wyobrazić sobie jako jedną wielką jednowymiarową tablicę bajtów. W tej jednowymiarowej tablicy porozrzucane są dane plików, ich nazwy, katalogi i inne bajery, które są niezbędne do poprawnego odczytu informacji zapisanych na nośniku. Sporą część zajmują również bajty w których nie ma zapisanych żadnych użytecznych informacji i w każdej chwili możemy je wykorzystać na to, aby zapisać tam nowe informacje np. nowy plik na dysku. Praca na takiej jednowymiarowej tablicy z danymi byłaby jednak dla nas niewygodna, dlatego też system operacyjny zajmuje się zarządzaniem tej pamięci, dając nam w zamian ładną logiczną strukturę danych w postaci katalogów i plików. System operacyjny dostarcza więc nam cały zbiór narzędzi do wygodnej pracy z danymi zapisanymi na dysku.

Plik, a jego logiczna struktura

Jak już wiesz, dysk twardy możemy traktować w praktyce jako jednowymiarową tablicę. W niej znajdują się pliki, a dane jednego pliku mogą być (i zazwyczaj są) porozrzucane w różnych miejscach na dysku. Aby dało się jednak wygodnie pracować z plikiem, system operacyjny dostarcza nam narzędzia umożliwiające dostęp do pliku tak, jakby każdy plik był jedną i jednocześnie ciągłą tablicą jednowymiarową. Co więcej, tą jednowymiarową 'tablicę' pliku możemy sobie powiększać do woli, bądź zmniejszać w zależności od tego ile miejsca potrzebujemy na przechowanie interesujących nas informacji. System operacyjny oczywiście odmówi powiększenia pliku, jeżeli nie będzie miał już więcej wolnego miejsca na dysku, no ale nie o to w tym całym wywodzie chodzi :)

Pozycja w pliku

Jak już wcześniej się dowiedziałeś, każdy plik możemy traktować jak jednowymiarową tablicę z danymi. Dostęp do tej tablicy umożliwiają jak zwykle narzędzia dostarczone przez system operacyjny. Wspomniane narzędzia zostały stworzone tak, aby umożliwiały wygodną pracę z plikiem. Wymyślono więc, że każdy bajt pliku zostanie ponumerowany, a numeracja bajtów w każdym pliku będzie się zaczynała od zera. Pozycja w pliku określa więc numer bajta do którego chcemy się 'dostać' w celu odczytania bądź zapisania nowej informacji.

Podsumowanie teorii

Z kilku powyższych paragrafów powinieneś dowiedzieć się jak mniej więcej zorganizowane są dane na dysku twardym czy też płycie DVD. Pamiętaj, że przedstawiony powyżej opis jest bardzo ogólny oraz jest bardzo uproszczony w stosunku do rzeczywistości. Ważne jest to, abyś miał jakieś wyobrażenie jak są zorganizowane dane na dysku. Ważne jest również, abyś wiedział czym jest pozycja w pliku, bowiem reszta niniejszego rozdziału będzie koncentrowała się właśnie wokół tego terminu.

Określanie pozycji odczytu danych z pliku

Jak już wiesz do pracy z plikiem w trybie do odczytu wykorzystujemy klasę std::ifstream. W klasie tej znajdują się dwie metody o nazwie seekg, które służą do ustawiania pozycji od której chcesz wczytywać dane z pliku. Pierwsza wersja metody seekg przyjmuje jeden argument za pomocą którego określamy pozycję od której chcesz odczytywać dane z pliku. Pozycja ta musi być wyrażana względem początku pliku. W praktyce oznacza to, że wskazujesz po prostu numer znaku od którego chcesz rozpocząć czytanie pliku. Przykład:
C/C++
std::ifstream plik;
//(...) otwierasz plik i weryfikujesz czy otwarcie się powiodło
plik.seekg( 10 ); //Ustawiasz pozycję odczytu danych od 11 znaku (pamiętaj, pozycje numeruje się od zera)
//(...) wykonujesz operację odczytu danych z pliku
plik.seekg( 1 ); //Ustawiasz pozycję odczytu danych od drugiego  znaku.
//(...) wykonujesz operację odczytu danych z pliku
plik.seekg( 0 ); //Ustawiasz pozycję odczytu danych od pierwszego znaku.
//(...) wykonujesz operację odczytu danych z pliku
Druga wersja metody seekg posiada dwa argumenty. Pierwszym argumentem określa się 'o ile chcesz przesunąć pozycję odczytu', natomiast drugi argument określa 'względem jakiej pozycji chcesz przesunąć pozycję odczytu'. Argument pierwszy jest więc liczbą całkowitą (może być to wartość ujemna) , natomiast drugi argument może przyjąć jedną z trzech wartości:
  • std::ios::beg
     – przesunięcie pozycji  jest wyrażone względem początku pliku.
  • std::ios::cur
     – przesunięcie pozycji jest wyrażane względem aktualnej pozycji.
  • std::ios::end
     – przesunięcie pozycji jest wyrażone względem końca pliku.
Przesunięcie pozycji do odczytu na koniec pliku będzie więc wymagało napisania:
C/C++
plik.seekg( 0, std::ios::end );
Przesunięcie pozycji odczytu danych o 3 znaki w lewo względem aktualnej pozycji w pliku będzie wymagało z kolei zastosowania zapisu:
C/C++
plik.seekg( - 3, std::ios::cur );
Określenie pozycji odczytu względem początku pliku można zrobić na dwa sposoby:
 
C/C++
plik.seekg( 5, ios::beg ); //Ustawiasz pozycję odczytu danych od 6  znaku.
//Poniższy zapis jest równoważny powyższemu:
plik.seekg( 5 ); //Ustawiasz pozycję odczytu danych od 6  znaku.

Pobieranie aktualnej pozycji odczytu danych z pliku

Utworzony obiekt typu std::ifstream, który wykorzystujemy do pracy z plikiem umożliwia również odczytywanie aktualnej pozycji odczytu danych z pliku. Do tego celu służy metoda tellg, która nie przyjmuje żadnego argumentu, ale za to zwraca wartość, która określa aktualną pozycję odczytu danych z pliku. Zwracana pozycja jest wyrażona zawsze względem początku pliku. Przykład:
C/C++
std::streampos iOdczytanaPozycja = plik.tellg();
std::cout << "Aktualna pozycja odczytu danych z  pliku: " << iOdczytanaPozycja << std::endl;
Warto również w tym miejscu wspomnieć, że metodę tellg można wykorzystać przy określaniu pozycji odczytu danych z pliku:
C/C++
plik.seekg( 8, ios::cur );
//Poniższy zapis jest równoważny do powyższego:
plik.seekg( plik.tellg() + 8 );
 

Przykład

C/C++
#include <iostream>
#include <fstream>

int main()
{
    std::ifstream plik( "dane.txt" );
    if( !plik.good() )
         return 0;
   
    std::streampos iPozycjaStartowa = 4;
    plik.seekg( iPozycjaStartowa );
   
    int iLiczba;
    plik >> iLiczba;
   
    int iWczytanychZnakow = plik.tellg() - iPozycjaStartowa;
    std::cout << "Liczba: " << iLiczba << std::endl;
    std::cout << "Wczytano " << iWczytanychZnakow << " znakow" << std::endl;
    return 0;
}

Podsumowanie

Po przeczytaniu niniejszego rozdziału powinieneś już wiedzieć w jaki sposób poruszać się po pliku w trybie do odczytu. Wiedz jednak, że dostęp do danych przechowywanych na dysku jest kosztowny czasowo i dla małych plików z danymi znacznie lepiej i wygodniej wczytać całe dane do pamięci, unikając tym samym zbędnego poruszania się po pliku. Korzyści z poruszania się po pliku zaczynają być zauważalne dopiero przy wyszukiwaniu danych w dużych i dobrze zorganizowanych zbiorach danych, takich jak np. wszelkiego rodzaju bazy danych ze ściśle określoną strukturą danych. Generalnie rzecz biorąc, pracując z plikami należy dążyć do minimalizacji liczby odczytów dyskowych, starając się jednocześnie odczytywać duże bloki pamięci jednym wywołaniem metody odczytującej dane. Traktuj tą informację na chwilę obecną jako ciekawostkę, a aktualnie staraj się pisać przede wszystkim prosty i krótki kod.

Zadanie domowe

  • Napisz program, który odczytuje co trzeci znak z pliku. Użyj do tego metod poznanych w niniejszym rozdziale. Przetestuj poprawność działania programu na bardzo krótkim pliku tekstowym, wypisując co trzeci odczytany znak na ekranie.
  • Przetestuj szybkość działania napisanego wcześniej programu na dowolnym pliku, który zajmuje co najmniej kilka MB. Wykomentuj wiersz odpowiedzialny za wypisywanie odczytanego znaku na ekranie.
Poprzedni dokument Następny dokument
Wczytywanie zawartości pliku, a kontrola błędów Poziom 5