D/Podstawowe procedury wejścia i wyjścia

Z Wikibooks, biblioteki wolnych podręczników.

< D

Spis treści

[edytuj] Procedury wejścia i wyjścia

Procedury wejścia i wyjścia (ang. input/ouput, IO) są sposobem na komunikację programu ze światem zewnętrznym. Może to być interakcja z klawiaturą i ekranem, albo zapis plików na dysk, czy też komunikacja przez sieć. Są one bardzo ważne, ponieważ bez nich nie bylibyśmy w stanie się dowiedzieć jaki są wyniki działania programu. Do procedur wejścia wyjścia można też zaliczyć sterowanie innymi urządzeniami. Procedury wejścia i wyścia nie są formalnie częścią języka, a raczej biblioteki standardowej, lecz ich możliwości w pewien sposób świadczą o języku. Są one również utożsamiane z językiem z powodu bardzo częstego ich użycia. W tym rozdziale zajmiemy się procedurami obsługi ekranu a potem klawiatury.

[edytuj] Standardowe wejście i wyjście programu

W większości systemów operacyjnych standardowe wejście i wyjście to synonim na klawiaturę i ekran (terminal znakowy). Są to pliki, i jako takie można na nich przeprowadzać operacje czytania i zapisywania, standardowymi procedurami plikowymi które poznamy później. Jednak z powodu częstości w której się używa procedur obsługi klawiatury i ekranu, stworzone do tego cele specjalne procedury ukrywające szczegóły obsługi tych plików oraz dodatkowo ułatwiające skompilkowane operacje (takie jak formatowanie, rozpoznawanie typów) - wbrew pozorom proste wyświetlenie liczby to operacja bardzo skompilkowana (np. funkcja writefln i kluczowe funkcje pomocnicze mają łącznie ok. 1500 linii kodu)

[edytuj] Standardowe wyjście (stdout) oraz literały tekstowe, writef i formaty

Aby łatwo używać standardowe wyjście (ekran) do wypisywania tekstów oraz tekstowej reprezentacji innych typów (np. liczb) stworzono funkcje writefln i writef znajdujące się w module std.stdio. Funkcje writefln już używaliśmy do wypisywania łańcuchów znaków oraz liczb.

Argumenty writefln są w pewnym sensie mieszanego typu. Jest to podtyktowane tym, iż funkcja writef (i writefln) jest bardzo podobna do funkcji printf z C (też jest dostępna w D, jednak nie jest zalecane jej użycie).

Aby nie wprowadzać zamieszanie, najpierw opiszemy elementy odziedziczone z printf.

Funkcji writef przekazujemy jako pierwszy argument łancuch znaków, z ewentualnymi znakami formatującmi i specyfikatorach typu.

W najprostszej postaci jest to sam tekst:

writef("Hello");

wypisze to nam tekst Hello, bez przechodzenia do nowej linii (potocznie bez entera na końcu)

Aby dodać enter należy dodać znak specjalny oznaczający nową linie, jest to znak '\n', na przykład na końcu:

writef("Hello\n");

Kompilator podczas analizy kodu zinterpretuje te dwa znaki (backslash i n) jako pojedyńczy znak. Do najważniejszych innych znaków specjalnych należą:

znak specjalny znaczenie
\n nowa linia
\t tabulacja
\b cofnięcie o jeden znak (backspace)
\\ wypisywanie samego znaku \ (backslash)
\" wypisanie znaku podwójnego apostrofa " - \" (ponieważ w przeciwnym wypadku kompilator uznał by to za koniec napisu).

Ponieważ wstawianie entera na końcu jest bardzo często praktyką i częściowo zaciemnia nasz tekst, lepiej jest użyć funkcji writefln, która właśnie wstawia enter, po skończeniu wypisywania wszystkich swoich argumentów:

writefln("Hello");  // równoważne z writef("Hello\n");

Znaki specjalne możemy wstawiać w dowolnych miejscach w łancuchu znaków, np.

writef("Hello\nWorld\n");

spowoduje wypisywanie słowa Hello, przejścia do nowej linii, wypisania World oraz znowu przejścia do nowej linii.

Łańcuch formatujący może zawierać informacje o typach i formatowaniu kolejnych argumentów, na przykład

writefln("1/3 = %f", 1.0/3.0);

spowoduje wyświetlenie "1/3 = 0.3333333333". %f jest tutaj właśnie formatem. Zaczyna się on od znaku procenta, kończy na literze f oznacającej liczbę zmiennoprzecikową (float), inaczej rzeczywistą.

Istnieje wiele innych formatów. Do sprawnego posługiwania się procedurami wyjścia wystarczy znajomość kilku, tu podajemy je dla kompletności:

format znaczenie
 %d liczba całkowita (wyświetlana dzięsiętna)
 %f, %F liczba rzeczywista (float, double, real): standardowo 6 cyfr dziesiętnych po przecinku oraz przynajmniej jedna cyfra przed przecinkiem.
 %e, %E format naukowy liczb rzeczywistych: standardowo w formacie: 1.231213e+11, tj. jedną cyfrą przed przecinkiem, 6 cyframi po, oraz 2 wykładniku)
 %g, %G liczba rzeczywista w formacie automatycznie dobieranym pomiędzy %f, a %e
 %s łańcuch znaków UTF8 lub tekstowa reprezentacja innych danych: (true/false dla bool, %d dla całkowitych, %g dla rzeczywistych, kolejne elementy tablicy dla tablic dynamicznych i statycznych, opis obiektu poprzez metodę .toString() jeśli istnieje)
 %% sekwencja służąca do wyświetlania znaku % (procent)
 %x, %X liczba całkowita (wyświetlana szesnastkowo, małymi lub dużymi literami)
 %o liczba całkowita (wyświetlana ósemkowo)
 %b liczba całkowita (wyświetlana dwójkowo)
 %a, %A liczba rzeczywista (wyświetlana szesnastkowo, z wykładnikiem przy podstawie 2)

W jednym łańcuchu może być więcej danych o formatach argumentów. Na przykład

int a = 42, b = 22;
writefln("a=%d b=%d a+b=%d", a, b, a+b);

wyświetli "a=42 b=22 a+b=64".

Należy pamiętać, aby formaty zgadzały się z typami podanych argumentów, oraz formatów nie było więcej niż podanych argumentów - w przeciwnym wypadku funkcja writefln zgłosi wyjątek i w domyślnej sytuacji zatrzyma program.

W D jednak formatów może być mniej niż argumentów. Wtedy zostaną one wyświetlone po wyświetleniu wszystkich już obsłużonych zmiennych w sposób domyślny, a w przypaku stałych literałów tekstowych (napisów) zostaną one zinterpretowane znowu jako łańcuch formatujący w którym znowu można używać znaków %, które tym razem będą dotyczyć argumentów za tym łańcuchem. Na przykład:

writefln("a=%d b=%d", a, b, " a+b=%d", a+b); // równoważne poprzedniemu
writefln("a=%d b=%d ", a, b, "a+b=%d", a+b); // j.w., tylko spacja w innym miejscu

albo

writefln("a=", a, " b=", b, " a+b=", a+b);    // j.w., ale zauważ spacje

Formaty mogą mieć dodatkowe parametry (wpisywane pomiędzy %, a znak formatu), takie jak ilość cyfr po przecinku, czy szerokość pola w którym będzie wyświetlana zmienna:

float a = 1.0/3.0;
writefln("%.3f", a);       // wyswietli 3 liczby po przecinku tj. 0.333
writefln("%.6f", a);       // wyswietli 6 liczb po przecinku tj. 0.333333
int b = 12;
writefln("%6d", b);        // wyświetli 12, ale z dopisanymi 4 spacjami
  // po lewej, tak aby łącznie napis zajął 6 znaków, tutaj "    12"
  // przydatne przy tworzeniu tabelek, czy dobrze wyglądających raportów
float c = 124.22;
writefln("%8.2f", c);      // wyświetli 2 znaki po przecinku, oraz dopisze z 
  // lewej strony tyle spacji aby łącznie cały napis zajął 8 znaków.
  // tutaj "   124.22", przydatne np. w obliczeniach finansowych.
Do zrobienia Do zrobienia:
Formaty: szesnastkowe, ósemkowe, wepełnianie pól, wyrównywanie


[edytuj] Standardowe wyjście błędów (stderr)

[edytuj] Standardowe wejście (stdin)

Najprostszym sposobem na pobranie jest użycie funkcji

string readln(_iobuf* fp = stdin, dchar terminator = '\x0a');

Funkcja ta należy do modułu std.stdio - należy dodać import std.stdio na początku źródła. Zwraca ona wartość typu char[] zakończone zerem lub null w przypadku odczytania znaku EOF W przypadku, gdy pobieramy dane ze standardowego wejścia, możemy przyjąć, że ma postać

char[] readln();

[edytuj] Wczytywanie liczb

Język D w przeciwieństwie do C i C++ nie umożliwia bezpośredniego wczytywania danych liczbowych ze standardowego wejścia. Aby pobrać liczbę, należy najpierw wczytać linię tekstu, używając powyższej metody, a następnie skonwertować ją do odpowiedniego typu liczbowego.

Do konwersji ciągów znaków na liczby służą metody zawarte w module std.conv (oraz analogiczne dla pozostałych typów liczbowych):

int toInt(char[]);
uint toUint(char[]);
double toDouble(char[]);

Przykładowy program wczytujący ze standardowego wejścia liczbę wygląda tak:

import std.stdio;
import std.conv;

void main()
{
  writef("Wprowadź liczbę: ") // Dzięki użyciu writef zamiast writefln kursor ustawi się na końcu bieżącej linii zamiast w nowej
  string str = readln();
  int liczba = toInt(str);
}

Inną metodą konwersji tekstu na liczby jest użycie szablonu parse (również zawartego w module std.conv):

 int liczba = parse!(int)(str);
 double liczba = parse!(double)(str);
Porada Po wykorzystaniu szablonu parse wartość zmiennej str ulegnie zmianie - z przodu odcięta zostanie liczba, którą otrzymujemy w wyniku.


[edytuj] Obiekty din, dout iderr

[edytuj] Używanie formatów do własnych potrzeb