Rozdział IV Interakcja z otoczeniem (we/wy, bd) zapisywanie i odczytywanie wyników

/Paweł Bensel/

Większość aplikacji do działania potrzebuje sposobu komunikacji, dzięki któremu użytkownik będzie miał wpływ na jej działanie - czy to poprzez wprowadzenie danych do przetworzenia, poruszania określonym elementem na ekranie, wybranie określonego elementu na ekranie, czy chociażby wczytania konfiguracji potrzebnej do uruchomienia programu.

W zależności od budowanej aplikacji komunikacja aplikacji z otoczeniem prowadzona jest przy użyciu różnych kanałów. Mogą to być operacje wprowadzania danych z klawiatury, wczytywania ich z pliku czy z odczyt z bazy danych. W nowoczesnych aplikacjach mobilnych często polecenia do aplikacji wydawane są poprzez klikanie określonych elementów wyświetlanych na ekranie. Wprowadzone dane czy też wybranie określonych elementów mogą mieć wpływ na działanie aplikacji i powodować różną reakcje aplikacji. Operacje pobierania danych i ich przekazywania do użytkownika nazywa się operacjami wejścia wyjścia.

IV.1. Operacje Wejścia - wyjścia

Większość aplikacji musi pobierać dane do przetworzenia – mogą to być dane pobierane np. z klawiatury, graficznego interfejsu użytkownika, plików czy bazy danych. Przetworzone dane mogą być przekazywane wyświetlane na ekranie (tzw. urządzenia wyjścia), zapisane do pliku czy przekazane do bazy danych.

W zależności od języka programowania funkcje związane z odczytywaniem i zapisywaniem danych dostępne mogą być w zewnętrznych bibliotekach.

IV.1.1 Operacje wejścia wyjścia dla klawiatury i monitora (konsoli)

Najczęściej operacje wejścia-wyjścia zapisują dane do zmiennych, aby wykorzystać je w późniejszym etapie.

Przykładowa operacja wczytywania danych z klawiatury i ich wyświetlenia na monitorze wygląda w sposób następujący: VAR ZMIENNA: INTEGER; //DEKLARACJA ZMIENNEJ LICZBOWEJ /*UWAGA OD AUTORA: W ZALEŻNOŚCI OD UŻYWANEGO JĘZYKA PROGRAMOWANIA WCZEŚNIEJSZA DEKLARACJA ZMIENNYCH MOŻE NIE BYĆ WYMAGANA. */ ZMIENNA=READ(); //PRZYPISANIE DO ZMIENNEJ WARTOŚCI ZACZYTANA Z KLAWIATURY PRINT(ZMIENNA); //WYPISANIE WARTOŚCI ZMIENNEJ FUNKCJE WYPISUJĄCE DANE NA EKRAN CZĘSTO POZWALAJĄ ŁĄCZYĆ WIELE ZMIENNYCH, WYŚWIETLAĆ WYNIKI OPERACJI NA ZMIENNYCH CZY ŁĄCZYĆ JE Z DOWOLNYMI ŁAŃCUCHAMI ZNAKÓW, NP.: VAR LICZBA1, LICZBA2: INTEGER; //DEKLARACJA ZMIENNEJ LICZBOWEJ PRINT(‘TEN PROGRAM OBLICZA SUMĘ DWÓCH LICZB’); PRINT(‘PODAJ PIERWSZĄ LICZBĘ: ’); LICZBA1=READ(); PRINT(‘PODAJ DRUGĄ LICZBĘ: ’); LICZBA2=READ(); PRINT(‘SUMA PODANYCH LICZB WYNOSI’,LICZBA1+LICZBA2); /*UWAGA OD AUTORA: W ZALEŻNOŚCI OD UŻYWANEGO JĘZYKA PROGRAMOWANIA PARAMETRY FUNKCJI WYPISUJĄCEJ ZMIENNE MOGĄ BYĆ RÓŻNE. */

IV.1.2 Operacje na plikach

Większość języków programowania pozwala na operacje odczytu i zapisu danych do plików. Dane zapisywane do pliku mogą być danymi tekstowymi, ale również mogą to być pliki binarne w określonym formacie np. pliki graficzne czy pliki dźwiękowe.

Po wczytaniu danych mogą być one przetworzone w kodzie programu i ponownie zapisane do pliku. Najczęściej operacja dostępu do pliku wymaga jego otworzenia przy pomocy odpowiedniej funkcji języka programowania.

Podczas otwarcia pliku – jako parametr funkcji otwierającej plik - podajemy tryb dostępu do pliku – najczęściej spotykane tryby to:

* tryb do odczytu (read) – oznaczany jako R

* tryb do zapisu (write) – oznaczany jako W

* tryb do zapisu i odczytu (read/write)– oznaczany jako RW

* tryb dopisywania do pliku (append)- oznaczany jako A

Kolejnym krokiem jest operacja odczytu lub zapisu do pliku, po której należy zamknąć plik – spowoduje on odłączenie (zwollnienie) pliku, dzięki czemu będzie mógł być wykorzystywany przez inne programy działające w systemie operacyjnym.

IV.1.3 Operacje na plikach tekstowych

W zależności od języka programowania operacje czytania z plików tekstowych mogą dotyczyć:

* odczytu całego pliku

* odczytu kolejnej linii z pliku

* odczyt kolejnego znaku z pliku

* odczyt określonej liczby bajtów z pliku

Niektóre języki programowania w celu ułatwienia sekwencyjnego przetwarzania plików oferują funkcję bądź zmienną oznaczającą znacznik końca przetwarzanego pliku (ang. END OF FILE, skrót EOF), dzięki czemu w kodzie można używać pętli do odczytu danych.

Ogólny kod źródłowy odczytu z pliku tekstowego plik.txt zapisanego w katalogu /home/user/wyglądać będzie następująco:

EOF=FALSE; //PRZYPISANIE WARTOŚCI DO ZMIENNEJ TYPU LOGICZNEGO WYKORZYSTYWANEJ DO SPRAWDZANIA KOŃCA PLIKU PLIK = OPEN(‘/HOME/USER/PLIK.TXT’,’R’); WHILE (NOT EOF) { LINIA_TEKSTU =READ(PLIK); //PRZYPISANIE DO ZMIENNEJ WARTOŚCI ZCZYTANEJ Z KLAWIATURY IF (LINIA_TEKSTU==’’) EOF=TRUE; //WCZYTANO PUSTĄ LINIĘ, CO OZNACZA KONIEC PLIKU ELSE PRINT(LINIA_TEKSTU); //WYDRUK LINII NA EKRANIE } CLOSE (PLIK);

IV.1.4 Operacje na plikach o zdefiniowanych formatach

Wiele języków programowania oferuje biblioteki zawierające funkcje obsługi popularnych formatów plików takich jak pliki graficzne, pliki edytorów tekstów czy pliki PDF. Dostęp do tych plików oraz oferowanych funkcji zależny jest od konkretnej biblioteki. W przypadku plików graficznych funkcje te mogą dotyczyć np.

* zmiany rozdzielczości obrazu

* zmiany rozmiarów obrazu

* wycięcia fragmentu obrazu

* dodanie znaków wodnych lub innych efektów

* wstawianie tekstu na obraz

Przykładowy kod źródłowy programu do zmiany rozmiaru obrazu zapisanego w katalogu /home/user w pliku zdjecie.jpg i zapisanie go pod nazwą miniaturka.jpg:

IMPORT IMAGELIBRARY; //DOŁĄCZENIE BIBLIOTEKI OBSŁUGI PLIKÓW GRAFICZNYCH IMAGELIBRARY PLIKGRAFICZNY = NEW IMAGE; //UTWORZENIE OBIEKTU Z KLASY IMAGE ZDEFINIOWANEJ W BIBLIOTECE PLIKGRAFICZNY.OPEN(‘/HOME/USER/ZDJECIE.JPG’); //OTWARCIE OBRAZ ZAPISANEGO W PLIKU PLIKGRAFICZNY.RESIZE(320,240); //ZMIANA ROZMIARU OBRAZU PLIKGRAFICZNY.SAVE(‘/HOME/USER/MINIATURKA.JPG’); //ZAPIS OBRAZU DO PLIKU PLIKGRAFICZNY.DESTROY(); //USUNIĘCIE Z PAMIĘCI OBIEKTU PLIKGRAFICZNY

IV.2 Zapis i odczyt danych z baz danych

Większość współczesnych aplikacji przetwarzających dane korzysta z serwerów baz danych - jest to usługa (program) działająca na dowolnym serwerze (którym może być również dowolny komputer w tym również komputer programisty). Serwery baz danych pozwalają na zapis, odczyt i manipulację danymi w określony – standardowy sposób. Ze względu na standaryzację dostępu do danych, możliwości zabezpieczenia danych, możliwości dostępu przez wielu użytkowników serwery baz danych są wygodnym narzędziem to zapisywania danych.

Do najbardziej popularnych serwerów baz danych należą:

  • MySql
  • PostrgeSQL
  • MS Sql
  • SQLite

Dostęp do danych zapisanych w bazach danych umożliwia język SQL (ang. Structured Query Language) – strukturalny język zapytań.

Najczęściej wykonywane operacje dotyczące danych to:

* Tworzenie danych i struktur (ang. create)

* Odczyt danych (ang. read)

* Aktualizacja danych (ang. update)

* Usuwanie – oznacza operacje usuwania danych

IV.2.1 Krótka teoria dotycząca baz danych

W systemach baz danych dane przechowywane są w tabelach o określonej strukturze. Chcąc zapisać w tabeli dane pracowników należy stworzyć tabelę zawierającą wymagane pola o określonym typie, np.:

* Imię – pole tekstowe o długości 100 znaków

* Nazwisko – pole tekstowe o długości 100 znaków

* Numer PESEL – pole tekstowe o długości 11 znaków

* Data urodzenia – pole typu data

* Wynagrodzenie – pole typu liczba zmiennoprzecinkowa

* Liczba dzieci – pole typu liczba całkowita

Przy zapisie danych do bazy warto utworzyć dodatkowe pole pozwalające jednoznacznie zidentyfikować poszczególny wiersz (rekord) w tabeli. Takie pole najczęściej jest polem typu liczba całkowita, którego wartość przypisywana jest automatycznie przez serwer bazy danych (w bazie MySql pole autoincrement). Pole, które jednoznacznie określa rekord w tabeli nazywane jest kluczem głównym (ang. Primary Key), jest ono używane podczas operacji odczytu i zapisu pojedynczych rekordów w bazie.

Instrukcja utworzenia tabeli danych osobowych pracowników w języku SQL (składnia MySql) wygląda następująco:

Create table Pracownicy (id int not null autoincrement, imię varchar(100), nazwisko varchar(100), pesel varchar(11), data_urodzenia date, wynagrodzenie decimal(10,2), liczba_dzieci int, primary key (id));

Aby wstawić dane do tabeli w bazie SQL korzystamy z instrukcji INSERT:

Insert into pracownicy (imie, nazwisko, pesel,data_urodzenia,wynagrodzenie,liczba_dzieci) values (‘Jan’,’Kowalski’,’79010100234’,’1979-01-01’,1000.00,0); Insert into pracownicy (imie, nazwisko, pesel,data_urodzenia,wynagrodzenie,liczba_dzieci) values (‘Anna’,’Nowak’,’79020300234’,’1979-02-03’,2000.00,2 );

Po wykonaniu powyższych instrukcji w bazie danych zostaną dodane dwa rekordy o podanych wartościach oraz automatycznych przypisanych dla pola ID:

Odczyt danych z tabel umożliwia komenda SELECT. Poniżej prezentowane jest kilka przykładów dotyczących wyświetlania poszczególnych rekordów:

SELECT * FROM pracownicy; Zwróci wszystkie pola tabeli pracownicy SELECT imie, nazwisko FROM pracownicy ORDER BY nazwisko, imie; Zwróci pola imię oraz nazwisko z tabeli pracownicy posortowane w kolejności alfabetycznej wg nazwiska oraz imienia SELECT * FROM pracownicy WHERE id=2; Zwróci wszystkie pola z rekordu, dla którego wartość pola ID (klucz główny) wynosi 2 z tabeli pracownicy. SELECT * FROM pracownicy WHERE data_urodzenia>’1979-01-31’; Zwróci wszystkie rekordy z tabeli pracownicy, dla którego wartość pola data urodzenia jest większa niż ‘1979-01-31’ – czyli zwróci rekordy dotyczące osób urodzonych po 31 stycznia 1979.

Aby zmienić wartości poszczególnych pól w tabelach należy użyć komendy UPDATE. Przykładowa składnia polecenia przedstawiona jest poniżej:

UPDATE pracownicy SET wynagrodzenie=1500.00;

Polecenie przypisze dla wszystkich wierszy w tabeli pracownicy wynagrodzenie w wysokości 1500.

UPDATE pracownicy SET wynagrodzenie=wynagrodzenie*1.2 where liczba_dzieci >=1;

Polecenie zwiększy wynagrodzenie o 20% dla wszystkich wierszy w tabeli pracownicy wynagrodzenie w wysokości 1500 dla osób posiadających więcej niż jedno dziecko.

UPDATE pracownicy SET liczba_dzieci=liczba_dzieci+1 where id=1;

Polecenie zwiększy wartość pola liczba_dzieci o jeden dla wiersza oznaczonego identyfikatorem (kluczem głównym) 1.

Za usuwanie danych w bazach danych odpowiada polecenie DELETE.

DELETE from pracownicy;

Usuwa wszystkie rekordy z tabeli pracownicy.

DELETE from pracownicy where nazwisko =’Kowalski’;

Usuwa pracownika o nazwisku Kowalski

IV.2.2 Komunikacja z bazą danych

W celu bezpieczeństwa danych przechowywanych w bazach danych dostęp do nich wymaga autoryzacji przy użyciu nazwy użytkownika oraz hasła – dane te konfigurowane są przez administratora bazy danych.

Do podłączenia do serwera bazy danych wymagane są następujące parametry:

* Nazwa serwera baz danych

* Port, na którym działa serwer baz danych

* Nazwa bazy danych

* Nazwa użytkownika

* Hasło użytkownika

Najczęściej w językach programowania mechanizmy komunikacji z bazami danych dostępne są w zewnętrznych bibliotekach.

Niezależnie od języka programowania oraz używanych baz danych komunikacja z serwerem bazodanowym przebiega podobnie:

Import DBLibrary; //dołączenie biblioteki obsługi bazy danych DBLibrary baza = new DBClass; //utworzenie obiektu z klasy DBClass zdefiniowanej w bibliotece baza.connect(‘localhost’,3306,’dbname’,’user’,’password123’); //otwarcie połączenia do bazy danych dbname działającej na serwerze lokalnym (localhost) na porcie 3306 jako użytkownik user z hasłem password123 baza.query(‘SELECT * FROM pracownicy order by nazwisko, imie’); // zapytanie SELECT … result=baza.fetchAll(); //pobranie wyniki wykonanego zapytania do zmiennej tablicowej result; foreach (result as row) //pętla wykonywana dla wszystkich wierszy zmiennej result oznaczanych w kolejnych iteracjach jako zmienna row { print(row[imie], row[nazwisko]); //wydruk na ekranie pól nazwisko oraz imie kolejnych wierszy zmiennej result } baza.disconnect(); //zakończenie połączenia z bazą danych baza.destroy();//usunięcie obiektu z pamięci

Podobny mechanizm komunikacji z serwerem baz danych ma zastosowanie w przypadku innego rodzaju zapytań np, dodających czy aktualizujące dane – wówczas zmianie ulega treść zapytania w języku SQL wysyłana do serwera bazy danych oraz analiza odpowiedzi.

IV.2.3 Bazy danych noSQL

Wraz z rozwojem internetu i nowych technologii zmieniło się również podejście do przechowywania danych. Dane zapisane w tabelach, kolejne zaindeksowane wiersze w tabeli nie zawsze są wygodne przy przechowywaniu skomplikowanych i nieregularnych struktur danych.

Serwery baz danych noSQL zapewniają mechanizm przechowywania i dostępu do danych, które nie są przechowywane w regularnych tabelach, na których manipulacja jest bardziej intuicyjna i nie wymaga znajomości języka SQL. Dodatkowym atutem jest brak utrudnień związanych ze zmianami w strukturze danych dzięki czemu często serwery baz danych noSQL wykorzystywane są przy przetwarzaniu dużych ilości danych (ang. Big data).

Do najbardziej popularnych serwerów baz danych noSQL należą:

MongoDB

Apache Cassandra

Redis

Solr

W przeciwieństwie do danych zapisanych w tabelach SQL dane w bazach noSQL nie posiadają określonej struktury – poszczególne rekordy w bazie noSQL (odpowiadające wierszom) zapisywane są w tzw. Kolekcji mogą różnić się kolejnymi atrybutami (odpowiadającymi kolumnom w tabeli SQL).

Spróbujmy przeanalizować przykład zapisu i odczytu danych dotyczący pracownika w bazach noSQL (na przykładzie MongoDB).

Jak powiedziano wcześniej bazy noSQL nie wymagają wcześniejszej definicji struktury danych, wystarczy jedynie deklaracja kolekcji, do której zapisywane będą dane o dowolnie zdefiniowanych polach.

db.createCollection("pracownik"); Polecenie to stworzy w bazie danych kolekcję o nazwie pracownik. Aby dodać dane należy użyć komendy insert, której parametrami są nazwy pól i ich wartości. db.pracownik.insert({"imie":"Anna","nazwisko":"Nowak","pesel":"’79020300234","data_urodzenia":"1979-02-03","wynagrodzenie ":2000.00, "liczba_dzieci":0});

Kolejny wpis do bazy może różnić się definicją danych – nie jest wymagane podanie wartości poszczególnych pól, które zostały zdefiniowane wcześniej, mogą być wstawiane zupełnie inne dane.

db.pracownik.insert({"imie":"Jan","nazwisko":"Kowalski","pesel":"’79010100234","data_urodzenia":"1979-01-01","wynagrodzenie ":1000.00, "numer_telefonu":"501 501 501"});

Po dodaniu danych serwer bazy danych automatycznie nada identyfikator wprowadzonemu rekordowi – będzie on zapisany w polu _id. Automatyczne indeksowanie kolejnych rekordów może być wyłączone na etapie tworzenia kolekcji.

db.pracownik.find()

Zwróci wszystkie pola tabeli pracownicy

db.pracownik.find().sort({"nazwisko":1, "imie":1})

Zwróci dane pracowników posortowane w kolejności alfabetycznej wg nazwiska oraz imienia

db.pracownik.find({_id:2})

Zwróci wszystkie pola z rekordu, dla którego wartość pola _id (klucz główny) wynosi 2 z tabeli pracownicy.

SELECT * FROM pracownicy WHERE data_urodzenia>’1979-01-31’; db.pracownik.find({ data_urodzenia: { $gt : 1979-01-31 } })

Zwróci wszystkie rekordy pracowników, dla których wartość pola data urodzenia jest większa niż ‘1979-01-31’ – czyli dane dotyczące osób urodzonych po 31 stycznia 1979. W celu porównywania danych używane są następujące operatory:

Aby zmienić wartości poszczególnych pól w tabelach należy użyć komendy UPDATE. Przykładowa składnia polecenia przedstawiona jest poniżej:

db.pracownik.update({},{ $set :{ "wynagrodzenie":1500} })

Polecenie przypisze dla wszystkich pracowników wynagrodzenie w wysokości 1500.

UPDATE pracownicy SET liczba_dzieci=liczba_dzieci+1 where id=1; db.pracownik.update({"_id":1},{ $inc :{ "liczba_dzieci":1} })

Polecenie zwiększy wartość pola liczba_dzieci o jeden dla wpisu oznaczonego identyfikatorem (kluczem głównym) 1.

Za usuwanie danych w odpowiada polecenie REMOVE.

db.pracownik.remove({})

Usuwa wszystkie rekordy z tabeli pracownicy.

db.pracownik.remove({"nazwisko":"Kowalski"})

Usuwa pracownika o nazwisku Kowalski

Większość nowoczesnych języków programowania zawiera biblioteki obsługujące bazy noSQL ich obługa najczęściej podobna jest do obsługi baz SQL, z tą różnicą, że dostęp do danych nie jest zapewniany przez zapytania SQL ale przy pomocy odpowiednich metod obiektu reprezentującego bazę danych.

Ogólna postać dostępu do bazy noSQL wygląda w sposób następujący:

Import NoSQLLibrary; //dołączenie biblioteki obsługi bazy danych DBLibrary baza = new NoSQLClass; //utworzenie obiektu z klasy DBClass zdefiniowanej w bibliotece baza.open(‘localhost’,2233,’dbname’,’user’,’password123’); //otwarcie połączenia do bazy danych dbname działającej na serwerze lokalnym (localhost) na porcie 2233 jako użytkownik user z hasłem password123 var pracownicy=baza.collection(‘pracownicy‘); // przypisanie do obiektu pracownicy kolekcji pracownicy z bazy danych noSQL. resullt=pracownicy.find({"nazwisko":"Kowalski"}).sort({"nazwisko":1, "imie":1}) foreach (result as row) //pętla wykonywana dla kolejnych danych w zmiennej result oznaczanych w kolejnych iteracjach jako zmienna row { print(row.imie, row.nazwisko.); //wydruk na ekranie pól nazwisko oraz imie kolejnych wierszy zmiennej result } baza.disconnect(); //zakończenie połączenia z bazą danych baza.destroy();//usunięcie obiektu z pamięci

Funkcje obsługujące konkretne operacje na danych zależne są od wybranego języka programowania.

Poprzedni rozdział Następny rozdział