]>
suma logiczna (OR, +, |) | a OR 1 = 1 | a OR 0 = a | a OR a = a |
iloczyn logiczny (AND, *, &) | a AND 1 = a | a AND 0 = 0 | a AND a = a |
negacja (NOT, ~, ^, !) | NOT 1 = 0 | NOT 0 = 1 | NOT (NOT a) = a |
łączność | (a OR b) OR c = a OR (b OR c) | (a AND b) AND c = a AND (b AND c) |
przemienność | a OR b = b OR a | a AND b = b AND a |
rozdzielność | a AND (b OR c) = (a AND b) OR (a AND c) | a OR (b AND c) = (a OR b) AND (a OR c) |
absorpcja | a AND (b OR a) = a | a OR (b AND a) = a |
negacja sumy i iloczynu | NOT (a OR b) = (NOT a) AND (NOT b) | NOT (a AND b) = (NOT a) OR (NOT b) |
pochłanianie | a OR (NOT a) = 1 | a AND (NOT a) = 0 |
alternatywa wykluczająca (XOR) | a XOR b = (a AND (NOT b)) OR (b AND (NOT a)) | a XOR 0 = a | a XOR a = 0 |
a NAND b = NOT (a AND b) | (a NAND b) NAND (c NAND d) = (a AND b) OR (c AND d) | ||
a NOR b = NOT (a OR b) | (a NOR b) NOR (c NOR d) = (a OR b) AND (c OR d) | ||
a XNOR b = NOT (a XOR b) = a XAND b |
a | b | a OR b | a AND b | a NOR b | a NAND b | a XOR b | a XNOR b | NOT a |
0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 |
0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | |
1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 0 |
1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 |
a3 a2 a1 a0
reprezentuje liczbę an-1 ... a3 a2 a1 a0
będzie miała wartość 0b101 = 101b
reprezentuje liczbę 5 w systemie dziesiętnym (0xc7 = #c7 = c7h
reprezentuje liczbę 199 w systemie dziesiątkowym (0xc = 0b1100
oraz 0x7 = 0b0111
to 0xc7 = 0b 1100 0111
.
J | K | Qn+1 | Opis |
0 | 0 | Qn | brak zmiany |
0 | 1 | 0 | reset |
1 | 0 | 1 | set |
1 | 1 | NOT Qn | zmiana wyjścia na przeciwny |
clang++
lub g++
). Kompilacja C i C++ przebiega kilku etapowo. W pierwszej kolejności wywoływany jest preprocesor, który jest odpowiedzialny za włączanie plików określonych poprzez #include
(jest to literalne włączenie zawartości wskazanego pliku w danym miejscu, obsługę rozwijania stałych makr preprocesora (definiowanych z użyciem #define
) oraz kompilację warunkową z wykorzystaniem poleceń takich jak #ifdef
czy #if
. Kompilatory pozwalają na uzyskanie nie tylko wynikowego pliku binarnego, ale także plików po przetworzeniu przez preprocesor czy też po konwersji na assembler.-a
(jej podanie zakłada że podawany kod jest tylko częścią ujętą w <?php
i ?>
, bez tej opcji kod zostanie zinterpretowany i wykonany dopiero po wczytaniu znaku końca pliku, który można wprowadzić Ctrl+D).int main() { // liczba całkowita ze znakiem int liczbaA = -34; // liczba rzeczywista (pojedynczej precyzji) float liczbaB = 673.1; // 8 bitowa liczba całkowita bez znaku uint8_t liczbaC = 0xf3; // zmienna napisowa "C NULL-end string" char* napisA = "q we"; // zmienna napisowa typu "C++ string" std::string napisB = "a bc"; }
# dynamiczne typowanie - typ określany jest # na podstawie wartości zapisywanej do zmiennej zmiennaA = -91.7 zmiennaB = "qa z"
# dynamiczne typowanie - typ określany jest # na podstawie wartości znajdującej się w zmiennej # # zasadniczo wszystkie zmienne są napisami, a interpretacja # ma miejsce przy ich użyciu a nie przy tworzeniu # obsługiwane liczby całkowite oraz napisy # brak obsługi liczb zmiennoprzecinkowych # # brak spacji pomiędzy nazwą zmiennej a znakiem równości # w operacji przypisania jest wymogiem składniowym zmiennaA=-91 zmiennaB="qa z" zmiennaC=98.6 # to będzie traktowane jako napis a nie liczba # Odwołanie do zmiennej odbywa się z użyciem znaku dolara, # po którym występuje nazwa zmiennej. Nazwa może być ujęta # w klamry. Rozwijaniu ulegają nazwy zmiennych znajdujące # się w napisach umieszczonych w podwójnych cudzysłowach. # Umieszczenie odwołania do zmiennej w podójnych # cudzysłowach zabezpiecza białe znaki (spacje nowe linie) # przy przekazywaniu do funkcji i programów (w tym przy # przekazywaniu do echo, celem wypisywania). echo $zmiennaA ${zmiennaA}AA echo "$zmiennaA ${zmiennaA}AA" echo '$zmiennaA ${zmiennaA}AA' # Jeżeli chcemy aby zmienna była widoczna przez programy # uruchamiane z naszej powłoki należy ją wyeksportować za # pomocą polecenia `export zmienna` (nazwa bez znaku dolara).
<?php # dynamiczne typowanie - typ określany jest # na podstawie wartości zapisywanej do zmiennej $zmiennaA = -91.7 $zmiennaB = "qa z" ?>
var a = 13.3 var b = "qa z"
#include <stdio.h> int main() { double a = 12.7, b = 3, c, d, e; int x = 5, y = 6, z; // dodawanie, mnożenie, odejmowanie zapisuje się // i działają one tak jak w normalnej matematyce: e = (a + b) * 4 - y; // dzielenie zależy od typów argumentów d = a / b; // będzie dzieleniem zmiennoprzecinkowym bo a i b są typu float c = x / y; // będzie dzieleniem całkowitym bo z i y są zmiennymi typu int b = (int)a / (int)b; // będzie dzieleniem całkowitym a = (double)x / (double)y; // będzie dzieleniem zmiennoprzecinkowym // reszta z dzielenia (tylko dla argumentów całkowitych) z = x % y; // wypisanie wyników printf("%d %f %f %f %f %f\n", z, e, d, c, b, a); // operacje logiczne: // ((a większe równe od 0) AND (b mniejsze od 2)) OR (z równe 5) z = (a>=0 && b<2) || z == 5; // negacja logiczna z x = !z; printf("%d %d\n", z, x); // operacje binarne: // bitowy OR 0x0f z 0x11 i przesunięcie wyniku o 1 w lewo x = (0x0f | 0x11) << 1; // bitowy XOR 0x0f z 0x11 y = (0x0f ^ 0x11); // negacja bitowa wyniku bitowego AND 0xfff i 0x0f0 z = ~(0xfff & 0x0f0); printf("%x %x %x\n", x, y, z); // uwaga: powyższy program może nie wykonywać obliczeń w czasie działania // ze względu na optymalizację i fakt iż wyniki wszystkich operacji // są znane w momencie kompilacji programu }
a = 12.7 b = 3 x = 5 y = 6 # dodawanie, mnożenie, odejmowanie zapisuje się # i działają one tak jak w normalnej matematyce: e = (a + b) * 4 - y # dzielenie zmiennoprzecinkowe c = x / y #dzielenie całkowite b = a // b # reszta z dzielenia z = x % y; # wypisanie wyników print(e, c, b, z) # operacje logiczne: # ((a większe równe od 0) AND (b mniejsze od 2)) OR (z równe 5) z = (a>=0 and b<2) or z == 5; # negacja logiczna z x = not z; print(z, x); # operacje binarne: # bitowy OR 0x0f z 0x11 i przesunięcie wyniku o 1 w lewo x = (0x0f | 0x11) << 1; # bitowy XOR 0x0f z 0x11 y = (0x0f ^ 0x11); # negacja bitowa wyniku bitowego AND 0xfff i 0x0f0 z = ~(0xfff & 0x0f0); print(hex(x), hex(y), hex(z & 0xffff)); # wypisując z musimy określić jego bitowość # wieloargumentowa operacja przypisania # może być użyta do zamiany wartości pomiędzy dwoma zmiennymi # bez jawnego używania zmiennej tymczasowej print(a, b) a, b = b, a print(a, b) # oczywiście można w jej ramach używać więcej niż dwóch zmiennych
a=12; b=3; x=5; y=6 # aby wykonać działania arytmetyczne należy # umieścić je wewnątrz $(( i )) # dodawanie, mnożenie, odejmowanie zapisuje się # i działają one tak jak w normalnej matematyce: e=$(( ($a + $b) * 4 - $y )) # dzielenie całkowite c=$(( $x / $y )) # wypisanie wyników # (zwróć uwagę na wynik wypisania niezainicjalizowanej zmiennej z) echo $e $c $z # operacje logiczne obsługiwane są komendą test # lub operatorem [ ] wynik zwracany jest jako kod powrotu # należy zwrócić uwagę na escapowanie odwrotnym ukośnikiem # nawiasów i na to że spacje mają znaczenie # ((a większe równe od zera) AND (b mniejsze od dwóch)) OR (z równe 5) [ \( $a -ge 0 -a $b -lt 2 \) -o $z -eq 5 ]; z=$? # negację realizuje !, ale wynikiem negacją dowolnej liczby jest FALSE # więc nie da się zanegować z jak w pozostałych przykładach echo $z # bash stosuje logikę odwróconą 0 == TRUE, coś nie zerowego to FALSE # bash nie obsługuje liczb zmiennoprzecinkowych ani operacji bitowych # nieobsługiwane operacje można wykonać za pomocą innego programu np: a=`echo 'print(3/2)' | python3` b=$(echo '3/2' | bc -l) echo $a $b # ujęcie polecenia w znaki ` powoduje podstawienie w tym miejscu # standardowego wyjścia tego polecenia (w tym wypadku zapisania go # do zmiennej), alternatywną składnią jest $( polecenie ) # pokazany na przykładzie zmiennej b
var a = 12.7, b = 3, c = 13, d, e; // dodawanie, mnożenie, odejmowanie zapisuje się // i działają one tak jak w normalnej matematyce: e = (a + b) * 4 - c; // dzielenie zawsze jest zmiennoprzecinkowe d = a / b; c = b / c; // wypisanie wyników console.log( "e=" + e + "\n" + "d=" + d + "\n" + "c=" + c + "\n" ); // operacje logiczne: // ((a większe równe od 0) AND (b mniejsze od 2)) OR (c równe 5) var z = (a>=0 && b<2) || c == 5; // negacja logiczna z a = !z; console.log( "z=" + z + "\n" + "a=" + a + "\n" ); // operacje binarne: // bitowy OR 0x0f z 0x11 i przesunięcie wyniku o 1 w lewo a = (0x0f | 0x11) << 1; // bitowy XOR 0x0f z 0x11 b = (0x0f ^ 0x11); // negacja bitowa wyniku bitowego AND 0xfff i 0x0f0 c = ~(0xfff & 0x0f0); console.log( "a=0x" + a.toString(16) + "\n" + "b=0x" + b.toString(16) + "\n" + "c=0x" + (c & 0xffff).toString(16) + "\n" );
goto
(realizująca skok bezwarunkowy) jest pełnoprawną instrukcją skoku, jedyną wadą jej stosowania jest to że przy niewłaściwym / zbyt częstym wykorzystywaniu (zamiast wywołań funkcji, warunków i pętli) kod programu staje się mniej czytelny.#include <stdio.h> int main() { int i, j, k; // instrukcja waunkowa if - else if (i<j) { puts("i<j"); } else if (j<k) { puts("i>=j AND j<k"); } else { puts("i>=j AND j>=k"); } // podstawowe operatory logiczne if (i<j || j<k) puts("i<j OR j<k"); // innymi operatorami logicznymi są && (AND), ! (NOT) // pętla for for (i=2; i<=9; ++i) { if (i==3) { // pominięcie tego kroku pętli continue; } if (i==7) { // wyjście z pętli break; } printf(" a: %d\n", i); } // pętla while while (i>0) { printf(" b: %d\n", --i); } // pętla do - while do { printf(" c: %d\n", ++i); } while (i<2); // instrukcja wyboru switch switch(i) { case 1: puts("i==1"); break; default: puts("i!=1"); break; } goto ETYKIETA; puts("to się nigdy nie wykona"); puts("bo wcześniej robimy bezwarunkowe goto"); ETYKIETA: puts("a to się wykona"); }
i, k, j = 0, 0, 0 # wielokrotne przypisanie # najpierw oblicza wartości wyrażeń po prawej, # potem przypisuje. Pozwala na a, b = b, a # celem zamiany wartości zmiennych # instrukcja waunkowa if - else if i<j : print("i<j") elif j<k : print("i>=j AND j<k") else: print("i>=j AND j>=k") # podstawowe operatory logiczne if (i<j or j<k) puts("i<j OR j<k"); # innymi operatorami logicznymi są and oraz not # pętla for for i in range(2, 9): if i==3: # pominięcie tego kroku pętli continue; if i==7: # wyjście z pętli break; print(" a:", i); else: # nie wejdzie tutaj bo pętla kończy # się z użyciem break print("else w pętli"); # pętla while while i>0 : i = i - 1; print(" b:", i); else: # wejdzie tu gdy warunek i>0 # nie będzie spełniony # zarówno przy pierwszym # jak i kolejnych sprawdzeniach print("else w pętli");
# pętla for for (( i=0 ; $i<=20 ; i++ )) ; do echo $i; done # w bardziej "shellowym" stylu: for i in `seq 0 20`; do echo $i; done # pętla for po liście rozdzielanej spacjami # wypisanie nazw wszystkich plików z /tmp for nazwa in /tmp/* ; do echo $nazwa; done # pętla while z read (po liniach pliku) cat /etc/fstab | while read slowo reszta; do echo $reszta; done # powyższa pętla wypisze po kolei wszystkie # wiersze pliku przekazanego przez stdin # (cat nazwa_pliku |) z pominięciem # pierwszego słowa (które wczytywane było do # zmiennej slowo) # domyślnym separatorem pól dla komendy read # jest dowolny ciąg białych znaków (spacji i # tabulatorów) można go zmienić przy pomocy # zmiennej IFS: IFS=: while read a b c d; do echo $c done < /etc/passwd unset IFS # Należy mieć na uwadze, że w konstrukcjach typu # while read, pętla while uruchamiana może być w # procesie potomnym obecnej powłoki. # Efektem tego jest iż w niektórych przypadkach # wykonywane modyfikacje zmiennych wewnątrz # takiej pętli nie będą widoczne poza nią. # instruikcja if - else if [ "$xx" = "kot" -o "$xx" = "pies" ]; then echo "kot lub pies"; elif [ "$xx" = "ryba" ]; then echo "ryba" else echo "coś innego" fi # spacje wokół i wewnątrz nawiasów kwadratowych # przy warunku są istotne składniowo, zawartość # nawiasów kwadratowych to tak naprawdę # argumenty dla komendy test, zatem wywołanie: # if [ "$xx" = "ryba" ]; then # jest równoważne: # if test "$xx" = "ryba"; then # a więcej na temat warunków można znaleźć w: # man test # jako warunek może wystąpić dowolne polecenie # wtedy sprawdzany jest jego kod powrotu # 0 oznacza prawdę / zakończenie sukcesem # wartość nie zerowa fałsz / błąd if grep '^root:' /etc/passwd > /dev/null; then echo /etc/passwd zawiera użytkownika root; fi # istnieje możliwość skróconego zapisu warunków # z użyciem łączenia instrukcji przy pomocy: # && wykonaj instrukcję występująca po prawej # gdy poprzednia zwróciła zero (true) # || (wykonaj instrukcję występująca po prawej # gdy poprzednia zwróciła nie zero -- false [ "$xx" = "ryba" ] && echo '$xx = to ryba' grep '^root:' /etc/passwd > /dev/null && \ echo /etc/passwd zawiera użytkownika root; # instrukcja case # (w odróżnieniu od switch z C obsługuje napisy) case $xx in kot | pies) echo "kot lub pies" ;; ryba) echo "ryba" ;; *) echo "cos innego" ;; esac
<?php $i=$j=$k=0; // instrukcja waunkowa if - else if ($i<$j) { echo("i<j\n"); } else if ($j<$k) { echo("i>=j AND j<k\n"); } else { echo("i>=j AND j>=k\n"); } // pętla for for ($i=2; $i<7; ++$i) { if ($i==$j) { // pominięcie tego kroku pętli continue; } printf(" a: %d\n", $i); } // pętla while while ($i>0) { if ($i==$k) { // wyjście z pętli break; } printf(" b: %d\n", --$i); } // pętla do - while do { printf(" c: %d\n", ++$i); } while ($i<2); // instrukcja wyboru switch switch($i) { case 1: echo("i==1\n"); break; default: echo("i!=1\n"); break; } ?>
var i, j, k; // instrukcja waunkowa if - else if (i<j) { console.log("i<j\n"); } else if (j<k) { console.log("i>=j AND j<k\n"); } else { console.log("i>=j AND j>=k\n"); } // podstawowe operatory logiczne if (i<j || j<k) console.log("i<j OR j<k\n"); // innymi operatorami logicznymi są && (AND), ! (NOT) // pętla for for (i=2; i<=9; ++i) { if (i==3) { // pominięcie tego kroku pętli continue; } if (i==7) { // wyjście z pętli break; } console.log(" a: ", + i + "\n"); } // pętla while while (i>0) { console.log(" b: " + --i + "\n"); } // pętla do - while do { console.log(" c: " + ++i + "\n"); } while (i<2); // instrukcja wyboru switch switch(i) { case 1: console.log("i==1\n"); break; default: console.log("i!=1\n"); break; }
#include <stdio.h> #include <stdarg.h> // potrzebne dla obsługi dowolnej ilości argumentów // funkcja bezargumentowa niezwracająca wartości void f1() { puts("ABC"); } // funkcja dwuargumentowa zwracająca wartość int f2(int a, int b) { return a*2.5 + b; } // funkcja z jednym argumentem obowiązkowym // i jednym opcjonalnym float f3(int a, int b=1) { puts("F3"); return a*2.5 + b; } // funkcja z dwoma argumentami wymaganymi // i dowolną ilością argumentów opcjonalnych float f4(int a, int b, ...) { float ret; va_list vl; va_start(vl, b); // w tym miejscu potrzebujemy znać ilość // oraz typy argumentów for (int i=0; i<a; i++) { ret += b * va_arg(vl,double); } va_end(vl); return ret; } int main() { f1(); int a = f2(3, 6); // zwracaną wartość można wykorzystać // (jak wyżej) lub zignorować: f3(0); float b = f4(2, 1, 2.8, 3.5); printf("%d %f\n", a , b); }
# funkcja bezargumentowa, zwracająca wartość def f1(): print("AA") return 5 a = f1() print(a) # funkcja przyjmująca jeden obowiązkowy # argument oraz dwa opcjonalne def f2(a, b=2, c=0): print(a**b+c) f2(3) f2(3, 3) # można pominąć dowolne z argumentów z wartością # domyślną odwołując się do pozostałych nazwami f2(2, c=1) # można podawać argumenty w dowolnej kolejności # odwołując się do nich nazwami f2(b=3, a=2) # nieokreślona ilość argumentów pozycyjnych def f(*a): for aa in a: print(aa) f(1, "y", 6) # ale nie: f(1, "y", u="p") # nieokreślona ilość argumentów nazwanych def f(**a): for aa in a: print(aa, "=", a[aa]) f(a="y", u="p") # ale nie: f(1, u="p") # nieokreślona ilość argumentów pozycyjnych i nazwanych def f(*a1, **a2): print(a1) print(a2) f(1, "y", 6) f(a="y", u="p") f(1, "y", u="p") # można też wymusić ileś argumentów jawnych def f(x, *a1, y="8", **a2): print(x, y) print(a1) print(a2) f(1, "y", 6) f(1, "y", u="p") f(1, "z", y="y", u="p") # ale nie: f(a="y", u="p")
# w bashu każda funkcja może przyjmować # dowolną ilość parametrów pozycyjnych # (w identyczny sposób obsługiwane są # argumenty linii poleceń dla całego skryptu) f1() { echo "wywołano z $# parametrami" echo "parametry to: $@" [ $# -lt 2 ] && return; # można odwoływać się do pojedynczych parametrów echo "drugi: $2" echo "pierwszy: $1" # albo kolejnych w pętli for a in "$@"; do echo $a; done # lub z użyciem polecenia shift for i in `seq 1 $#`; do echo $1 shift # powoduje zapomnienie $1 # i przenumerowanie pozostałych # argumentów pozycyjnych o 1 # wpływa na wartości $@ $# itp done # funkcja może zwracać tylko wartość numeryczną # tzw kod powrotu return 83 } # wywołanie - tak jak komendy, czyli bez nawiasów, # a argumenty rozdzielane białym znakiem (spacją) f1 aaa 3 t 56 # kod powrotu ostatnio wywołanej komendy lub funkcji # uzyskuje się poprzez $?: echo $? # często funkcje (tak jak wiele komend) wynik swojego # działania zwracają poprzez standardowe wyjście f2() { echo "Uwolnić mrożone truskawki$1"; } # pozyskać go można poprzez `` lub $() # zapis z użyciem $() może być zagnieżdżony a=`f2 '!!!'` echo $a b=$(f2 '!') echo $b # w jednolinijkowym zapisie definicji funkcji (jak # miało to miejsce dla f2) spacje po { i przed } są # obowiązkowe, podobnie jak średniki po instukcjach
<?php # funkcja bezargumentowa, zwracająca wartość function f1() { print("AA\n"); return 5; } $a = f1(); print($a); print("\n"); # funkcja przyjmująca jeden obowiązkowy # argument oraz dwa opcjonalne function f2($a, $b=2, $c=0) { print($a**$b+$c); print("\n"); } f2(3); f2(3, 3); # nieokreślona ilość argumentów pozycyjnych function f3() { $num = func_num_args(); print("ilość argumentów:\n"); print( $num ); print("\n"); for($i=0; $i<$num; ++$i) { print("argument $i: "); print(func_get_arg($i)); print("\n"); } } f3(); f3("a", 1, "c", 5); # jeden wymagany, jeden opcjonalny # i nieokreślona ilość dodatkowych function f4($a, $b="16") { # tablica z wszystkimi argumentami # jest alternatywną metodą dostępu # do wszystkich argumentów w stosunku # co do pokazanej w f3() $args = func_get_args(); print_r($args); } f4("a", 1, "c", 5); ?>
function f1() { console.log("ABC\n"); } // funkcja dwuargumentowa zwracająca wartość function f2(a, b) { return a*2.5 + b; } // funkcja z jednym argumentem obowiązkowym // i jednym opcjonalnym function f3(a, b=1) { console.log("F3\n"); return a*2.5 + b; } // funkcja z dowolną ilością argumentów function f4() { var ret = 0; for(var i=0; i<arguments.length; i++) { ret += arguments[i]; } return ret; } f1(); // zwracaną wartość można wykorzystać: var a = f2(3, 6); // lub zignorować: f3(0); var b = f4(2, 1, 2.8, 3.5); console.log("a=" + a +" b=" + b + "\n");
#!/ścieżka/do/programu
) stanowiący informację dla programu uruchamiającego skrypt jakiego interpretera ma użyć w celu wykonania kodu zawartego w pliku (niekiedy zawiera też opcje z jakimi należy uruchomić interpreter).main
. Zakończenie tej funkcji oznacza zakończenie programu, a wartość przez nią zwracana odpowiedzialna jest za tzw. kod powrotu przekazany procesowi wywołującemu program.
#include<iostream> /// generowanie wyjątków void wyj(int i) { switch(i) { // rzucamy wyjątek w zależności od i case 0: throw "Ala ma kota"; case 1: std::cout << "wypisze sie (0) ?" << std::endl; throw "Kot ma Ale"; case 2: throw 13; } std::cout << "wypisze sie (1) ?" << std::endl; } int main() { for (int j=0; j<4; j++) { std::cout << std::endl << "j = " << j << std::endl; try { wyj(j); std::cout << "wypisze sie (2) ?" << std::endl; wyj(1); } catch (char const* opis) { std::cout << "WYJATEK: " << opis << std::endl; } catch (...) { std::cout << "inny wyjątek" << std::endl; } // aby zaoszczędzić jednego kopiowania należy przechwytywać wyjątek przez referencję // ale należy pamiętać iż ze względu na mechanizm działania wyjątków // wartość ta i tak będzie kopiowana } }
# Prawie wszystkie błędy w Pythonie mają postać wyjątków, # które mogą zostać obsłużone blokiem try/except. try: a = 5 / 0 except ZeroDivisionError: print("dzielenie przez zero") except: print("inny błąd") # Przy obsłudze błędów może przydać się instrukcja pusta "pass", # która w tym przypadku pozwala na zignorowanie obsługi danego błędu. try: slownik["a"] += 1 except: pass # Powyższy kod zwiększy wartość związaną z kluczem "a" w słowniku "slownik", # jednak gdy napotka błąd (np. słownik nie zawiera klucza "a") zignoruje go. # Możemy także generować wyjątki z naszego kodu, służy do tego instrukcja raise, # której należy przekazać obiektem dziedziczącym po \python{BaseException} np: \begin{CodeFrame*}[python]{} raise BaseException("jakiś błąd")
# obliczanie silni z użyciem rekurencji def silnia(n): # każda rekurencja musi mieć warunek końca if n == 1: return 1 else: return n*silnia(n-1) silnia(20)
# iteracyjne obliczanie silni def silnia(n): s = 1 for i in range(2,n+1): s = s*i return s silnia(20)
#include <iostream> #include <stdint.h> struct NazwaStruktury { // pola składowe int a; std::string d; // zmienna statyczna // wspólna dla wszystkich obiektów tej klasy static int x; // stała static const int y = 7; // pola binarne (jedno i trzy bitowe) uint8_t mA :1; uint8_t mB :3; // metody składowe void wypisz() { std::cout << " a=" << a << " d=" << d << "\n"; } // deklaracja metody // definicja musi być podana gdzieś indziej int getSum(int b) ; /// metody statyczna static void info() { std::cout << "INFO\n"; } // konstruktor i destruktor NazwaStruktury(int aa = 0) { std::cout << "konstruktor\n"; a = aa; d = "abc ..."; } ~NazwaStruktury() { // potrzebny gdy klasa tworzy jakieś // obiekty które nalezy usuwać, itp std::cout << "destruktor\n"; } }; // definicja zmiennej statycznej z nadaniem jej wartości // jest to niezbędne aby była ona widoczna ... int NazwaStruktury::x = 13; // wcześniej zdeklarowane metody // możemy definiować także poza deklaracją klasy int NazwaStruktury::getSum(int b) { return a + b; } int main() { // korzystanie ze struktur NazwaStruktury s; s.a = 45; s.wypisz(); // korzystanie z metod statycznych NazwaStruktury::info(); // a także poprzez obiekt danej klasy s.info(); }
class NazwaKlasy: # pola składowe a=0 d="ala ma kota" # metody składowe def wypisz(self): print(self.a + self.b) # warto zauważyć jawny argument # w postaci obiektu tej klasy # w C++ także występuje ale nie jest # jawnie deklarowany, ani nie trzeba # się nim jawnie posługiwać # metody statyczna @staticmethod def info(): print("INFO") # konstruktor (z jednym argumentem) def __init__(self, x = 1): print("konstruktor", self.a , self.d) # i kolejny sposób na utworzenie # pola składowego klasy self.b = 13 * x # korzystanie z klasy k = NazwaKlasy() k.a = 67 k.wypisz() # do metod można odwoływać się także tak: # (jawne użycie argumentu w postaci obiektu klasy) NazwaKlasy.wypisz(k) # korzystanie z metod statycznych NazwaKlasy.info() print("k jest typu:", type(k)) print("natomiast k.a jest typu:", type(k.a)) # obiekty można rozszerzać o nowe składowe i funkcje: k.b = k.a + 10 print(k.b)
<?php class NazwaKlasy { public $a; public $d = "tekst"; # metody składowe public function wypisz() { echo "a=" . $this->a . " d=" . $this->d . "\n"; } # warto zauważyć jawne odwołania do składowych # poprzez zmienną $this # metody statyczna public static function info() { echo "INFO\n"; } } // korzystanie z klasy $k = new NazwaKlasy; $k->a = 87; $k->wypisz(); // korzystanie z metod statycznych NazwaKlasy::info(); // a także poprzez zmienną przechowującą nazwę klasy $t="NazwaKlasy"; $t::info(); ?>
#include <iostream> #include <vector> int main() { // klasyczna tablica int t[4] = {1, 8, 3, 2}; std::cout << t[2] << " -> "; t[2] = 55; std::cout << t[2] << " = " << *(t+2) << "\n"; // jest ona podobnie jak struktura ciągłym obszarem pamięci, // możliwe jest zatem traktowanie takiej tablicy jako struktury struct Struktura { int a, b, c, d; }; Struktura *tt = (Struktura*)t; std::cout << tt->a << " " << tt->c << "\n"; // dynamicznie alokowana tablica C++ STL std::vector<int> v(4); v[3] = 21; std::cout << v[3] << "\n"; }
a[0]="a b c" a[1]=123 a[2]=zz a[3]=qq x=1 echo "wybrane elementy tablicy: " ${a[$x]} ${a[0]} echo "cała tablica: " ${a[@]} echo "ilość elementów w tablicy tablica: " ${#a[@]}
var t = ['a b c', 8, 3, 2]; console.log( "pierwszy element: " + t[0] + "\n" + "długość tablicy: " + t.length + "\n" ); // usuwamy 2 elementy od pozycji 1 t.splice(1, 2) // dodajemy nowe elementy t[5] = "pp"; t[7] = "ww"; // wypisanie wszystkich elementów for (var i=0; i<t.length; ++i) { console.log("t["+ i +"]="+ t[i] +"\n"); } // za pomocą for..in for (var ii in t) { console.log("t["+ ii +"]="+ t[ii] +"\n"); }
#include <iostream> #include <list> int main() { std::list<int> l; // dodanie elementu na końcu l.push_back(17); l.push_back(13); l.push_back(3); l.push_back(27); l.push_back(21); // dodanie elementu na początku l.push_front(8); // wypisanie liczby elementów std::cout << "size=" << l.size()<< "\n"; // wypisanie pierwszego i ostatniego elementu std::cout << "first=" << l.front() << " last=" << l.back() << "\n"; // usuniecie ostatniego elementu l.pop_back(); // posortowanie listy l.sort(); // odwrócenie kolejności elementów l.reverse(); // usuniecie pierwszego elementu l.pop_front(); for (std::list<int>::iterator i = l.begin(); i != l.end(); ++i) { // wypisanie wszystkich elementów std::cout << *i << "\n"; // możliwe jest także: // - usuwanie elementu wskazanego przez iterator // - wstawianie elementu przed wskazanym przez iterator } }
l = [ 3, 5, 8 ] # wstawienie elementu na koniec l.append(1) # wstawienie elementu na pozycje 2 l.insert(2, 13) print("liczba elementów =", len(l)) print("pierwszy =", l[0]) print("dwa kolejne =", l[1:3]) # wypisanie wszystkich elementów for e in l: # możemy modyfikować zmienną "e", # ale nie będzie maiło to wplywu na listę print(e) # alternatywne iterowanie po elementach # (pozwala na ich modyfikowanie) for i in range(len(l)): l[i] = l[i] + 1 print(l[i]) # możemy też uzyskać listę w oparciu o wykonanie jakiś # operacji na danej liście w formie jednolinijkowca: l = [a * 2 for a in l] # listę taką możemy przypisać do innej # lub (jak wyżej) do tej samej zmiennej # pobranie i usuniecie ostatniego elementu print("ostatnim był:", l.pop()) print("ostatnim był:", l.pop()) # pobranie i usuniecie elementu na wskazanej pozycji print("drugim elementem był:", l.pop(1)) # wypisanie całej listy print(l)
#include <iostream> #include <map> int main() { std::map<std::string, int> m; m["a"] = 6; m["cd"] = 9; std::cout << m["a"] << " " << m["ab"] << "\n"; // wyszukanie elementu po kluczu std::map<std::string, int>::iterator iter = m.find("cd"); // sprawdzenie czy istnieje if (iter != m.end()) { // wypisanie pary - klucz wartość std::cout << iter->first << " => " << iter->second << "\n"; // usunięcie elementu m.erase(iter); } m["a"] = 45; // wypisanie całej mapy for (iter = m.begin(); iter != m.end(); ++iter) std::cout << iter->first << " => " << iter->second << "\n"; // jak widać mapa jest wewnętrznie posortowana }
m = { "ab" : 11, "cd" : "xx" } x = "e" m[x] = True; # pobranie samych kluczy for k in m: print (k, "=>", m[k]) # sprawdzenie istnienia if "ab" in m: print ("jest ab") # usunięcie elementu del m['ab'] # modyfikacja wartosci m["cd"] = "oi" # pobranie par klucz wartosc for k,v in m.items(): print (k, "=>", v)
<?php $a = array('ab' => True, 'cd' => 'xx'); $a['efg'] = 15; $a['cd']="yu"; # sprawdzenie istnienia if (isset($a['ab'])) { // uwaga: gdy $a['ab'] = null // to isset zwróci FALSE // patrz array_key_exists() echo "jest ab"; # usunięcie elementu unset($a['ab']); } while ( list($k, $v) = each($a) ) { echo "a[$k] = $v\n"; } ?>
// pseudo odpowiednik // tablicy asocjacyjnej var o = {a: 1, b: 2, c: 3}; // dodanie elementu o.ab = "pp"; // usunięcie elementu delete o.b; for (var k in o) { console.log( `o.${k} = ${o[k]} \n` ); }
std::multimap<>
.
std::set<>
i std::multiset<>
z C++ oraz set([k1, k2, ...])
z Pythona). Różnią się one od map tym że nie przechowują one wartości a jedynie same klucze.
#include <stdio.h> #include <iostream> void f1(int *b) { *b = 2 * *b; } void f2(int (*f)(const char *s)) { f("Uwolnić mrożone truskawki !!!"); } struct kl { // pola składowe int a; int getSum(int b) { return a + b; } }; int main() { // zmienna typu int i wskaźnik na zmienna typu int int a = 5678; int *b = NULL; // pobranie adresu zmiennej do wskaźnika b = &a; std::cout << "a ma adres " << b << "\n"; // modyfikacja wartości na która wskazuje wskaźnik *b = 3456; std::cout << a << " = " << *b << "\n"; // referencja int &c = a; c = 6543; std::cout << a << " = " << c << "\n"; // wskaźniki na obiekty std::pair<int,int> p = std::make_pair(2, 5); std::pair<int,int> *q = &p; // dostęp do składowych poprzez wskaźnik na strukturę (*q).first = 7; q->second = 8; std::cout << p.first << " " << p.second << "\n"; // wskaźnik na składową b = &(q->second); *b = 3; std::cout << p.first << " " << p.second << "\n"; // przekazywanie wskaźnika do funkcji: // 1. (podobnie jak trzymanie wskaźników na obiekty, // zamiast obiektów w listach itp) pozwala na // przekazywanie większych obiektów bez ich kopiowania // 2. pozwala na modyfikowanie wartości argumentów: f1(b); std::cout << p.first << " " << p.second << "\n"; // wskaźnik "fun" na funkcje przyjmującą // wskaźnik const char i zwracającą int int (*fun)(const char *s); // przypisanie adresu funkcji puts do zmiennej fun fun = &puts; // użycie wskaźnika na funkcję jako funkcji fun("aaa"); // wskaźnik na funkcję może być przekazywany // do innych funkcji jako argument f2(fun); // wskaźniki a tablice // w C tablica to wskaźnik na pierwszy element // a t[x] jest równoważne *(t+x) short t[4] = { 11, 22, 33, 44 }; short *tt = t; std::cout << "t[2] = " << t[2] << " = " << *(t + 2) << "\n"; std::cout << "t[0] = " << *tt << " @ " << tt << "\n"; ++tt; std::cout << "t[1] = " << *tt << " @ " << tt << "\n"; // wskaźnik na metodę składową jakiejś klasy // (z wyjątkiem metod statycznych) // wymaga określenia typu tej klasy, // gdyż jest on typem niejawnego argumentu jej metod int (kl::*fun2)(int) = &kl::getSum; // aby skorzystać trzeba mieć obiekt danej klasy // (lub wskaźnik do niego) kl o1; o1.a = 2; kl *o2 = &o1; std::cout << (o1.*fun2)(3) << " " << (o2->*fun2)(3) << "\n"; }
a, b = 5, [1, 2, 3] def pinfo(x, xx): print( "id(x) =", hex(id(x)), " == " if id(xb) == id(xx) else " != ", "id(xx) =", hex(id(xx)) ) # utworzenie kopii zmiennej oznacza utworzenie # nowej referencji wskazującej nadal na ten sam # obiekt w pamięci aa = a pinfo(id(a), id(aa) bb = b pinfo(id(b), id(bb) # przypisanie nowego obiektu pod zmienną # powoduje zmianę (adresu) obiektu który # wskazuje: aa = 5 pinfo(id(a), id(aa) bb = [9, 11, 13] pinfo(id(b), id(bb) # ale modyfikacja obiektów "immutable" # (takich jak liczby, napisy) nie jest możliwa # zatem zawsze tworzony jest nowy obiekt aa=a aa+=1 pinfo(id(a), id(aa) # jeżeli do starego obiektu nie ma innych # referencji może on zostać usunięty # a nowy może być umieszczony w jego miejscu # (w takim przypadku wynik id się nie zmieni) # natomiast modyfikacja obiektów "mutable" # (takich jak listy i słowniki) # nie tworzy nowego obiektu tylko modyfikuje # istniejący (wynik id się nie zmieni) zatem # wszystkie referencje wskazują na zmodyfikowany # obiekt: bb = b bb[0] = 17 print ("bb =", bb, "b=", b) pinfo(id(b), id(bb) # aby uzyskać kopię należy skorzystać # z odpowiedniej metody bb = b.copy() print ("bb =", bb, "b=", b) bb[0] = 99 print ("bb =", bb, "b=", b) pinfo(id(b), id(bb) # uwaga kopiowanie takie jest płytkie: jeżeli w # liście mamy obiekty "mutable" obie (niezależne # pod względem zbioru elementów) kopie listy # będą wskazywać na te same obiekty # usuwanie zmiennej b = None # mapowanie zmiennej "b" zostało zmienione # nie wskazuje już na listę tylko na # obiekt typu NoneType bbb = bb del bb # nazwa "bb" została usunięta # ale do danych możemy dostawać się przez "bbb" # dopiero po usunięciu wszystkich referencji # na dany obiekt to Python może go usunąć (ale # nie musi wykonać tego natychmiast) # python także umożliwia przekazywanie funkcji # jako argumentu do innej funkcji def a(z): print(z*3) def b(x, y): x(y+2) b(a, 1) # ponadto funkcja może także zwracać funkcję: def aa(y): def t(x): return y*x return t b = aa(3) b(2) # można też: aa(3)(4)
# w Bashu nie ma operacji na wskaźnikach # ale podobną funkcję w pewnych wypadkach # może pełnić zmienna zawierająca nazwę # innej zmiennej A='tekst do wypisania, $HOME, `ls`'; B="A"; # proste podejście typu echo ${$B} # (działające np. w PHP) nie zadziała, # ale można to zrobić na kilka innych sposobów: C=${!B}; # ta metoda nie działa w czystym sh echo $C export A C=$(echo "\$$B" | envsubst) # ta metoda wymaga zewnętrznego polecenia # envsubst i wyeksporotwania zmiennych echo $C C=$( eval "echo \$$B" ) echo $C # z użyciem większej liczby poleceń eval możemy # zapewnić także podstawienie kolejnego poziomu # zmiennych lub wykonanie wpisanych w nich poleceń C=$( eval eval "echo \$$B" ) echo $C # zmienna może przechowywać komendę / nazwę # funkcji do wykonania x=ls $x /tmp
<?php // w PHP korzystanie z nazwy zmiennej // przechowywanej w innej zmiennej // jest jeszcze prostsze: $a="12"; $b="a"; $c="b"; echo "$a $b $c\n" echo "${$b} ${$c}\n" echo "${${$c}}\n" // możliwe jest także używanie zmiennych // zawierających nazwy funkcji oraz // przekazywanie ich do innych funkcji function fa($z) { print($z*3); print("\n"); } $c="fa"; $c(2); function fb($x, $y) { $x($y+2); } fb("fa", 1); ?>
new
limitowana jest widoczność i istnienie otrzymanego wskaźnika, ale nie zaalokowanego bloku pamięci. Zatem ograniczona jest widoczność takich zmiennych ale nie czas ich istnienia, dlatego też przed utratą wskaźnika na nie należy je usunąć (zwolnić zaalokowaną pamięć).
#include <iostream> int funA(int a) { a = a*2; return a; } int funB(int &a) { a = a*2; return a; } int main() { int a = 57, b = 23; std::cout << ++a << " " << ++b << "\n"; // wypisze 58 24 { int a = b; std::cout << ++a << " " << ++b << "\n"; // wypisze 25 25 // bo a z tego bloku (==24) przesłoniło wcześniejsza zmienną a (==58) } std::cout << ++a << " " << ++b << "\n"; // wypisze 59 26 // bo a z wcześniejszego bloku już nie istnieje i nie przesłania naszego a (==58) std::cout << funA(a) << " " << funB(b) << "\n"; // wypisze wartości zwracane przez funkcje czyli 118 (59*2) i 52 (26*2) std::cout << a << " " << b << "\n"; // wypisze aktualne wartości argumentu: // a=59 bo przekazanie przez wartość i funkcja operowała na własnej kopii // b=52 bo przekazane przez referencję i funkcja operowała na tej samej kopii }
a, b, d = 5, 12, [1, 2, 3] def f1(c): return c+b def f2(c): b=2 return c+b # f1 korzysta z zmiennej globalnej b # f2 przysłania sobie zmienną globalną b poprzez swoją zmienną lokalną print("f1(a) =", f1(a), "f2(a) =", f2(a), "ale b nadaj wynosi:", b) def f3(c): global b b=16 return c+b # f3 dzięki temu że jawnie deklaruje iż używa globalnego b # może modyfikować zmienną globalną print("f3(a) =", f3(a), "teraz b wynosi:", b) def f4(c): c = 2*c return c # argumenty funkcji są zmiennymi lokalnymi print("f4(a) =", f4(a), "ale a nadaj wynosi:", a) def f5(c): c[0]="xx" return c print("f5(d) =", f5(d), "tym razem d uległo modyfikacji:", d) if d[1] == 2: x = "ABC" else: x = 22 print("zmienna x jest widoczna poza blokiem w którym została utworzona", x)
#include <inttypes.h> #include <stdio.h> int main() { // dane jako tablica liczb 16 bitowych uint16_t aa[4] = {0x1234, 0x5678, 0x9abc, 0xdeff}; // wypisujemy ją printf("A0: %x %x %x %x\n", aa[0], aa[1], aa[2], aa[3]); // chyba nikogo nie zaskoczy wynik powyższego printf: A0: 1234 5678 9abc deff // wypisujemy dwie pierwsze liczby rozłożone na części 8 bitowe (poszczególne bajty) printf("A1: %x %x %x %x\n", (aa[0] >> 8) & 0xff, aa[0] & 0xff, (aa[0] >> 8) & 0xff, aa[0] & 0xff); // efekt też jest oczywisty: A1: 12 34 12 34 // każemy na te same dane patrzeć jako na liczby 8 bitowe (poszczególne bajty) uint8_t* bb = (uint8_t*) aa; printf("B0: %x %x %x %x\n", bb[0], bb[1], bb[2], bb[3]); // czego się teraz spodziewamy? // - wypisze nam tylko połowę oryginalnej tablicy // - ale dokładny wynik zależy od architektury na której uruchamiamy program: // * na little endian (np. x86) będzie to: B0: 34 12 78 56 // * na big endian (np. sparc) będzie to (bardziej naturalne dla człowieka): B0: 12 34 56 78 }
. - dowolny znak [a-z] - znak z zakresu [^a-z] - znak z poza zakresu (aby mieć zakres z ^ należy dać go nie na początku) ^ - początek napisu/linii $ - koniec napisu/linii * - dowolna ilość powtórzeń ? - 0 lub jedno powtórzenie + - jedno lub więcej powtórzeń {n,m} - od n do m powtórzeń () - pod-wyrażenie (może być używane dla operatorów powtórzeń, a także dla referencji wstecznych) | - alternatywa: wystąpienie wyrażenia podanego po lewej stronie albo wyrażenia podanego prawej stronie
#include <stdio.h> #include <iostream> #include <string> #include <string.h> #include <bitset> #include <regex> #include <sstream> int main() { // napisy w stylu C // czyli tak naprawdę tablice bajtów (znaków) const char* x = "abcdefg"; // wypisanie długości napisu printf("%d\n", strlen(x)); // wypisanie pod-napisu od 2 do końca puts(x+2); // wyszukiwanie // pod-napisu "cd" w x od pozycji 1 const char* cd = strstr(x+1, "cd"); printf("%d\n", cd-x); // 3 znakowy pod-napis napisu x // rozpoczynający się od cd char buf[16]; strncpy(buf, cd, 3); buf[3]=0; // NULL end puts(buf); // porównywanie if (strcmp(x, "a") == 0) puts("x == \"a\""); if (strncmp(x, "a", 1) == 0) puts("pierwsze 1 znaków x to \"a\""); // napisy w stylu C++ std::string xx(x); std::string y = "aa bb cc bb dd bb ee"; // wypisanie długości napisu std::cout << xx.size() << "\n"; // .size() to to samo co .length() // uzyskanie napisy w stylu C puts(xx.c_str()); // wypisanie pod-napisu od 2 do końca std::cout << xx.substr(2) << "\n"; std::cout << xx.substr(2, std::string::npos) << "\n"; // i od 0 (początku)do 3 std::cout << xx.substr(0, 3) << "\n"; // wyszukiwanie pod-napisu "bb" w y od pozycji 5 std::cout << y.find("bb", 5) << "\n"; // porównywanie if (xx == "a") std::cout << "x == \"a\"\n"; if (xx.compare(0, 1, "a") == 0) puts("pierwsze 1 znaków x to \"a\""); if ( std::regex_match(xx, std::regex(".*[dz].*")) ) puts("x zawiera d lub z"); // regex_match dopasowuje całość napisu do wyrażenia regularnego // dopasowanie częściowe wraz z opcjonalnym uzyskaniem // pasującej części umożliwia: std::regex_search() // modyfikowanie std::string xx = "Ala ma psa"; // wstawianie - insert(pozycja, co) xx.insert(6, " kota i"); std::cout << xx << std::endl; // zastępowanie - replace(pozycja, ile, czym); xx.replace(4, 2, "miała samochód", 0, 6); // mogłoby też być xx.replace(4, 2, "miała"); i parę innych wariantów ... std::cout << xx << std::endl; // usuwanie - erase(pozycja, ile); xx.erase(9, 1); // 9 zamiast 8 bo UTF-8 i ł ma dwa znaki std::cout << xx << std::endl; // zastępowanie z użyciem wyrażeń regularnych std::cout << std::regex_replace (y, std::regex("[bc]+"), "XX") << "\n"; // zastępowanie z użyciem podstawienia // $2 zostanie zastąpione wartością drugie pod-wyrażenia, // czyli fragmentu ujętego w nawiasach std::cout << std::regex_replace ( y, std::regex("([bc]+) ([bc]+)"), "X-$2-X" ) << "\n"; // konwersja liczb na napis w systemach: // dwójkowym, ósemkowym, dziesiętnym i szesnastkowym std::cout << std::bitset<8>(7) << " "; std::cout << std::oct << 0xf << " "; std::cout << std::dec << 010 << " "; std::cout << std::hex << 0b11 << "\n"; // liczby podawane do wypisywania są w odpowiednio systemach: // dziesiętnym, szesnastkowym, ósemkowym i dwójkowym // wskazane jest to przez brak prefixu i prefixy "0x" "0" "0b" // alternatywnie w stylu printf, ale bez dwójkowego printf("0o%o %d 0x%x\n", 0xf, 010, 0b11); // wypisywanie znaków unicodu puts("\u21c4 = ⇄"); // strumienie napisowe std::ostringstream zzz; zzz << xx << " cpp\n"; zzz << 34.6 << " " << std::oct << 0xf << " "; // konwersja do std::string xx = zzz.str(); std::cout << xx << "\n"; }
import re x = "abcdefg" y = "aa bb cc bb dd bb ee" z = "qw=rt" # wypisanie długości napisu print(len(x)) # wypisanie pod-napisu od 2 do końca # i od 0 (początku)do 3 print (x[2:], x[0:3]) # wypisanie ostatniego i 3 ostatnich znaków print (x[-1], x[-3:]) # wypisanie co 3ciego znaku z napisu oraz napisu od tyłu print (y[::3], x[::-1]) # wyszukiwanie # pod-napisu "bb" w y od pozycji 5 print (y.find("bb", 5)) # porównywanie if x == "a": print("x == \"a\"") if re.search("[dz]", x): print(x, "zawiera d lub z") # sprawdzanie czy jest pod-napisem if "ab" in x: print ("ab jest pod-napisem:", x) # sprawdzanie czy jest pod-napisem if "ba" in x: print ("ba jest pod-napisem:", x) # zastępowanie print (re.sub('[bc]+', "XX", y, 2)) print (re.sub('[bc]+', "XX", y)) # zastępowanie z użyciem podstawienia # \\2 zostanie zastąpione wartością drugie pod-wyrażenia, # czyli fragmentu ujętego w nawiasach print (re.sub('([bc]+) ([bc]+)', "X-\\2-X", y)) # mamy też wpływ na zachłanność wyrażeń regularnych: print (re.sub('bb (.*) bb', "X \\1 X", y)) # "bb (.*) bb" dopasowało najdłuższy możliwy fragment, czyli: cc bb dd print (re.sub('.*bb (.*) bb.*', "\\1", y)) # "bb (.*) bb" dopasowało jedynie "dd", bo najdłuższy możliwy # fragment został dopasowany przez poprzedzające ".*" print (re.sub('.*?bb (.*) bb.*', "\\1", y)) # "bb (.*) bb" mogło i dopasowało najdłuższy możliwy fragment, # gdyż było poprzedzone niezachłanną odmianą dopasowania # dowolnego napisu, czyli: .*? # Po każdym z operatorów powtórzeń (. ? + {n,m}) możemy dodać # pytajnik (.? ?? +? {n,m}?) aby wskazać że ma on dopasowywać # najmniejszy możliwy fragment, czyli ma działać nie zachłannie. # nie da się modyfikować napisu z użyciem odwołań x[numer] np. # x[2]="X" # nie zadziała # można (gdy dużo tego typu modyfikacji) przepisać do listy: l=list(x) # alternatywnie można manualnie: # l=[] # for c in x: # l.append(c) # albo tak: # l=[c for c in x] l[1]="X" l[3]="qqq" del l[5] print("".join(l)) # albo tak (gdy mniej modyfikacji) print(x[:2] + "XXX" + x[3:]) # można także modyfikować po kolei i dodawać do nowego napisu s = "" for c in x: if c == "a": s += "AA" else: s += c print(s) # przy pomocy metody split() napis możemy podzielić # na listę napisów przy pomocy dowolnego separatora print(y.split(" ")) print(y.split(" cc ")) # konwersja liczb na napis w systemach: # dwójkowym, ósemkowym, dziesiętnym i szesnastkowym print( bin(7), oct(0xf), str(0o10), hex(0b11) ) # liczby podawane do wypisywania są w odpowiednio systemach: # dziesiętnym, szesnastkowym, ósemkowym i dwójkowym # wskazane jest to przez brak prefixu i prefixy "0x" "0o" "0b" # alternatywnie w stylu printf, ale bez dwójkowego s = "0o%o %d 0x%x" % (0xf, 0o10, 0b11) print(s) # wypisywanie znaków z użyciem ich numeru w unikodzie # - funkcja chr() zwraca napis złożony ze znaku o podanym numerze # w ramach napisów można też użyć \uNNNN gdzie NNNN jest numerem znaku # lub po prostu umieścić dany znak w pliku kodowanym UTF8 print(chr(0x21c4) + " == \u21c4 == ⇄") # funkcja ord() umożliwia konwersję napis złożonego # z pojedynczego znaku na numer unicodowy print(hex(ord("⇄")), hex(ord("\u21c4")), hex(ord(chr(0x21c4))) ) # Python używa Unicode dla obsługi napisów, jednak przed # przekazaniem napisu do świata zewnętrznego konieczne # może być zastosowanie konwersji do określonej postaci # bytowej (zastosowanie odpowiedniego kodowania) # służy do tego metoda encode() np. a = "aąbcć ... ⇄" inUTF7 = a.encode('utf7') inUTF8 = a.encode() # lub a.encode('utf8') print("'" + a + "' w UTF7 to: " + str(inUTF7)) print(" i jest typu: " + str(type(inUTF7))) # obiekty typu 'bytes' mogą zostać zdekodowane do napisu print("zdekodowany UTF7: " + inUTF7.decode('utf7')) # lub zostać poddane dalszej konwersji np. kodowaniu base64: import codecs b64 = codecs.encode(inUTF8, 'base64') print("napis w UTF8 po zakodowaniu base64 to: " + str(b64))
<?php $x = "abcdefg"; $y = "aa bb cc bb dd bb ee"; $z = "qw=rt"; # wypisanie długości napisu echo strlen($x) . "\n"; # wypisanie pod-napisu od 2 do końca echo substr($x, 2); # i od 0 (początku) 3 kolejne znaki echo substr($x, 0, 3) . "\n"; # wyszukiwanie # pod-napisu "bb" w $y od pozycji 5 echo strpos($y, "bb", 5) . "\n"; # porównywanie if ($x == "a") echo "$x == a\n"; if (substr_compare($x, "de", 3, 2)) { echo "2 znakowy pod-napis od"; echo "pozycji 3 w $x to \"de\"\n"; } if (preg_match("/[dz]/", $x)) { echo "$x zawiera d lub z\n"; } # zastępowanie echo str_replace("bb", "BB", $y) . "\n"; echo preg_replace('/[bc]+/', "XX", $y, 2) . "\n"; echo preg_replace('/[bc]+/', "XX", $y) . "\n"; # zastępowanie z użyciem podstawienia # $2 zostanie zastąpione wartością pierwszego pod-wyrażenia, # czyli fragmentu ujętego w nawiasach echo preg_replace('/^([^=]*)=.*$/', '$1', $z); echo " = "; echo preg_replace('/^[^=]*=(.*)$/', '$1', $z); echo "\n"; # wypisywanie w różnych systemach liczbowych printf("0b%b 0o%o %d 0x%x\n", 7, 0xf, 010, 0b11); ?>
# ${zmienna:-"napis"} zwróci napis gdy # zmienna nie jest zdefiniowana lub jest pusta # ${zmienna:="napis"} zwróci napis # oraz wykona podstawienie zmienna="napis" gdy # zmienna nie jest zdefiniowana lub jest pusta # ${zmienna:+"napis"} zwróci napis gdy # zmienna jest zdefiniowana i nie pusta a=""; b=""; c="" echo ${a:-"aa"} ${b:="bb"} ${c:+"cc"} echo $a $b $c a="x"; b="y"; c="z" echo ${a:-"aa"} ${b:="bb"} ${c:+"cc"} echo $a $b $c # ${#str} zwróci długość napisu w zmiennej str # ${str:n} zwróci pod-napis $str od n do końca # ${str:n:m} zwróci pod-napis $str od n o długości m x=abcdefg echo ${#x} ${x:2} ${x:0:3} ${x:0:$((${#x}-2))} # ${str#"ab"} zwróci $str z obciętym "ab" z początku # ${str%"fg"} zwróci $str z obciętym "fg" z końca echo ${x#"abc"} ${x%"efg"} echo ${x#"ac"} ${x%"eg"} # w napisach do obcięcia możliwe jest stosowanie shellowych # znaków uogólniających, czyli *, ?, [abc], itd # operator # i % dopasowują minimalny napis do usunięcia # operatory ## i %% dopasowują maksymalny napis do usunięcia x=abcd.e.fg echo ${x#*.} ${x##*.} ${x%.*} ${x%%.*} # ${str/"n1"/"n2"} zwróci $str z zastąpionym # pierwszym wystąpieniem n1 przez n2 # ${str//"n1"/"n2"} zwróci $str z zastąpionymi # wszystkimi wystąpieniami n1 przez n2 y="aa bb cc bb dd bb ee" echo ${y/"bb"/"XX"} echo ${y//"bb"/"XX"} # polecenie expr match $x 'wr1\(wr2\)wr3' # zwróci część $x pasującą do wyrażenia regularnego wr2 # wyrażenia regularne wr1 i wr2 pozwalają na # określanie części napisu do odrzucenia # alternatywną składnią jest expr $x : 'wr1\(wr2\)wr3' z="ab=cd" expr match $z '^\([^=]*\)=' expr $z : '^[^=]*=\(.*\)$' # możliwe jest też sprawdzanie dopasowań wyrażeń # regularnych poprzez (uwaga na brak cytowania): [[ "$z" =~ ^([^=]*)= ]] && echo "OK" # wypisywanie w różnych systemach liczbowych printf "0o%o %d 0x%x\n" 0xf 010 3 # do bardziej zaawansowanych operacji # mogą być przydatne także polecenia: # grep diff sed awk # join comm paste # należy też pamiętać o różnicy w działaniu echo "$a" i # echo $a w przypadku gdy zmienna a zawiera znaki nowej linii: # - w pierwszym wypadku będą traktowane jako znaki nowej linii # - w drugim jako spacje
# obsługa napisów w bash'u przy pomocy standardowych komend POSIXa # jako że większość operacji bashowych wiąże się z # uruchamianiem zewnętrznych programów to także # przetwarzanie napisów może być realizowane w ten sposób a="aąbcć 123" # obliczanie długości napisu w znakach, w bajtach i ilości słów w napisie echo -n $a | wc -m echo -n $a | wc -c echo -n $a | wc -w # obliczanie ilości linii (dokładniej ilości znaków nowej linii) wc -l < /etc/passwd # wypisanie 5 pola (rozdzielanego :) z pliku /etc/passwd z eliminacją # pustych linii oraz linii złożonych tylko ze spacji i przecinków cut -f5 -d: /etc/passwd | grep -v '^[ ,]*$' # komenda cut wybiera wskazane pola, opcja -d określa separator # alternatywne podejście z użyciem AWK awk -F: '$5 !~ "^[ ,]*$" {print $5}' /etc/passwd # awk daje duże możliwości przy przetwarzaniu tego typu tekstowych baz # danych ... możemy np. wypisywać wypisywać pierwsze pole w oparciu # o warunki nałożone na inne: awk -F: '$5 !~ "^[ ,]*$" && $3 >= 1000 {print $1}' /etc/passwd # jak widać w powyższych przykładach do poszczególnych pól odwołujemy # się poprzez $n, gdzie n jest numerem pola, $0 oznacza cały rekord # program dla każdego rekordu przetwarza kolejne instrukcje postaci # "warunek { komendy }", instrukcji takich może być wiele w programie # (przetwarzane są kolejno) komenda "next" kończy przetwarzanie danego rekordu # separator pola ustawiamy opcją -F (lub zmienną FS) domyślnym separatorem # pola jest dowolny ciąg spacji i tabulatorów (w odróżnieniu od cut # separator może być wieloznakowym napisem lub wyrażeniem regularnym) # domyślnym separatorem rekordu jest znak nowej linii # (można go zmienić zmienną RS) # awk jest prostym językiem programowania obsługującym podstawowe pętle # i instrukcje warunkowe oraz funkcje wyszukujące i modyfikujące napisy echo "aba aab bab baa bba bba" | awk '{ for (i=1; i<=NF; ++i) { # dla każdego pola w rekordzie if(i%2==0) # jeżeli jego numer jest parzysty gsub("b+", "B", $i); # zastąp wszystkie ciągi b pojedynczym B ii = index($i, "B") # wyszukaj pozycję pod-napisu B if (ii) # jeżeli znalazł to wypisz pozycję i pod-napis printf("# %d %s\n", ii, substr($i, ii)) # od tej pozycji do końca } print $0 }' # AWK obsługuje także tablice asocjacyjne pozwala to np. policzyć # powtórzenia słów echo "aa bb aa ee dd aa dd" | awk ' BEGIN {RS="[ \t\n]+"; FS=""} {slowa[$0]++} {printf("rekord: %d done\n", NR)} END {for (s in slowa) printf("%s: %s\n", s, slowa[s])} ' # podobny efekt możemy uzyskać stosując "uniq -c" (który wypisuje unikalne # wiersze wraz z ich ilością) na odpowiednio przygotowanym napisie # (spacje zastąpione nową linią, a linie posortowane) echo "aa bb aa ee dd aa dd" | tr ' ' '\n' | sort | uniq -c # jednak rozwiązanie awk można łatwo zmodyfikować aby wypisywało pierwsze # wystąpienie linii bez sortowania pliku # inną bardzo przydatną komendą jest sed pozwala ona m.in na zastępowanie # wyszukiwanego na podstawie wyrażenia regularnego tekstu innym echo "aa bb cc bb dd bb ee" | sed -e 's#\([bc]\+\) \([bc]\+\)#X-\2-X#g' # sedowe polecenie s przyjmuje 3 argumenty (oddzielane mogą być dowolnym # znakiem który wystąpi za s), pierwszy to wyszukiwane wyrażenie, drugi # tekst którym ma zostać zastąpione, a trzeci gdy jest g to powoduje # zastępowanie wszystkich wystąpień a nie tylko pierwszego # należy zwrócić uwagę na różnicę w składni wyrażenia regularnego polegającą # na poprzedzaniu (, ) i + odwrotnym ukośnikiem aby MIAŁY znaczenie specjalne # sed z opcją -i i wskazaniem pliku modyfikuje zawartości tego pliku # pozwala to na łatwe stworzenie funkcji rekurencyjnego zastępowania: rreplace() { if [ $# -ne 2 ]; then echo USAGE: $1 str1 str2 return fi grep -R "$1" . | cut -f 1 -d: | uniq | while read f; do [ -L $f ] || sed -e "s#$1#$2#g" -i $f; done; }
var x = "abcdefg"; var y = "aa bb cc bb dd bb ee"; var z = "qw=rt"; // podstawianie wartości zmiennych w napisie console.log(`x=${x} y=${y} z=${z}\n`) function echo(a) { // łączenie napisów (lub napisu i czegoś // co można skonwertować na napis) // z użyciem operatora + console.log(a + "\n"); } // wypisanie długości napisu echo(x.length); // wypisanie pod-napisu od 2 do końca echo(x.substr(2)); // i od 0 (początku) 3 kolejne znaki echo(x.substr(0, 3)); // wyszukiwanie // pod-napisu "bb" w $y od pozycji 5 echo(y.indexOf("bb", 5)); // porównywanie if (x == "abcdefg") echo("x == abcdefg"); if (x.search(/[dz]/) > 0) { echo("x zawiera d lub z"); } // zastępowanie echo(y.replace("bb", "BB")); echo(y.replace(/[bc]+/g, "XX")); // zastępowanie z użyciem podstawienia // $2 zostanie zastąpione wartością // pierwszego pod-wyrażenia, // czyli fragmentu ujętego w nawiasach console.log( z.replace(/^([^=]*)=.*$/, '$1') + " = " + z.replace(/^[^=]*=(.*)$/, '$1') + "\n" ); var a = 7, b = 0xf, c = 010, d = 0b11; echo("0b" + a.toString(2)) echo("0o" + b.toString(8)) echo(c.toString()) echo("0x" + d.toString(16))
<b>aa</b>
), znaczniki bez wartości mogą być samo-zamykające (np. <g />
).
Dokumenty HTML mogą być zgodne z wymogami formalnymi XML tym samym stanowiąc dokumenty XML.// wymaga pobrania biblioteki nagłówkowej rapidxml (http://rapidxml.sourceforge.net/) #include "rapidxml.hpp" namespace rapidxml { namespace internal { // fix bug in rapidxml https://sourceforge.net/p/rapidxml/bugs/16/ template<class V, class C> V print_children(V, const xml_node<C>*, int, int); template<class V, class C> V print_element_node(V, const xml_node<C>*, int, int); template<class V, class C> V print_data_node(V, const xml_node<C>*, int, int); template<class V, class C> V print_cdata_node(V, const xml_node<C>*, int, int); template<class V, class C> V print_declaration_node(V, const xml_node<C>*, int, int); template<class V, class C> V print_comment_node(V, const xml_node<C>*, int, int); template<class V, class C> V print_doctype_node(V, const xml_node<C>*, int, int); template<class V, class C> V print_pi_node(V, const xml_node<C>*, int, int); }} #include "rapidxml_print.hpp" #include <iostream> #include <string.h> int main() { char xmlString[1024]; strncpy(xmlString, "<a>" "<b>A<h>qwe ... rty</h></b>" "ABCD...HIJ..." "<c x=\"q\" w=\"p p\">EE F</c>" "<g y=\"zz\" />" "<c x=\"pp\">123 <d rr=\"oo\">456</d> 78 90.</c>" "</a>", 1024 ); // utworzenie obiektu drzewa XMLowego w oparciu o napis rapidxml::xml_document<> xmlDoc; xmlDoc.parse<0>(xmlString); // pobranie głównego węzła rapidxml::xml_node<>* xmlRoot = xmlDoc.first_node(); std::cout << "nazwa głównego elementu to: " << xmlRoot->name() << "\n"; std::cout << "jego zawartość tekstowa to: " << xmlRoot->value() << "\n"; std::cout << "jego wartość to: {{{{" << *xmlRoot << "}}}}\n"; std::cout << "jego potomkowie to: \n"; rapidxml::xml_node<>* xmlNode = xmlRoot->first_node(); while(xmlNode) { std::cout << " " << xmlNode->name() << " : "; rapidxml::print( std::cout, *xmlNode, rapidxml::print_no_indenting ); // rapidxml::print() może zapisywać także do napisu std::cout << "\n"; xmlNode = xmlNode->next_sibling(); } std::cout << "pierwszy węzeł c ma atrybuty:\n"; xmlNode = xmlRoot->first_node("c"); if(xmlNode) { rapidxml::xml_attribute<>* xmlAtrib = xmlNode->first_attribute(); while(xmlAtrib) { std::cout << xmlAtrib->name() << " = " << xmlAtrib->value() << "\n"; xmlAtrib = xmlAtrib->next_attribute(); } } // modyfikacje dokumentu: // zmiana nazwy i zawartości elementu xmlNode = xmlRoot->first_node("g"); if (xmlNode) { xmlNode->name("noweGG"); xmlNode->value("!@#$"); } // zmiana nazwy i wartości atrybutu xmlNode = xmlRoot->first_node("c"); if(xmlNode) { rapidxml::xml_attribute<>* xmlAtrib = xmlNode->first_attribute(); while(xmlAtrib) { if (xmlAtrib->name() == std::string("w")) { xmlAtrib->name("uu"); xmlAtrib->value("1 2 3"); break; } xmlAtrib = xmlAtrib->next_attribute(); } } // usuwanie wszystkich potomków ostatniego <c> xmlNode = xmlRoot->last_node("c"); xmlNode->remove_all_nodes(); xmlNode->value(""); // usuwanie wszystkich atrybutów ... xmlNode->remove_all_attributes(); // są też funkcje usuwające wskazanego potomka lub atrybut: // remove_node() i remove_attribute() // dodawanie atrybutów xmlNode->append_attribute( xmlDoc.allocate_attribute("abc", "098") ); xmlNode->append_attribute( xmlDoc.allocate_attribute("qwe", "...") ); // powyższa metoda dodaje na koniec, można także dodawać na początek: // prepend_attribute() lub na wskazaną pozycję insert_attribute() // dodawanie potomków rapidxml::xml_node<>* xmlNode2; xmlNode2 = xmlDoc.allocate_node(rapidxml::node_data, NULL); xmlNode2->value("ert"); xmlNode->append_node(xmlNode2); xmlNode2 = xmlDoc.allocate_node(rapidxml::node_element, "kk"); xmlNode2->value("uio"); xmlNode->append_node(xmlNode2); xmlNode2 = xmlDoc.allocate_node(rapidxml::node_data, NULL); xmlNode2->value("bnm"); xmlNode->append_node(xmlNode2); // podobnie jak atrybuty nody też możemy dodawać na początku // prepend_node() lub na dowolnej pozycji insert_node() // wypisanie zmienionego dokumentu std::cout << xmlDoc; }
import xml.etree.ElementTree as xml # Python oferuje także inne niz ElementTree moduły do obsługi XML # także wspierające DOM d = """<a> <b>A<h>qwe ... rty</h></b> ABCD... &' HIJ... <c x="q" w="p p">EE FĄ</c> <g y="zz" /> <c x="pp">123 <d rr="oo">456</d> 78 90.</c> </a>""" rootNode = xml.fromstring(d) # pobieranie informacji z dokumentu print("nazwa głównego elementu to:", rootNode.tag) print("jego zawartość tekstowa to:", rootNode.text) print("jego pełna zawartość tekstowa to:", "".join(rootNode.itertext())) print("jego wartość to: {{{{", xml.tostring(rootNode, encoding="unicode") ,"}}}}") print("jego potomkowie to:") for c in rootNode: print(" ", c.tag, ":", xml.tostring(c, encoding="unicode")) print("pierwszy węzeł c ma atrybuty:") try: ci = rootNode.iter("c") print(next(ci).attrib) except StopIteration: print(" [brak takiego węzła]") # modyfikacje dokumentu # zmiana nazwy i zawartości elementu try: ci = rootNode.iter("g") cc = next(ci); cc.tag = "noweGG" cc.text = "!@#$" except StopIteration: pass # zmiana nazwy i wartości atrybutu try: ci = rootNode.iter("c") cc = next(ci); del cc.attrib["w"] cc.attrib["uu"] = "1 2 3" except StopIteration: pass try: cc = next(ci); # usuwanie wszystkich potomków drugiego <c> cc.clear() # jest też remove() które usuwa wskazany element # usuwanie wszystkich atrybutów ... cc.attrib.clear() se = xml.SubElement(cc, "kk", attrib={"aa":str(45)}) se.text = "uio" se = xml.SubElement(cc, None) se.text = "bnm" # możliwe jest też wstawienie w podanym miejscu se = xml.Element(None) se.text = "ert" cc.insert(0, se) except StopIteration: pass print(xml.tostring(rootNode, encoding="unicode"))
<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="pl"> <head> <meta http-equiv="ContentType" content="application/xhtml+xml; charset=utf-8" /> </head><body> <div id="xyz"> <p> <b id="abc">Lorem <i>ipsum</i></b> dolor sit amet, <i>consectetur</i> adipiscing elit. <ul class="typA typX"> <li>Sed congue, eros quis ultricies ornare,</li> <li>massa sem auctor arcu, et semper ex arcu id augue.</li> <li>Fusce pretium <i><b>turpis</b></i> massa, maximus <i>dapibus</i> sit amet.</li> <li>Nulla fermentum molestie finibus.</li> </ul>Etiam accumsan <i>tempus</i> ante at congue.</p> <p data-abc="123" data-zz="0" class="az" id="q23y"> Sed feugiat vestibulum sapien eget iaculis.<br /> Integer quis magna nec lacus tempor sagittis. </p> </div> <script type="text/javascript">//<![CDATA[ function fff() { console.log("wszystkie elementy <i> w dokumencie:"); var a = document.getElementsByTagName("i"); for (var j = 0; j < a.length; j++) { console.log(a[j].innerHTML + "\n"); } // modyfikacja zawartości elementu o id="abc" var b = document.querySelector("#abc"); b.innerHTML = "ABC <u>ABC</u> ABC"; // potomkowie rodzica wskazanego elementu var child = b.parentNode.firstElementChild while (child) { console.log("potomek jest węzłem: " + child.nodeName + "\n"); if (child.nodeName == "ul") { // jeżeli jest to lista <ul> // która posiada (wśród klass podanych w atrybucie class) klasę "typA" // to ją usuń i dodaj klasę "typB" if (child.classList.contains('typA')) { child.classList.remove('typA'); child.classList.add('typB'); } } child = child.nextElementSibling; } var c = document.querySelector("#xyz"); // drugi potomek typu <p> elementu określonego w zmiennej c var d = c.querySelector("p:nth-of-type(2)"); console.log("ma atrybuty:") for (var i = 0; i < d.attributes.length; ++i) { console.log(" " + d.attributes[i].name + " = " + d.attributes[i].value + "\n"); } // można też pytać o konkretny atrybut console.log("abc => " + d.getAttribute("data-abc")); // oraz ustawić (zarówno dodać nowy jak i zmienić istniejący) d.setAttribute("data-def", "tyui"); } addEventListener('DOMContentLoaded', fff, false); //]]></script> </body></html>
import json from pprint import pprint a='''{ "info": "bbb", "ver": 31, "d": [ {"a": 21, "b": {"x": 1, "y": 2}, "c": [9, 8, 7]}, {"a": 17, "b": {"x": 6, "y": 7}, "c": [6, 5, 4]} ] }''' # interpretacja napisu jako zbioru danych w formacie json d = json.loads(a) # wypisanie zbioru danych pprint(d) # pprint ładnie formatuje złożone zbiory danych # jak widać jest to zagnieżdżona struktura list i słowników # odpowiadająca 1 do 1 temu co było w napisie # dostęp do poszczególnych elementów: "po pythonowemu" print(d["d"][1]["b"]) print(d["d"][1]["b"]["x"]) print(d["d"][1]["c"][1]) # przygotowaniem nowego zbioru danych b={'aa': 'pp', 'b': [3,4,5], 'c':d} b['e']="test" b['f']={'a':1, 'b':2} # wygenerowanie json-owego tekstu w oparciu o niego c=json.dumps(b,indent=1,ensure_ascii=False) print(c) # dla porównania: pprint(b)
# dla kodu pythonowego a="""print("ppp") b=13-17 """ exec(a) print(b) # dla wyrażeń a="9+7" b = eval(a) print(b) # lub exec("c="+a) print(c) # oczywiście napis może pochodzić z # dowolnego źródła, może być też # treścią całego skryptu pythonowego: # exec(open("plik.py").read())
# dla kodu bashowego a='echo "ppp"; b="ooo"' eval $a echo $b # dla obliczeń arytmetycznych a="13+17" b=$(( $a )) echo $b # alternatywnie przez # zewnętrzny kalkulator np.: b=`echo $a | bc` echo $b
<?php $a='echo "aaa"; $b="cc";'; eval($a); echo "\n" . $b . "\n"; $a='2+6'; eval('$b='. $a .';'); echo $b . "\n"; ?>
// dla kodu javascript a="console.log('AAA'); b='www';" eval(a); console.log("b to: " + b + "\n"); // dla wyrażeń a="5+7" b=eval(a) console.log("b to: " + b + "\n");
// Już w powyższych przykładach użycia list i map w C++ // wykorzystane były iteratory, pozwalające na // pobieranie kolejnych wartości z tych kontenerów: void wypiszListe1(std::list<int> l) { for (std::list<int>::iterator i = l.begin(); i != l.end(); ++i) { std::cout << *i << "\n"; } } // Iterator zwracają niektóre z metod tych kontenerów, // np. .begin() zwraca iterator na pierwszy element. // Zwiększanie iteratora odbywa się z użyciem operatora ++ // Wyjście poza zakres (zwiększenie iteratora wskazującego na // ostatni element kolekcji) nie powoduje rzucenia wyjątku, // za to iterator przyjmuje specjalną wartość oznaczającą koniec. // Iterator o tej wartości zwracany jest przez metodę .end() // (lub \cpp{.rend()} przy iterowaniu w przeciwną stronę). // Przy używaniu iteratorów w C++ wygodne jest korzystanie // z typu auto. Typ ten zwalnia programistę z konieczności // jawnego definiowania typu zmiennej do której przypisywana // jest od razu jakaś wartość z określonym typem. // Można napisać np. `auto x = 5;`, ale nie możemy napisać: // `auto x; x = 5;` void wypiszListe2(std::list<int> l) { for (auto i = l.begin(); i != l.end(); ++i) { std::cout << *i << "\n"; } } // C++ udostępnia także inną składnię pętli for pozwalającą // na iterowanie po wszystkich elementach kolekcji takich jak // listy, mapy, itp. I upraszczającą powyższy zapis do postaci: void wypiszListe3(std::list<int> l) { for (auto i : l) { std::cout << i << "\n"; } } // Zamiast `auto i` można napisać `auto& i` aby otrzymać dostęp // przez referencję (wtedy wykonanie przypisania wartości do i, // np. `i = 0`, spowoduje modyfikację elementu listy). // Warto zauważyć także, że w odróżnieniu od wcześniejszej pętli // zmienna reprezentuje wyłuskany iterator (jest to wartość / // referencja do wartości elementu a nie sam iterator).
# niekiedy zamiast tworzenia listy lepsze może być # uzyskiwanie jej kolejnych elementów "na żywo" # funkcjonalność taką w pythonie zapewniają generatory: def f(l): a, b = 0, 1 for i in range(l): r, a, b = a, b, a + b yield r # użycie generatora w pętli for for i in f(16): print(i) # można także tworzyć generatory nieskończone def ff(): a, b = 0, 1 while True: r, a, b = a, b, a + b yield r # pobieranie kolejnych elementów a = iter( ff() ) print( next(a) ) print( next(a) )
grep in.txt | sort > out.txt
(grep wybiera linie spełniające kryteria, a sort sortuje wynik)#include <stdio.h> int main() { // // wyjście / wyjście w stylu C // // wykorzystywane już wcześniej funkcje // puts i printf korzystają z stdout puts("Hello world"); printf("0x%x == %d == %.3f\n", 13, 13, 13.0); // jeżeli chcemy korzystać z innego strumienia // należy użyć wariantu pozwalającego na podanie // pliku do którego ma się odbywać zapis: // stdout - standardowe wyjście // stderr - standardowe wyjście błędu fputs("Hello world 2", stderr); fprintf(stderr, "0x%x == %d == %.3f\n", 13, 13, 13.0); // wczytanie napisu z standardowego wejścia: char napis[10]; fgets( napis, 10, stdin ); // wczytany napis będzie miał 9 znaków + NULL-end // zapominamy o reszcie inputu jeżeli był dłuższy niż 9 znaków if (napis[8] !='\n') { int c; while((c = getchar()) != '\n' && c != EOF); } // wczytanie liczby z standardowego wejścia: int d; fscanf (stdin, "%i", &d); fprintf(stdout, "liczba: %d napis: \"%s\"\n", d, napis); }
#include <iostream> #include <limits> #include <iomanip> int main() { // // wyjście / wyjście w stylu C++ // // standardowe wyjście i standardowe // wyjście błędu jako strumienie C++ std::cout << "Hello C++ world\n"; std::cerr << "Hello C++ world 2\n"; // strumienie pozwalają na proste wypisywanie zmiennych, // ale jeżeli chcemy formatować wyjście to // pisania jest więcej niż w printf int d = 1363; std::cout << "d=" << d << " d/3=" << d/3.0 << "\n"; std::cout << std::setprecision(3) << 1.2342; std::cout << " " << 23.567 << "\n"; // wczytanie napisu z standardowego wejścia: char napis[10]; std::cin.getline(napis, 10); if (std::cin.fail()) { // zapominamy o reszcie jeżeli była std::cin.clear(); std::cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' ); } // wczytanie liczby z standardowego wejścia: std::cin >> d; std::cout << "liczba to: " << d; std::cout << " napis to: \"" << napis << "\"\n"; }
import sys # wypisywanie na standardowe wyjście print("ABC: " + str(12) + " = " + hex(12)) # bez nowej linii na końcu print("abc", end='') # wypisywanie na standardowe wyjście błędu print("QWE", file=sys.stderr) # odczyt z wejścia li = iter(sys.stdin) liniaA = next(li) liniaB = next(li) print("l1: " + liniaA + "l2: " + liniaB) # można też z użyciem metody czytającej jedną linię liniaA = sys.stdin.readline() liniaB = sys.stdin.readline() print("l1: " + liniaA + "l2: " + liniaB) # należy zauważyć że dane wczytywane są # liniami i zawierają znak końca linii # można także w ramach: # for l in sys.stdin: # # lub skorzystać z metody wczytującej cały plik # jako listę poszczególnych jego linii: # ll = sys.stdin.readlines() # bądź po prostu jako jeden napis # napis = sys.stdin.read() # jest też: info = input("Wpisz coś: ") print(info)
f1() { # wypisywanie na standardowe wyjście echo "Hello World" printf "%d %.3f\n" 123 13.15686 # wypisywanie na standardowe wyjście błędu echo "Hello World 2" > /dev/stderr echo "ABC" > /dev/stderr } f2() { # odczyt ze standardowego wejścia # read wczytuje dane z stdin do podanych zamiennych # w taki sposób że do kolejnych zmiennych trafiają # kolejne słowa (napisy rozdzielane spacją lub # tabulatorem), a do ostatniej zmiennej reszta napisu # (do końca linii bez znaku końca linii) while read a b; do echo $(($a+$b)) done } # przekierowania strumieni standardowych: # wyjście z echo przekierowywane jest na wejście f2 echo -e "2 3\n1 6" | f2 # wyjścia f1 do odpowiednich plików f1 > /tmp/out.txt 2> /tmp/err.txt # użycie >> zamiast > spowoduje dopisywanie do pliku # zamiast nadpisywania jego zawartości # połączonych wyjść (normalnego i błędu) f1 do grep f1 |& grep -v Hello # standardowe wyjście może zostać przechwycone i # podstawione w danym miejscu poprzez użycie # `polecenie` lub $(polecenie) echo XXX `ls -ld /tmp` echo XXX $(ls -ld /tmp) # często też chcemy zignorować standardowe wyjście i/lub # standardowe wejście - możemy to uzyskać przekierowując # je do /dev/null np: grep '^root:' /etc/passwd > /dev/null # przekierowanie wyjścia cat do pliku /tmp/liczby # << EOF powoduje że bash podaje na standardowe # wejście komendy (w tym wypadku "cat") dane czytane # z skryptu (lub swojego wejścia) dopóki nie wystąpi # w nowej linii słowo podane po << (w tym # wypadku EOF), jeżeli słowo to jest ujęte w '' to # w przekazywanym tekście nie są dokonywane podstawienia # shellowe (np. rozwijane zmienne) cat << EOF > /tmp/liczby 1 3 13 9 9 4 7 10 EOF # przekierowanie pliku /tmp/liczby # na standardowe wejście f2 f2 < /tmp/liczby
ze względu na specyfikę tego języka wszystko co jest poza znacznikami php będzie wypisywane na standardowe wyjście <?php echo "są także funkcje "; print("realizujące wypisywanie\n"); printf("%d %.3f\n", 13, 45.33221); ?>
#include <stdio.h> int main() { // // obsługa plików w stylu C // // otwieramy plik określony w pierwszym argumencie, // w trybie określonym w drugim argumencie: // r - odczyt, w - zapis, a - dopisywanie, // + - dwukierunkowy (używane po r, w albo a) FILE *plik = fopen("/tmp/plik1.txt", "w+"); // zapisujemy do pliku fputs("Hello World !!!\n", plik); fprintf(plik, "%.3f\n", 13.13131); // jako że są to operacje buforowane to aby mieć // pewność że to jest już w pliku należy wykonać // fflush(), nie jest to konieczne gdy zamykamy // plik (wtedy wykonywane jest z automatu) fflush(plik); int poz = ftell(plik); printf("aktualna pozycja w pliku to %d\n", poz); // przewijamy do początku // jest to równoważne rewind(plik); fseek(plik, 0, SEEK_SET); // wczytywanie z pliku char napis[10]; fgets(napis, 10, plik); // wczytany napis będzie miał 9 znaków + NULL-end puts(napis); // powrot do poprzedniej pozycji fseek(plik, poz, SEEK_SET); // operacje binarne - w ten sposób możemy zapisywać // i odczytywać całe bufory z pamięci, czyli także napisy // zapis do pliku double x = 731.54112, y = 12.2; fwrite(&x, 1, sizeof(double), plik); fflush(plik); // przesuniecie do pozycji na której zapisywaliśmy fseek(plik, poz, SEEK_SET); // i odczyt z pliku ... fread(&y, 1, sizeof(double), plik); printf("zapisano: %f, odczytano: %f\n", x, y); // są także funkcje read() i write() działające w oparciu o // numeryczny deskryptor pliku uzyskiwany np. z funkcji open() // a nie obiekt FILE uzyskiwany z fopen() fclose(plik); }
#include <iostream> #include <fstream> int main() { // // obsługa plików w stylu C++ // // tworzymy strumień związany z plikiem std::fstream plik_w; // otwieramy plik w trybie dopisywania // ale pozwalającym na modyfikowanie obecnej treści // aby to działało musi być to strumień in/out plik_w.open( "/tmp/plik2.txt", std::fstream::ate|std::fstream::out|std::fstream::in ); // tworzymy strumień (typu in) związany z plikiem ... std::ifstream plik_r; // otwieramy plik do czytania plik_r.open("/tmp/plik1.txt"); // tryby możemy budować z następujących opcji: // std::ios::in - odczyt (domyślna dla ifstream) // std::ios::out - zapis (domyślna dla ofstream) // std::ios::ate - ustawie pozycji na koniec pliku // std::ios::app - dopisywanie bez możliwości zmiany zawartości // std::ios::trunc - nadpisuje plik std::cout << "Pozycja w pliku OUT: " << plik_w.tellp() << std::endl; std::cout << "Pozycja w pliku IN: " << plik_r.tellg() << std::endl; // pisanie i czytanie dokładnie jak dla cin cout ... plik_w << "Hello World !!!"; // przesunięcie na 16 bajt bliku wejściowego plik_r.seekg(16); // i wczytanie czegoś double x; plik_r >> x; std::cout << "wczytano: " << x << "\n"; // przesunięcie o 2 bajty do tyłu i wczytanie plik_r.seekg(-2, std::ios::cur); // inne tryby przesunięć to: // ios::beg - od początku pliku (domyślny) // ios::end - od końca pliku plik_r >> x; std::cout << "wczytano: " << x << "\n"; plik_w.seekp(-3, std::ios::end); plik_w << 123 << "\n"; // nma koniec zamykamy pliki plik_w.close(); plik_r.close(); }
import os.path # otwarcie pliku do odczytu f=open("/etc/passwd", "r") # funkcja pozwala na określenie kodowania pliku poprzez # argument nazwany "encoding" (np. encoding='utf8'), # domyślne kodowanie zależne jest od ustawień systemowych # można je sprawdzić poprzez locale.getpreferredencoding() # # jeżeli plik ma być otwarty w trybie binarnym a nie # tekstowym konieczne jest podanie flagi b w ramach # drugiego argumentu # odczyt po linii l1 = f.readline() l2 = f.readline() # można także czytać z jawnym użyciem iteratorów: li = iter(f) l3 = next(li) l4 = next(li) print("l1: " + l1 + "l2: " + l2 + "l3: " + l3 + "l4: " + l4) # albo w ramach pętli i = 5 for l in f: print(str(i) + ": " + l) i += 1 # powrót na początek pliku f.seek(0) # odczyt jako tablica linii ll = f.readlines() print(ll) # i kolejny raz ... jako jednolity tekst f.seek(0) print( f.read() ) f.close() # jeżeli plik istnieje to: if os.path.isfile("/tmp/plik3.txt"): # otwieramy w trybie do zapisu i odczytu # i ustawiamy się na końcu pliku celem dopisywania f=open("/tmp/plik3.txt", "r+") f.seek(0, 2) else: f=open("/tmp/plik3.txt", "w") # pobieramy aktualną pozycje w pliku # (która w tym wypadku jest równa długości pliku) pos = f.tell() # jeżeli plik ma więcej niż 5 bajtów if pos > 5: # to cofamy się o 3 f.seek(pos-3) f.write("0123456789") f.close() # obsługa plików binarnych # wymagane jest dodanie flagi b w flagach funkcji open(): f=open("/tmp/plik1.txt", "rb") # czytanie baj po bajcie while True: b = f.read(1) if b == b"": break print(b) f.close()
import sys, os, select rdfd, _, _ = select.select([sys.stdin], [], [], 3.0) # select() przyjmuje 3 listy deskryptorów plików # (czyli numerycznego identyfikatora otwartego pliku, # zwracanego np. przez funkcję open()) oraz ilość sekund, # którą ma czekać na początek danych. # Pierwsza lista związana jest z plikami z których chcemy # czytać, druga pisać, a trzecia z plikami na których # czekamy na wyjątkowe warunki. # Funkcja ta kończy działanie gdy pojawią się jakiekolwiek dane # (nie czeka na koniec danych – EOF) i zwraca również 3 takie listy, # ale zawierające jedynie deskryptory plików na których pożądana # operacja jest możliwa (np. są dane do wczytania, można zapisać dane). if not rdfd: print("czas minął") for fd in rdfd: print("czytam z:", fd) a = os.read(fd.fileno(), 1024) # Do odczytu zastosowana została funkcja os.read() a nie metoda fd.read(), # wynika to z faktu, iż fd.read() czeka na EOF lub podaną ilość bajtów, # a os.read() wczytuje to co jest dostępne i ogranicza jedynie maksymalną # ilość wczytywanych danych (resztę możemy doczytać kolejnym wywołaniem). print("wczytałem:", a)
// program korzysta z mechanizmów systemów POSIX // i może nie działać na niekompatybilnych platformach #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <stdio.h> #define _MMAP_MODE_ MAP_PRIVATE // MAP_SHARED // MAP_PRIVATE powoduje bez widoczności zmian w mapowanym pliku dla innych procesów // MAP_SHARED pozwala współdzielić zamapowany obszar z innymi procesami, // jest wymagane aby móc zmodyfikować plik z użyciem msync() int main() { const char* fname = "/tmp/aaa.txt"; int fsize; // pobieramy rozmiar pliku struct stat st; if (stat(fname, &st)) { if (errno == ENOENT) { printf("plik \"%s\" nie istnieje\n", fname); fsize = 0; } else { fprintf(stderr, "Error in stat(): %s\n", strerror(errno)); // powyższe zadziała jak perror() return -1; } } else { fsize = st.st_size; } // otwieramy plik w trybie zapis-odczyt uzyskując deskryptor // (jeżeli plik nie istnieje zostanie utworzony z prawami 600) int fd = open(fname, O_RDWR|O_CREAT, (mode_t)0600); int bufSize = fsize + 20; // jeżeli chcemy operować na większym buforze niż rozmiar pliku musimy powiększyć plik if (ftruncate(fd, bufSize)) perror("Error in ftruncate()"); // operacja ta nie ma wpływu na zajmowane miejsce na dysku: // int fd = open("/tmp/x", O_RDWR|O_CREAT); ftruncate(fd, 100000000); close(fd); // utworzy plik /tmp/x którego rozmiar wg `ls` będzie 98MB a wg `du` będzie 0 // alternatywnie można: // lseek(fd, bufSize-1, SEEK_SET); write(fd, "", 1); lseek(fd, 0, SEEK_SET) // mapujemy plik do pamięci ... w trybie odczytu i zapisu char *buf = (char*)mmap(NULL, bufSize, PROT_READ | PROT_WRITE, _MMAP_MODE_, fd, 0); if (buf == MAP_FAILED) { perror("Error in mmap()"); return -1; } buf[fsize]=0; printf("Długość pliku: %d\n", fsize); printf("Zawartość pliku: %s\n", buf); // modyfikujemy przedostatni bajt w pliku // (jeżeli trafimy na fragment wielo-bajtowego znaku to go popsujemy) if (fsize > 2) buf[fsize - 2]='X'; fsize += snprintf(buf+fsize, bufSize-fsize, "Ala ma kota\n\n"); fsize += snprintf(buf+fsize, bufSize-fsize, "Kot ma Alę\n\n"); fsize += snprintf(buf+fsize, bufSize-fsize, "czyżby ...\n\n"); // do pliku chcemy zapisać "fsize" bajtów z "buf", czyli: buf[0] ... buf[fsize-1] // nie chcemy zapisać kończącego napis znaku NULL (dodawanego przez snprintf), // jeżeli nie doszło do przepełnienia bufora nie jest on wliczany w długość "fsize" // i znajduje się w buf[fsize], zatem nie ma konieczności jawnego pomijania go // // jeżeli natomiast doszło do przepełnienia bufora jest on ostatnim znakiem w buforze, // czyli buf[bufSize-1] ... zatem aby go pominąć należy ustawić fsize = bufSize -1 if (fsize >= bufSize) { fprintf(stderr, "Error: przepełnienie bufora\n"); fsize = bufSize -1; } printf("Nowa długość pliku: %d\n", fsize); printf("Nowa zawartość pliku: %s\n", buf); // ustawiamy prawidłowy rozmiar pliku if (ftruncate(fd, fsize)) perror("Error in ftruncate()"); #if _MMAP_MODE_ == MAP_SHARED // synchronizujemy zawartość bufora pamięci do pliku if (msync(buf, fsize, MS_SYNC)) perror("Error in msync()"); #else // zapisujemy zmiany do pliku if (write(fd, buf, fsize) < 0) perror("Error in write()"); #endif // odmapowanie pliku z pamięci if (munmap(buf, fsize)) perror("Error in munmap()"); // zamknięcie pliku close(fd); }
// przy kompilacji konieczne jest dodanie: // -lboost_filesystem -lboost_system #include <iostream> #include <boost/filesystem.hpp> int main() { // sprawdzenie czy plik istnieje boost::filesystem::path pFile( "/tmp/abc.txt" ); if (boost::filesystem::is_regular_file(pFile)) std::cout << "istnieje\n"; else std::cout << "nie istnieje\n"; // listowanie katalogu i sprawdzanie typów boost::filesystem::path pDir( "/tmp/" ); boost::filesystem::directory_iterator end_iter; boost::filesystem::directory_iterator iter(pDir); for(; iter != end_iter; ++iter) { if (boost::filesystem::is_regular_file(iter->path())) { std::cout << iter->path().filename().generic_string() << " jest plikiem\n"; // wypisujemy tylko nazwę pliku } else if (boost::filesystem::is_directory(iter->path())) { std::cout << iter->path().generic_string() << " jest katalogiem\n"; // wypisuje,y pełną ścieżkę } else { std::cout << iter->path().generic_string() << " jest czymś innym\n"; } } boost::filesystem::rename("/tmp/a.txt", "/tmp/b.txt"); boost::filesystem::remove("/tmp/b.txt"); }
import os # sprawdzenie czy plik istnieje if os.path.isfile("/tmp/abc.txt"): print("istnieje") else: print("nie istnieje") # listowanie katalogu i sprawdzanie typów dl = os.listdir("/tmp/") print (dl) for f in dl: if os.path.isfile(f): print("\"" + f + "\" jest plikiem") elif os.path.isdir(f): print("\"" + f + "\" jest katalogiem") else: print("\"" + f + "\" jest czymś innym") # można także rekurencyjnie wraz z podkatalogami for currDir, dirs, files in os.walk('/tmp'): print("podkatalogi \"" + currDir + "\":") for d in dirs: print(" " + os.path.join(currDir, d)) print("pliki w \"" + currDir + "\":") for f in files: print(" " + os.path.join(currDir, f)) # zmiana nazwy os.rename("/tmp/a.txt" "/tmp/b.txt") # usuwanie os.remove("/tmp/b.txt")
# sprawdzenie czy plik istnieje if [ -f "/tmp/abc.txt" ]; then echo "istnieje" else echo "nie istnieje" fi # listowanie katalogu i sprawdzanie typów for f in /tmp/*; do if [ -f "$f" ]; then echo "\"$f\" jest plikiem" elif [ -d "$f" ]; then echo "\"$f\" jest katalogiem" else echo "\"$f\" jest czymś innym" # np. kolejką lub urządzeniem ... fi done # alternatywne listowanie katalogu ls /tmp | while read f; do echo "/tmp zawiera: $f" done # zmiana nazwy mv /tmp/a.txt /tmp/b.txt # usuwanie rm /tmp/b.txt
ls -la
to to samo co ls -l -a
), a opcji długich po dwóch myślnikach. Ewentualne argumenty opcji podawane są zaraz po opcji i w przypadku opcji krótkich oddzielone od niej spacją, a w przypadku opcji długich po znakiem równości (np. ls --color=auto
). Po opcjach występują argumenty pozycyjne (np. lista plików które ma wyświetlić ls).
// przy kompilacji konieczne jest dodanie: // -lboost_program_options #include <boost/program_options.hpp> #include <iostream> // pełna deklaracja funkcji main dla systemów POSIX // funkcja main otrzymuje: // - liczbę elementów tablicy argumentów // - tablicę argumentów (jest to tablica napisów typu C) // - tablicę zmiennych środowiskowych int main(int argc, char *argv[], char *envp[]) { std::cout << "program wywołano z " << argc << " argumentami\n"; for (int i=0; i<argc; ++i) std::cout << "argument " << i << " to: " << argv[i] << "\n"; // deklaracja opcji linii poleceń boost::program_options::options_description desc("Program options"); desc.add_options() ("help,h", "show help message") ("load", boost::program_options::value<std::string>(), "load from \"arg\" file") ; // parsowanie opcji linii poleceń wraz z obsługą wyjątków boost::program_options::variables_map vm; try { boost::program_options::store( boost::program_options::parse_command_line(argc, argv, desc), vm ); boost::program_options::notify(vm); } catch(boost::program_options::error& e) { std::cerr << "Cmdline args error: " << e.what() << "\n\n"; std::cerr << "Use --help to see full options description\n"; return 2; } // przetwarzanie otrzymanych opcji if (vm.count("help")) { std::cout << desc << "\n"; } else if (vm.count("load")) { std::cout << "Load from: " << vm["load"].as<std::string>() << "\n"; } else { std::cout << "Wywołano bez opcji\n"; } // jest też funkcja getopt_long() z biblioteki standardowej C // funkcja main powinna zwracać kod powrotu // typowo: gdy program zakończy się powodzeniem zero // a gdy zakończy się błędem (nie zerowy) kod błędu return 0; }
import argparse # parser agumentów linii poleceń # do argumentów można się też dostawać bezpośrednio poprzez sys.argv: # len(sys.argv) - ilość elementów tej tablicy # str(sys.argv[0]) - napis odpowiadający nazwie wywołania programu # str(sys.argv[1]) - napis odpowiadający pierwszemu argumentowi programu parser = argparse.ArgumentParser(description='Opis programu') parser.add_argument( 'ARG', default='ABC', nargs='?', help='argument pozycyjny (opcjonalny z wartością domyślną)' ) parser.add_argument( '-v', "--verbose", action="store_true", help='opcja typu przełącznik' ) parser.add_argument( "--input", action="store", help='opcja z argumentem' ) parser.add_argument( "--vector", action="store", metavar='V', nargs='*', help='opcja z wieloma argumentami' ) args = parser.parse_args() print(args)
# przetworzenie argumentów określonych w wywołaniu getopt # dalsze argumenty dostępne są w $1, $2, itd po zakończeniu pętli # : -> poprzedzająca opcja wymaga argumentu # :: -> poprzedzająca opcja może mieć argument # uwaga o ile wymagane argumenty mogą być rozdzielane od opcji spacją # o tyle opcjonalne nie (krótkie należy podawać zaraz po opcji, # długie rozdzielając =) eval set -- "`getopt -o xy:z:: -l opcja-x,opcja-y:,opcja-z:: -- "$@"`" while true; do case $1 in -x|--opcja-x) echo "X"; shift 1;; -y|--opcja-y) echo "Y, argument \`$2'"; shift 2;; -z|--opcja-z) echo "Z, argument \`$2'"; shift 2;; # tuataj $2 może być pusty (gdy nie podano), ale jest zdefiniowany --) shift; break;; esac done # inną komendą mogącą służyć przetwarzaniu opcji skryptu jest getopts
<?php /* możliwe jest używanie php w trybie command-line jednak ze względu na specyfikę i podstawowe zastosowania tego języka jest to przypadek dość egzotyczny dlatego też zamiast obsługi opcji przekazywanych z linii poleceń w przypadku skryptów php większy sens ma pokazanie obsługi opcji przekazywanych w żądaniu HTTP */ // wysłanie ciasteczka // musi być przed wysłaniem // jakiejkolwiek treści $val = time(); $val = "tmp $val"; setcookie("Ciasteczko", $val); echo '<pre>'; echo "wysłałem ciasteczko: $val\n"; // tablica zawierajaca zmienne // przekazane w adresie URL echo '$_GET: '; print_r($_GET); // tablica zawierajaca zmienne // przekazane wi treści żądania echo '$_POST: '; print_r($_POST); // tablica zawierajaca zmienne // przekazane w ciasteczkach echo '$_COOKIE: '; print_r($_COOKIE); echo '</pre>'; ?>
#include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #include <fcntl.h> #include <stdio.h> void getInput(int fd, int pid); int main() { // najprostszą metodą uruchomienia innego programu jest funkcja system int retcode = system("ls -l"); printf("Program uruchomiony przez system() zwrócił kod powrotu: %d\n", WEXITSTATUS(retcode) ); // jeżeli chcemy odebrać standardowe wyjście // (albo zapisać standardowe wejście) można skorzystać z popen FILE* p = popen("uname -a", "r"); puts("Proces wypisał na swój stdout:"); char buf[256]; size_t s; while ((s = fread(buf, sizeof(char), sizeof(buf), p))) { fwrite(buf, sizeof(char), s, stdout); } // tworzymy łącza nie nazwane - rury ... int toSys[2]; if ( pipe(toSys) == -1 ) { perror("Error in pipe(toSys)"); // perror wypisuje podany komunikat na stderr dodając do niego opis // błędu określonego przez errno, można też pobrać identyfikator błędu // lub jego opis - szczegóły w man 3 errno return 1; } // toSys[0] - odbiór z rury // toSys[1] - wysyłanie przez rurę int fromSys[2]; if ( pipe(fromSys) == -1 ) { perror("Error in pipe(fromSys)"); return 1; } // rozgałęziamy proces int pid; switch (pid = fork()) { case -1: { // funkcja zwróciła -1 co oznacza błąd perror("Error in fork"); return 1; } case 0: { // funkcja zwróciła zero co oznacza że jesteśmy w procesie potomnym // podmieniamy stdin i stdout potomka na odpowiednie // koniec łącz nienazwanych dup2(toSys[0], 0); dup2(fromSys[1], 1); // zamykamy końce łącz nienazwanych (te które używamy są już dostępne // jako stdin, stdout, innych nie potrzebujemy) close(toSys[0]); close(toSys[1]); close(fromSys[0]); close(fromSys[1]); // zastępujemy się innym procesem (w tym wypadku gerp dopasowujący napisy // zawierające znak z zakresu A-C) - w taki sposób w jaki robi to system() execl("/bin/sh", "sh", "-c", "grep --line-buffered '[A-C]'", (char *) 0); // oczywiście zamiast wywoływania innego programu można tutaj użyć własnego // kodu pracującego równolegle z programem głównym } default: { // funkcja zwróciła coś innego od 0 i -1 oznacza to że jesteśmy w procesie // macierzystym i otrzymaliśmy pid naszego dziecka ... // zamykamy niepotrzebne konce rur close(toSys[0]); close(fromSys[1]); // rury możemy obsługiwać jak pliki // i w ten spsób obsługiwana jest końcówka do zapisu FILE *pout = fdopen(toSys[1], "w"); fprintf(pout, "Ala ma kota\n"); fprintf(pout, "Kot ma coś\n"); fflush(pout); // można też je obsługiwać posługując się bezpośrednio deskryptorem // i tak obsługiwana jest rura do czytania // rurę do czytania ustawiamy w trybie nie blokującym fcntl(fromSys[0], F_SETFL, fcntl(fromSys[0], F_GETFL, 0) | O_NONBLOCK); // czytanie zostało wyniesione jest do osobnej funkcji getInput() // celem łatwiejszego jego powtarzania getInput(fromSys[0], pid); // potomek może potrzebować trochę czasu na przetworzenie danych sleep(1); getInput(fromSys[0], pid); // zamykamy nasz koniec rury do pisania // pozwoli to potomkowi czekającemu na input zakończyć się fclose(pout); // sprawdzamy czy jest jeszcze jakiś input // (konieczne np. gdy grep bez --line-buffered) sleep(1); getInput(fromSys[0], pid); // zamykamy rurę do czytania close(fromSys[0]); // czekamy na zakończenie potomka int ppid = wait(&retcode); printf("potomek o PID=%d zakończył się z kodem %d\n", ppid, WEXITSTATUS(retcode) ); } } } void getInput(int fd, int pid) { char* buf[256]; int len, gLen = 0; while ((len = read(fd, buf, 255)) > 0) { buf[len] = 0; gLen += len; printf("Odebrano od potomka %d:\n>>>>>\n%s\n<<<<<\n", pid, buf); } printf("Łącznie odebrano: %d\n", gLen); }
# napis który będzie podawany na standardowe wejście uruchamianego polecenia inStr = "Ala ma kota\nKot ma psa\n..." # Python pozwala na bezpośrednie stosowanie funkcji znanych z języka C # takich jak system(), popen(), fork(), execl() itd poprzez moduł os. # Odbywa się to w sposóba analogiczny do użycia w języku C. Na przykład: import os os.system('echo -en "' + inStr + '" | grep -v A') # Python zapewnia jednak także wygodny, zunifikowany sposób uruchamiania innych # programów / poleceń powłoki poprzez moduł subprocess oraz rozgałęziania # własnego procesu poprzez moduł multiprocessing. print(""" # # podstawy subprocess # """) import subprocess # uruchamiamy subprocess z grep'em res = subprocess.run(["grep", "-v", "A"], input=inStr.encode(), stdout=subprocess.PIPE) print("Kod powrotu to: " + str(res.returncode)) print("Standardowe wyjście z komendy to: " + res.stdout.decode()) # warto zwrócić uwagę na kodowanie i dekodowanie napisów # (przekazywanych/odbieranych przez stdin/stdout) do / z utf-8 # jeżeli chcemy korzystać np. z znaków uogólniających powłoki lub podać # komendę jako pojedynczy napis (a nie listę argumentów) to można użyć # opcji shell=True: subprocess.run(["ls -ld /etc/pa*"], shell=True) # jeżeli potrzebujemy tylko rozbicia napisu na listę argumentów można # użyć shlex.split() # run() pozwala także (obok subprocess.PIPE) na przekazywanie # istniejących deskryptorów (lub subprocess.DEVNULL, co ignoruje wyjście) # w ramach stdin, stdout, stderr # moduł subprocess oferuje także funkcję Popen() dającą większą kontrolę # nad uruchamianiem komendy print(""" # # podstawy multiprocessing # """) import multiprocessing, os, time def fun1(txt, st, cnt): for _ in range(cnt): print("Proces", os.getpid(), "z st =", st, " i argumentem:", txt) time.sleep(st) def fun2(c, e): # obsługa rury i zdarzenia c.send("Uwolnić mrożone truskawki") time.sleep(1.3) c.send([12, 13, { "ab" : 11, "cd" : "xx" }]) c.close() e.clear() if __name__ == '__main__': # przygotowanie potomków p1 = multiprocessing.Process(target=fun1, args=('Ala ma psa',1.5,4,)) p2 = multiprocessing.Process(target=fun1, args=('pies ma kota',1,3,)) print("mój pid to", os.getpid(), "... uruchamiam procesy") # uruchomienie potomków p1.start() p2.start() # czekanie na zakończenie p2 p2.join() print("p2 się zakończył") # wymuszenie zakończenia p1 jeżeli nadal żyje if p1.is_alive(): print("p1 żyje") p1.terminate() p1.join() print("p1 się zakończył") # rura i event # Pipe() tworzy nam parę obiektów multiprocessing.Connection # jednego będziemy używać w procesie głównym, a drugiego w potomnym pConn, cConn = multiprocessing.Pipe() # tworzymy obiekt typu Event ... jest to prosty sygnalizator # z flagą binarną zapewniający atomowość wykonywanych operacji evt = multiprocessing.Event() evt.set() # tworzymy i uruchamiamy kolejny podproces p1 = multiprocessing.Process(target=fun2, args=(cConn,evt,)) p1.start() # odbieramy dane z połączenia dopóki event jest ustawiony try: while evt.is_set(): if pConn.poll(0.1): print("otrzymałem: ", pConn.recv()) except EOFError: pass print(""" # # pool i bariera w multiprocessing # """) import multiprocessing, queue, os, time from threading import BrokenBarrierError def fun5Init(b): global bariera bariera = b def fun5(arg): for i in range(arg['n']): print("Wątek", arg['n'], " iter =", i) time.sleep(arg['ts']) try: print("Wątek", arg['n'], " czekam na innych") if bariera.wait() == 0: print("wątki zsynchronizowane") # więcej nie używaj tej bariery: bariera.abort() except BrokenBarrierError: print("czekanie anulowane") return arg['n'] + arg['ts'] if __name__ == '__main__': # tworzymy barierę i warunek b = multiprocessing.Barrier(3) # tworzymy pool'a ... w ramach inicjalizacji # przekazujemy barierę i warunek pool = multiprocessing.Pool( processes=3, initializer=fun5Init, initargs=(b,) ) # tworzymy listę argumentów dla funkcji # uruchamianej w procesach potomuych args = [ { 'n': 3, 'ts': 0.44 }, { 'n': 13, 'ts': 0.17 }, { 'n': 2, 'ts': 0.33 }, { 'n': 7, 'ts': 0.69 }, { 'n': 4, 'ts': 0.49 }, ] # tworzymy pulę 3 procesów potomnych i uruchamiamy w nich wskazaną # funkcję z kolejnymi zbiorami argumentów z podanej listy # uwaga: funkcja wywoływana przez map wraz z ewentualnymi zmiennymi # globalnymi musi być zdefiniowana przed wywołaniem Pool() results = pool.map(fun5, args) pool.close() pool.join()
pipe()
a otwarcia specjalnego pliku (utworzonego mkfifo()
) przez dwa procesy (jeden do czytania drugi do pisania).kill $PID
) i ma odpowiednie do tego zachowania domyślne. Proces może zablokować lub zdefiniować własną obsługę większości (ale nie wszystkich) sygnałów.
#include <stdio.h> #include <signal.h> #include <fcntl.h> #include <time.h> #include <unistd.h> #include <errno.h> // funkcje obsługi sygnału void obslugaSygnalu(int sig) { printf("Otrzymałem sygnał numer: %i\n", sig); } void akcjaSygnalu(int sig, siginfo_t *info, void *context) { printf("Otrzymałem sygnał numer: %i z kodem %d i wartością %d od pid=%d\n", sig, info->si_code, info->si_value.sival_int, info->si_pid); // si_code określa powód wysłania sygnału } int main() { int sig, pid; pid = getpid(); printf("Mój PID=%d\n", pid); // sygnały są asynchronicznym mechanizmem jednokierunkowej komunikacji // systemy POSIXowe oferują dwa rodzaje sygnałów - standardowe i czasu rzeczywistego // sygnały czasu rzeczywistego (w odróżnieniu od standardowych przekazujących jedynie // numer sygnału) zawierają także kod i wartość oraz są priorytetyzowane // (niższy numer => wyższy priorytet => dostarczany w pierwszej kolejności) // do zdefiniowania obsługi sygnału standardowego (do jego przechwycenia) służy funkcja: signal(SIGINT, &obslugaSygnalu); // pojawia się m.in. w efekcie Ctrl+C signal(SIGTSTP, &obslugaSygnalu); // pojawia się m.in. w efekcie Ctrl+Z // oczywiście można definiować różne funkcje dla różnych sygnałów // zamiast wskaźnika do funkcji możemy podać także SIG_IGN albo SIG_DFL // co oznacza odpowiednio ignorowanie tego sygnału lub jego domyślną obsługę // możliwe jest też maskowanie sygnałów sigset_t sigSet; // inicjujemy maskę sygnałów na pustą, na pełną byłoby sigfillset() if (sigemptyset(&sigSet)) perror("Error in sigemptyset()"); // dodajemy sygnał do maski // można także usuwać - sigdelset() i sprawdzać czy jest obecny - sigismember() // zamaskowany SIGTERM jest domyślnym sygnałem wysyłanym przez kill if (sigaddset(&sigSet, SIGTERM)) perror("Error in sigaddset()"); // ustawiamy maskę sygnałów na przygotowaną if (sigprocmask(SIG_SETMASK, &sigSet, NULL)) perror("Error in sigprocmask()"); // sygnały nie obsługiwane i nie blokowane przez proces obsługiwane są w sposób domyślny // na ogół prowadząc do zakończenia procesu ... sygnałów SIGKILL (wysyłanego przez kill -9) // oraz SIGSTOP nie można przechwycić ani zamaskować // do zdefiniowania obsługi sygnału czasu rzeczywistego służy funkcja sigaction wraz z strukturą // sigaction opisującą obsługę (można tak definiować także obsługę standardowych sygnałów): struct sigaction rtSigAct; // funkcja obsługi (moża także podać SIG_IGN albo SIG_DFL) rtSigAct.sa_sigaction = &akcjaSygnalu; // flagi ... SA_SIGINFO oznacza funkcję przyjmującą 3 argumenty a nie 1 jak w signal() rtSigAct.sa_flags = SA_SIGINFO; // maska sygnałów blokowanych w trakcie obsługi sygnału ... blokujemy wszystko co się da sigfillset(&rtSigAct.sa_mask); // ustawienie obsługi sigaction(SIGRTMIN, &rtSigAct, NULL); // wysłanie sygnału czasu rzeczywistego // (funkcja pozwala też na wysyłanie standardowych sygnałów) sigqueue(pid, SIGRTMIN, (const union sigval)123); // oczekiwanie na sygnał: puts("Czekam na dowolny sygnał"); pause(); // dodajemy SIGINT do zbioru if (sigaddset(&sigSet, SIGINT)) perror("Error in sigaddset()"); puts ("Czekam na sygnał z podanej maski"); puts (" inne będą obsługiwane ale funkcja nie zakończy się ..."); printf(" aby kontynuować Ctrl+C lub kill %d\n", pid); if (sigwait(&sigSet, &sig)) perror("Error in sigwait()"); printf("Czekanie zakończone po otrzymaniu sygnału: %d\n", sig); puts ("Śpię przez 10s ... dowolny sygnał przerwie"); sleep(10); // jest też usleep() dla mikro sekund puts ("Czekam przez 10.5s na sygnał z podanej maski"); siginfo_t sigInfo; struct timespec timeOut; timeOut.tv_sec = 10; timeOut.tv_nsec = 500000000; sig = sigtimedwait(&sigSet, &sigInfo, &timeOut); if (sig<0) { if (errno == EINTR) puts("otrzymałem inny sygnał"); else if (errno == EINVAL) puts("timeout"); else perror("Error in sigtimedwait()"); } else { printf("Czekanie zakończone po otrzymaniu sygnału %d od pid=%d\n", sig, sigInfo.si_pid ); } // jest też sigwaitinfo działająca jak sigtimedwait ale bez timeout'u }
import os, signal pid = os.getpid() print("Mój PID:", pid); # obsługa sygnału def obslugaSygnalu(s, f): print("Otrzymałem sygnał numer:", s) # SIGINT - pojawia się m.in. w efekcie Ctrl+C signal.signal(signal.SIGINT, obslugaSygnalu) # SIGTSTP - pojawia się m.in. w efekcie Ctrl+Z signal.signal(signal.SIGTSTP, obslugaSygnalu) print("Czekam na dowolny sygnał") signal.pause() print("Czekam na sygnał z podanego zbioru") # w odróżnieniu od C inne będą obsługiwane dopiero po otrzymaniu # sygnału ze zbioru (zostaną zakolejkowane) sig = signal.sigwait([signal.SIGTERM, signal.SIGINT]) print("Czekanie zakończone po otrzymaniu sygnału:", sig) print("Czekam przez 10.5s na sygnał z podanego zbioru") # w odróżnieniu od C inny sygnał jest obsługiwany, # ale nie przerywa czekania sig = signal.sigtimedwait([signal.SIGTERM, signal.SIGINT], 10.5) if sig == None: print("timeout"); else: print("Czekanie zakończone po otrzymaniu sygnału:", sig.si_signo, "od pid:", sig.si_pid )
// przy kompilacji konieczne jest dodanie: // -lrt -lpthread #include <stdio.h> #include <mqueue.h> #include <errno.h> #include <time.h> #include <string.h> #include <unistd.h> void getMsg(union sigval sv) { puts("kolejka nie jest już pusta"); // tu mógłby być realizowany odbiór wiadomości z kolejki } int main(int argc, char *argv[]) { if (argc != 3) { fprintf(stderr, "%s /nazwa 1|2\n", argv[0]); return 1; } char runType = argv[2][0]; // kolejki wiadomosci POSIX (wiecej man 7 mq_overview) // kolejka może mieć zarówno wielu producentów jak i odbiorców // raz odebrana wiadomość znika z kolejki // otwarcie kolejki struct mq_attr atrybuty; atrybuty.mq_maxmsg=3; // pojemność kolejki - 3 wiadomości atrybuty.mq_msgsize=20; // po 20 bajtów każda mqd_t msgQ = mq_open( argv[1], // nazwa zasobu O_RDWR|(runType == '1' ? O_CREAT|O_EXCL : 0), // flagi 0770, // prawa dostępu &atrybuty ); if (msgQ == (mqd_t) -1) { perror("Error in mq_open()"); return -1; } char buf[20]; unsigned priorytet; struct timespec timeout; clock_gettime(CLOCK_REALTIME, &timeout); timeout.tv_sec+=2; // odbieramy wiadomość z timeoutem ... // wiadomosci odbierane są w kolejności priorytetów, a następnie wysłania ssize_t size = mq_timedreceive(msgQ, buf, 20, &priorytet, &timeout); // mq_receive() - wersja bez timeoutu // mamy także możliwość odbioru bez timeoutu bądź zażądania powiadomienia // o wpisaniu pierwszej wiadomości do niepustej kolejki - mq_notify() if (size > 0){ printf("odebrałem %d bajtów z priotytetem %d: %s\n", size, priorytet, buf); } else { if (errno == ETIMEDOUT) puts("timeout"); else perror("Error in mq_timedreceive()"); } // ustawienie powiadamiania o pojawieniu się wiadomości w niepustej kolejce struct sigevent sev; sev.sigev_notify = SIGEV_THREAD; sev.sigev_notify_function = getMsg; sev.sigev_notify_attributes = NULL; if (mq_notify(msgQ, &sev) == -1) { perror("Error in mq_notify()"); } // wysyłamy wiadomość do kolejki // funkcja ta zawiesi proces gdy nie ma miejsca w kolejce która nie jest // otwarta z O_NONBLOCK na czas określony przez timeout // mq_send() - wersja bez timeout'u snprintf(buf, 20, "info A od: %d", getpid()); clock_gettime(CLOCK_REALTIME, &timeout); timeout.tv_sec+=2; if (mq_timedsend(msgQ, buf, strlen(buf), 9, &timeout)) perror("Error in mq_timedsend()"); printf("Wysłano: %s\n", buf); if (runType == '1') { snprintf(buf, 20, "info B od: %d", getpid()); if (mq_send(msgQ, buf, strlen(buf), 9)) perror("Error in mq_send()"); printf("Wysłano: %s\n", buf); sleep(20); mq_unlink(argv[1]); // w przypadku gdyby program zakończył się bez wykonania mq_unlink() // (np. na sutek kill -9) konieczne jest ręczne usunięcie plików z /dev/mqueue } }
print(""" # # kolejki w multiprocessing # """) import multiprocessing, multiprocessing.managers, queue, os, time def fun3(q): # obsługa kolejki ... zakończenie można także realizować # np. w oparciu o dane otrzymane w kolejce while True: if not q.empty(): el = q.get(block=False) # można także uruchamiać blokujący get() # z timeoutem lub bez ... if el[1] == None: break; else: print("potomek otrzymał", el) time.sleep(0.77) if __name__ == '__main__': # tworzymy kolejkę # moduł multiprocessing zawiera swoje implementacje kolejki FIFO # (Queue, SimpleQueue i JoinableQueue), których można bezpiecznie # używać pomiędzy procesami: # q = multiprocessing.Queue() # # chcemy jednak użyć kolejki priorytetowej z modułu queue # (oferuje także kolejki FIFO (Queue) i LIFO (LifoQueue)) # aby móc z niej poprawnie korzystać w multiprocessing # musimy użyć jej poprzez SyncManager'a: class Manager(multiprocessing.managers.SyncManager): pass Manager.register("PrioQueue", queue.PriorityQueue) m = Manager() m.start() q = m.PrioQueue() # dodajemy elementy do kolejki q.put((100, "delfin ma psa ;-)")) q.put((70, [13, 17])) q.put((200, [15, { "ab" : 11, "cd" : "xx" }])) # tworzymy i uruchamiamy kolejny podproces p1 = multiprocessing.Process(target=fun3, args=(q,)) p1.start() # wkładanie danych do kolejki time.sleep(0.404) print("dodaje nowe wiadomości do kolejki ...") q.put((13, "ABC ... i co dalej")) q.put((180, "bla bla bla ...")) # informacja dla potomka o zakończeniu obsługi kolejki print("czekam na opróżnienie kolejki ...") while q.qsize() > 0: time.sleep(0.333) # kończenie q.put((999, None)) p1.join() m.shutdown()
int blokada=false; zrownoleglijKod(); if (!blokada) { blokada=true; sekcjaKrytyczna(); blokada=false; }Niestety takie rozwiązanie nie gwarantuje skuteczności blokady, gdyż:
// przy kompilacji konieczne jest dodanie: // -lrt -lpthread #include <stdio.h> #include <semaphore.h> #include <fcntl.h> #include <unistd.h> #include <sys/mman.h> #include <errno.h> #include <time.h> #define BUF_SIZE 128 int main(int argc, char *argv[]) { if (argc != 3) { fprintf(stderr, "%s nazwa 1|2\n", argv[0]); return 1; } int retcode = 1; char runType = argv[2][0]; // pamięć dzielona przez shm i mmap (więcej `man 7 shm_overview`) // można by także przez mmap i zwykły plik // 1) utworzenie zasobu (pliku) SHM // jeżeli runType == '1' i zasób istnieje to wywołanie zakończy się błędem // ja jeżeli nie istnieje zostanie utworzony int shmFd = shm_open( argv[1], // nazwa zasobu O_RDWR|(runType == '1' ? O_CREAT|O_EXCL : 0), // flagi 0770 // prawa dostępu ); if (shmFd<0) { perror("Error in shm_open()"); return 1; } // 2) ustalenie wielkości if (ftruncate(shmFd, BUF_SIZE)) { perror("Error in ftruncate()"); goto END_1; } // 3) zamapowanie zasobu SHM w pamięci void *addr = mmap(0, BUF_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, shmFd, 0); if (addr == MAP_FAILED) { perror("Error in mmap()"); goto END_1; } // semafory POSIX (więcej `man 7 sem_overview`) // 1) utworzenie semafora // jeżeli runType == '1' i semafor istnieje to wywołanie zakończy się błędem sem_t* sem = sem_open( argv[1], // nazwa zasobu O_RDWR|O_CREAT|(runType == '1' ? O_EXCL : 0), // flagi 0770, // prawa dostępu 1 // wartość semafora ); if (sem == SEM_FAILED) { perror("sem_open"); goto END_1; } int* c = (int*) addr; if (runType == '1') { *c = 0; } // próba wejścia w kod zabezpieczony semaforem if (sem_trywait(sem)) { if (errno == EAGAIN) puts("semafor nie wpuszcza ... trzeba spróbować później"); else perror("Error in sem_trywait()"); } else { // semafor został opuszczony ... jesteśmy w sekcji krytycznej *c = *c + 0x01; printf("c = 0x%x\n", *c); sleep(3); // śpimy, to nieładnie spać w sekcji krytycznej, ale to tylko demo ... // podniesienie semafora if (sem_post(sem)) perror("Error in sem_post()"); } sleep(5); // czekanie na możliwość wejścia w kod zabezpieczony semaforem z timeout'em struct timespec timeout; clock_gettime(CLOCK_REALTIME, &timeout); timeout.tv_sec+=10; if (sem_timedwait(sem, &timeout)) { if (errno == ETIMEDOUT) puts("timeout ... trzeba spróbować później"); else perror("Error in sem_timedwait()"); } else { // semafor został opuszczony ... jesteśmy w sekcji krytycznej *c = *c + 0x0100; printf("c = 0x%x\n", *c); sleep(5); // śpimy, to nieładnie spać w sekcji krytycznej, ale to tylko demo ... // podniesienie semafora if (sem_post(sem)) perror("Error in sem_post()"); } sleep(3); // czekamy do skutku na możliwość wejścia w sekcję krytyczną if (sem_wait(sem)) { perror("Error in sem_timedwait()"); } else { // semafor został opuszczony ... jesteśmy w sekcji krytycznej *c = *c + 0x010000; printf("c = 0x%x\n", *c); sleep(5); // śpimy, to nieładnie spać w sekcji krytycznej, ale to tylko demo ... // podniesienie semafora if (sem_post(sem)) perror("Error in sem_post()"); } retcode = 0; // o ile w większości przypadków zwalnianie i zamykanie zasobów w momencie // kończenia programu nie ma znaczenia (zasoby są automatycznie odbierane przez // system - pamięć będzie zwolniona, pliki, gniazda sieciowe pozamykane, itd) // to w przypadku zasobów współdzielonych może mieć istotne znaczenie // np. jeżeli nie usuniemy zasobu SHM o danej nazwie nie będzie go można później // otworzyć gdy używamy flag O_CREAT|O_EXCL // // dlatego w powyższym kodzie w obsłudze błędów używamy goto do sekcji kończącej: END_2: if (runType == '1') { // usunięcie semafora (stanie się niedostępny także dla innych procesów) sem_unlink(argv[1]); } END_1: // odmapowanie zasobu z pamięci munmap(addr, BUF_SIZE); // zamknięcie zasobu SHM close(shmFd); if (runType == '1') { // usunięcie zasobu SHM (stanie się niedostępny także dla innych procesów) shm_unlink(argv[1]); } // w przypadku gdyby jednak program zakończył się bez wykonywania tego kodu // (np. na sutek kill -9) konieczne jest ręczne usunięcie plików z /dev/shm }
print(""" # # współdzielenie danych w multiprocessing # """) import multiprocessing, queue, os, time def fun4(lock, l, c): # obsługa zmiennej warunkowej for i in range(6): # blokujemy dostęp do listy c.acquire() # modyfikujemy zmienną na której mamy warunek l[0] = i # powiadamiamy wszystkich o tym c.notify_all() # zwalniamy dostęp do listy c.release() time.sleep(0.13) # obsługa locka i listy while True: if lock.acquire(timeout=0.5): # jeżeli udało się założyć lock'a ... l[1] += 1 print("podprocess:", l) time.sleep(0.77) # zdjęcie lock'a lock.release() time.sleep(0.33) else: print("podprocess: can't lock") if __name__ == '__main__': # tworzymy locka'a, czyli blokadę wejścia do sekcji krytycznej # (zmieniającej dane współdzielone) lock = multiprocessing.Lock() # oprócz zwykłych Lock'ów Python oferuje także: # * RLock - Lock w którym wątek zakładający blokadę może ją zakładać # kolejne razy bez jej uprzedniego zwalniania, tzn sekwencja: # l = RLock(); # l.acquire(); l.acquire(); # cos(); # l.release(); l.release(); # jest poprawna (przy zwykłym Lock'u zatrzyma się na drugim # l.acquire()) # * Semaphore - Lock z licznikiem dozwolonych wejść do sekcji # krytycznej np. l = Semaphore(value=3) pozwoli na 3 wywołania # l.acquire() (bez l.release()) # tworzymy współdzieloną listę ... na menagerze m = multiprocessing.Manager() l = m.list([0, 1, 2, 3]) # tworzymy zmienną warunkową używającą lock'a lock c = multiprocessing.Condition(lock) # tworzymy i uruchamiamy kolejny podproces p1 = multiprocessing.Process(target=fun4, args=(lock,l,c,)) p1.start() print("czekamy na spełnienie warunku ..."); c.acquire() while l[0] < 4: print("czekamy ... l[0]=", l[0]) c.wait() c.release() print("warunek spełniony ... l[0] = ", l[0]); time.sleep(0.17) # obsługa listy for i in range(3): # założenie lock'a print("process: czekam na lock ...") lock.acquire() # dodanie elementu do listy i wypisanie l.append(13 + i) print("process:", l) time.sleep(0.69) # zdjęcie lock'a lock.release() time.sleep(1.0) # zakończenie p1.terminate() p1.join() m.shutdown()
fork()
możliwe jest także tworzenie wątków (zwanych tez lekkimi procesami) w ramach procesu. Podstawowa różnica między procesem a wątkiem polega na tym iż wszystkie wątki (w ramach danego procesu) współdzielą całość pamięci (przestrzeni adresowej), podczas gdy każdy z procesów posiada niezależną pamięć i co najwyżej jakiś współdzielony z innymi fragment.
Każdy z wątków posiada natomiast niezależny stos (umieszczony we wspólnej przestrzeni adresowej), który jest używany m.in. do przechowywania zmiennych lokalnych (w tym argumentów funkcji). Jednak ze względu na umieszczenie tych danych w wspólnej przestrzeni adresowej są one także dostępne dla innych wątków (poprzez wskaźnik lub referencję).
// przy kompilacji konieczne jest dodanie: // -lrt -lpthread #include <pthread.h> #include <unistd.h> #include <stdio.h> #include <errno.h> #include <string.h> struct watekInfo { pthread_mutex_t* mut; pthread_cond_t* cond; const char* napis; int licznik; }; void wypisz1(struct watekInfo* wi) { int i, l = -99, ret; for(i=0; i<4; i++) { // próbujemy zablokować wejście na ten kawałek kodu // można czekać do skutku przy pomocy pthread_mutex_lock() if ((ret = pthread_mutex_trylock(wi->mut)) != 0) { fprintf(stderr, "Wątek: error in pthread_mutex_trylock(): %s\n", strerror(ret) ); } else { wi->licznik += 2; l = wi->licznik; pthread_mutex_unlock(wi->mut); } printf("Wątek: %s %d\n", wi->napis, l); usleep(600000); } sleep(8); pthread_exit(0); // kończy działanie wątku } void wypisz2(struct watekInfo* wi) { int i; printf("To watek: %s\n", wi->napis); pthread_mutex_lock(wi->mut); for(wi->licznik=0; wi->licznik<10; wi->licznik++) { printf("."); fflush(stdout); if (wi->licznik==5) pthread_cond_signal(wi->cond); sleep(1); } pthread_mutex_unlock(wi->mut); // zabraniamy przerwania watku w trakcie ponizszego wypisywania pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); for(i=0; i<10; i++) { printf("."); fflush(stdout); sleep(1); } printf("\n"); // a teraz już może być anulowywany pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); // ale od teraz tylko w wyznaczonych momentach // na funkcjach takich jak sleep, system, ... // czy tez pthread_testcancel() pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); sleep(50); puts("koniec wypisz2"); pthread_exit(0); } int main() { int i, l, ret; // przygotowanie mutex'a pthread_mutexattr_t mutAttr; pthread_mutexattr_settype(&mutAttr, PTHREAD_MUTEX_ERRORCHECK); // możemy ustawić: // * PTHREAD_MUTEX_ERRORCHECK aby mechanizm reagował błędem na // unlockowanie zalokowanego przez inny wątek mutexu itp, // * PTHREAD_MUTEX_RECURSIVE aby blokada nałożona przez wątek A // nie obowiazywala watku A (umożliwia to korzystanie z // mutexów w funkcjach rekurencyjnych) // * PTHREAD_MUTEX_NORMAL lub PTHREAD_MUTEX_DEFAULT // flagi te mogą się nieco różnić w zależności od stosowanej // biblioteki i użytych #define szczegółów należy szukać // w plikach features.h pthread.h oraz dokumentacji man pthread_mutexattr_setprotocol(&mutAttr, PTHREAD_PRIO_NONE); // poza nic nie zmieniającym PTHREAD_PRIO_NONE mamy także: // * PTHREAD_PRIO_INHERIT zabezpieczający wątki przed inwersją // priorytetów (priorytet po wejściu do sekcji krytycznej // jest podwyższany do najwyższego z priorytetów procesów // czekających // * PTHREAD_PRIO_PROTECT zabezpieczający również przed // zakleszczeniem dzięki podnoszeniu tego priorytetu powyżej // najwyższego z priorytetów procesów czekających // // do zapobiegania inwersji priorytetów przydatne jest też: // pthread_mutexattr_setprioceiling(); // inicjalizujemy mutex (dla domyślnych ustawień można podać // NULL jako drugi argument) pthread_mutex_t mut; pthread_mutex_init(&mut, &mutAttr); // przygotowanie danych dla wątku struct watekInfo wInfo; wInfo.napis = "Ala ma kota"; wInfo.licznik = 10; wInfo.mut = &mut; // tworzymy nowy watek uruchomiając w nim funkcje wypisz1 // i przekazując jako pierwszy argument tej funkcji wInfo pthread_t watek; if ((ret = pthread_create( &watek, 0, (void* (*)(void *))wypisz1, (void*)&wInfo ) ) != 0 ) { fprintf(stderr, "Error in pthread_create(): %s\n", strerror(ret)); return 1; } for(i=0; i<7; i++) { pthread_mutex_lock(wInfo.mut); wInfo.licznik -= 1; printf("Program główny: %d\n", wInfo.licznik); pthread_mutex_unlock(wInfo.mut); sleep(1); } #ifdef _GNU_SOURCE // pobranie atrybutu wątku // i sprawdzenie czy jest wątkiem procesu czy systemu pthread_attr_t atrybuty; if ((ret = pthread_getattr_np(watek, &atrybuty))) { fprintf(stderr, "Error in pthread_getattr_np(): %s\n", strerror(ret)); return 1; } if ((ret = pthread_attr_getscope(&atrybuty, &i))) { fprintf(stderr, "Error in pthread_attr_getscope(): %s\n", strerror(ret)); return 1; } if (i == PTHREAD_SCOPE_PROCESS) { puts("wątek procesu"); } else if (i == PTHREAD_SCOPE_SYSTEM) { puts("wątek systemu ... będzie widoczny w ps -m:"); system("ps -m"); } else { puts("inny wątek :-o"); } #endif // możemy oczekiwać na zakończenie wątku poprzez wywołanie // w innym funkcji pthread_join() umożliwia ona także // uzyskanie kodu z jakim zakończył się wątek printf("Program główny czeka na koniec wątku\n"); if ((ret = pthread_join(watek, NULL))) { fprintf(stderr, "Error in pthread_join(): %s\n", strerror(ret)); return 1; } printf("Wątek zakończył się\n"); // jej wywołanie jest konieczne aby wątek po zakończeniu // był w całości usunięty, chyba że wątek został określony // jako taki do którego nie można się dołączyć przez wywołanie // pthread_detach() lub podanie przy tworzeniu flagi // PTHREAD_CREATE_DETACHED // przygotowujemy zmienną warunkową pthread_cond_t cond; pthread_cond_init(&cond, NULL); // aktualizujemy strukturę danych dla wątku wInfo.napis = "drugi"; wInfo.licznik = 0; wInfo.cond = &cond; // tworzymy kolejny wątek if ((ret = pthread_create( &watek, 0, (void* (*)(void *))wypisz2, (void*)&wInfo ) ) != 0 ) { fprintf(stderr, "Error in pthread_create(): %s\n", strerror(ret)); return 1; } // czekamy ... while (wInfo.licznik < 5) pthread_cond_wait(wInfo.cond, wInfo.mut); // pthread_cond_wait zwalnia tak na prawdę na chwile podany // do niego mutex a po odebraniu pthread_cond_signal na // dozorowanym przez niego warunku ponownie go zamyka printf(" jesteśmy za warunkiem "); fflush(stdout); sleep(7); printf(" zabijamy wątek "); // i go kończymy z zewnątrz // w zależności od ustawień wątku anulowanie wątku dzieje // się natychmiastowo albo w jednym z punktów anulowania // zobacz funkcje to ustawiające w wypisz2() pthread_cancel(watek); // wypada też odebrać jego kod powrotu pthread_join(watek, NULL); puts("a teraz watek już nie żyje ..."); // usunięcie mutex'a pthread_mutex_destroy(wInfo.mut); // usunięcie zmiennej warunkowej pthread_cond_destroy(wInfo.cond); }
// przy kompilacji konieczne jest dodanie: // -lboost_system -lboost_thread -lpthread #include <boost/thread/mutex.hpp> #include <boost/thread/thread.hpp> #include <queue> #include <string> #include <iostream> std::queue<std::string> kolejka; class Watek1 { Watek1(const Watek1&); public: int abc; boost::mutex mutex; // boost oferuje także mutx'a pozwalającego na tworzenie kolejnych // blokad przez wątek który utworzył pierwszą - boost::recursive_mutex Watek1() {} void operator()() { for (int i=0; i<8; i++) { if (mutex.try_lock()) { abc += 1; std::cout << "wątek, abc= " << abc << std::endl; mutex.unlock(); } else { std::cout << "wątek nie dostałem mutex'a ..." << std::endl; } boost::this_thread::sleep(boost::posix_time::millisec(500)); } for (int i=0; i<8; i++) { boost::this_thread::sleep(boost::posix_time::millisec(700)); boost::mutex::scoped_lock scoped_lock(mutex); // powyższy lock (na mutexie tego wątku) // blokuje od teraz do końca bloku if (!kolejka.empty()) { std::cout << i << ") odebrałem z kolejki \ " << kolejka.front() << std::endl; kolejka.pop(); } else { std::cout << i << ") kolejka pusta" << std::endl; } } } }; int main() { // przygotowanie danych wątku Watek1 wf1; wf1.abc=56; // uruchomienie wątku ... // boost::ref() potrzebne bo obiekty klasy Watek1 są niekopiowalne boost::thread watek1(boost::ref(wf1)); boost::this_thread::sleep(boost::posix_time::millisec(1500)); // manipulacja danymi wątku wf1.mutex.lock(); wf1.abc=13; boost::this_thread::sleep(boost::posix_time::seconds(1)); wf1.mutex.unlock(); // obsługa kolejki wf1.mutex.lock(); kolejka.push("Ala ma kota"); kolejka.push("Kot ma Ale"); wf1.mutex.unlock(); std::cout << "2 pierwsze elementy są w kolejce" << std::endl; boost::this_thread::sleep(boost::posix_time::seconds(4)); wf1.mutex.lock(); kolejka.push("Abecadlo"); kolejka.push("z pieca spadlo"); wf1.mutex.unlock(); std::cout << "2 kolejne elementy sa w kolejce" << std::endl; // czekamy na zakończenie wątku watek1.join(); }
import threading, time # UWAGA: # w standardowej implementacji CPython w danym # czasie tylko jeden wątek może wykonywać # kod pythonowy # # w efekcie stosowanie wątków ma sens tylko dla # wątków czekających na coś, a nie ma sensu dla # wątków mających wykonywać się równolegle # (na różnych rdzeniach) # # w takim przypadku należy używać pełnego # rozgałęziania procesu (moduł multiprocessing) def fun(txt, st): # używamy lokalnej pamięci dla danego wątku daneWatku = threading.local() for daneWatku.i in range(4): print(txt, daneWatku.i) time.sleep(st) # uruchomienie wątku t = threading.Thread(target=fun, args=("AA",2,)) t.start(); # można także zdefiniować timer który # uruchomi wątek po zadanym czasie tim = threading.Timer(3, fun, args=("BB",1.2,)) tim.start() # dla wątków można stosować także mechanizmy # synchronizacji takie jak dla multiprocessing # # ponadto wątki można także obsługiwać przez # interfejs identyczny z multiprocessing # poprzez multiprocessing.dummy
#include <sys/socket.h> #include <netinet/ip.h> #include <arpa/inet.h> #include <netdb.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define BUF1_SIZE 256 #define BUF2_SIZE 64 const char* getAddresInfo( struct sockaddr_storage *srcAddr, socklen_t srcAddrLen, char* buf, int bufLen ); int main(int argc, char *argv[]) { char buf1[BUF1_SIZE], buf2[BUF2_SIZE]; int ret, pid; if (argc != 3 && argc != 4) { fprintf(stderr, "USAGE: %s dstHost dstPort [listenPort]\n", argv[0]); return EXIT_FAILURE; } // struktura zawierająca adres na który wysyłamy // użycie funkcji getaddrinfo pozwala na zapewnienie obsługi zarówno IPv4 jak i IPv6, // a także podawanie adresów w postaci numerycznej bądź przy użyciu nazw // alternatywnie można ręcznie przygotowywać struktury sockaddr_in / sockaddr_in6 // z użyciem np. htons(), inet_aton(), ... struct addrinfo *dstAddrInfo; if((ret = getaddrinfo(argv[1], argv[2], NULL, &dstAddrInfo)) != 0) { fprintf(stderr, "Error in getaddrinfo(): %s\n", gai_strerror(ret)); return EXIT_FAILURE; } // tutaj mogliśmy uzyskać kilka adresów, // ale optymistycznie zakładamy że pierwszy będzie OK // utworzenie gniazda sieciowego ... SOCK_DGRAM oznacza UDP // (na gnieździe z SOCK_DGRAM standardowo nie będzie widać błędów ICMP // związanych np. z niedostępnością docelowego hosta lub portu) int sfd = socket(dstAddrInfo->ai_family, SOCK_DGRAM, 0); if (sfd<0) { perror("Error in socket()"); return EXIT_FAILURE; } if (argc == 4) { // jeżeli podane opcjonalne argumentu to określamy na jakim porcie słuchamy if (dstAddrInfo->ai_family == AF_INET6) { struct sockaddr_in6 listenAddr; listenAddr.sin6_family=AF_INET6; listenAddr.sin6_port=htons(atoi(argv[3])); // host to network short (porządek bajtów), są też dla long i network to host listenAddr.sin6_addr=in6addr_any; // in6addr_any oznacza że nasłuchujemy na każdym adresie IP danego hosta // (zamiast tego można określić konkretny adres) if(bind(sfd, (struct sockaddr *) &listenAddr, sizeof(struct sockaddr_in6))) { perror("Error in bind()"); return EXIT_FAILURE; } } else if (dstAddrInfo->ai_family == AF_INET) { struct sockaddr_in listenAddr; listenAddr.sin_family=AF_INET; listenAddr.sin_port=htons(atoi(argv[3])); // host to network short (porządek bajtów), są też dla long i network to host listenAddr.sin_addr.s_addr=INADDR_ANY; // INADDR_ANY oznacza że nasłuchujemy na każdym adresie IP danego hosta // (zamiast tego można określić konkretny adres) if(bind(sfd, (struct sockaddr *) &listenAddr, sizeof(struct sockaddr_in))) { perror("Error in bind()"); return EXIT_FAILURE; } } // określenie tego wraz z uruchomieniem recvfrom() pozwala na oczekiwanie na // pakiety UDP na wskazanym adresie i porcie, gdyby w ramach obsługi odebranej // wiadomości odsyłana byłaby odpowiedź do nadawcy to byłby to "serwer UDP" } // aby móc niezależnie wysyłać i odbierać rozgałęziamy proces // alternatywnie można użyć select() do czekania na dane z sieci lub standardowego wejścia switch (pid = fork()) { case -1: { // funkcja zwróciła -1 co oznacza błąd perror("Error in fork"); return 1; } case 0: { // potomek odbiera odpowiedzi w nieskończonej pętli // zostanie zakończony sygnałem od rodzica while(1) { // struktura do której zapisany zostanie adres nadawcy struct sockaddr_storage srcAddr; socklen_t srcAddrLen = sizeof(struct sockaddr_storage); // informację tą można wykorzystać do odsyłania odpowiedzi do nadawcy // odbiór wiadomości ... blokuje do momentu otrzymania wiadomości ret = recvfrom( sfd, buf1, BUF1_SIZE, 0, (struct sockaddr *) &srcAddr, &srcAddrLen ); // wypisanie wiadomości z informacją o nadawcy buf1[ret] = '\0'; printf("odebrano %d bajtów od %s: %s\n", ret, getAddresInfo(&srcAddr, srcAddrLen, buf2, BUF2_SIZE), buf1 ); } } default: { // rodzic wysyła wiadomości while (fgets( buf1, BUF1_SIZE, stdin )) { ret = sendto( sfd, buf1, strlen(buf1), 0, dstAddrInfo->ai_addr, dstAddrInfo->ai_addrlen ); printf("wysłano: %d bajtów: %s\n", ret, buf1); } // kończymy potomka gdy skończyliśmy nadawanie kill(pid, SIGTERM); wait(0); } } // zamkniecie gniazda puts("KONIEC"); freeaddrinfo(dstAddrInfo); close(sfd); } // konwersja adresów IPv4 i IPv6 wraz z numerem portu na napis const char* getAddresInfo( struct sockaddr_storage *srcAddr, socklen_t srcAddrLen, char* buf, int bufLen ) { if (srcAddrLen > sizeof(struct sockaddr_storage)) return NULL; const char* addrStr = NULL; uint16_t port; if (srcAddr->ss_family == AF_INET) { struct sockaddr_in* srcAddrPtr = (struct sockaddr_in*) srcAddr; addrStr = inet_ntop(AF_INET, &(srcAddrPtr->sin_addr), buf, bufLen); port = ntohs(srcAddrPtr->sin_port); } else if (srcAddr->ss_family == AF_INET6) { struct sockaddr_in6* srcAddrPtr = (struct sockaddr_in6*) srcAddr; addrStr = inet_ntop(AF_INET6, &(srcAddrPtr->sin6_addr), buf, bufLen); port = ntohs(srcAddrPtr->sin6_port); } if (addrStr) { int len = strlen(buf); snprintf(buf+len, bufLen-len, ":%d", port); } return addrStr; }
import socket, sys, os, signal if len(sys.argv) != 3 and len(sys.argv) != 4: print("USAGE: " + sys.argv[0] + " dstHost dstPort [listenPort]", file=sys.stderr) exit(1); # struktura zawierająca adres na który wysyłamy dstAddrInfo = socket.getaddrinfo(sys.argv[1], sys.argv[2]) # tutaj mogliśmy uzyskać kilka adresów, # ale optymistycznie zakładamy że pierwszy będzie OK dstAddrInfo = dstAddrInfo[0] # utworzenie gniazda sieciowego ... SOCK_DGRAM oznacza UDP # (na gnieździe z SOCK_DGRAM standardowo nie będzie widać błędów ICMP # związanych np. z niedostępnością docelowego hosta lub portu ...) sfd = socket.socket(dstAddrInfo[0], socket.SOCK_DGRAM) if len(sys.argv) == 4: # jeżeli podane opcjonalne argumentu to określamy na jakim porcie słuchamy if dstAddrInfo[0] == socket.AF_INET6: sfd.bind(('::', int(sys.argv[3]))) # :: oznacza dowolny adres IPv6 elif dstAddrInfo[0] == socket.AF_INET: sfd.bind(('0.0.0.0', int(sys.argv[3]))) # 0.0.0.0 oznacza dowolny adres # określenie tego wraz z uruchomieniem recvfrom() pozwala na oczekiwanie na # pakiety UDP na wskazanym adresie i porcie, gdyby w ramach obsługi odebranej # wiadomości odsyłana byłaby odpowiedź do nadawcy to byłby to "serwer UDP" while True: rdfd, _, _ = select.select([sfd, sys.stdin], [], []) if sfd in rdfd: # odbieramy i wypisujemy dane z UDP data, addr = sfd.recvfrom(4096) print("odebrano od", addr, ":", data.decode()); if sys.stdin in rdfd: # wczytujemy stdin data = sys.stdin.readline() if not data: # konczymy sfd.close() sys.exit() else: # wysyłamy dane z stdin sfd.sendto(data.encode(), dstAddrInfo[4]) # alternatywnie można użyć fork(): # pid = os.fork() # if pid == 0: # # potomek odbiera odpowiedzi w nieskończonej pętli # # zostanie zakończony sygnałem od rodzica # while True: # data, addr = sfd.recvfrom(4096) # print("odebrano od", addr, ":", data.decode()); # else: # # rodzic wysyła wiadomości # while True: # data = sys.stdin.readline() # if not data: # break # else: # sfd.sendto(data.encode(), dstAddrInfo[4]) # os.kill(pid, signal.SIGTERM) # sfd.close()
close()
, co informuje zarówno nasz system jak i serwer o zakończeniu połączenia.#include <sys/socket.h> #include <netinet/ip.h> #include <arpa/inet.h> #include <netdb.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define BUF_SIZE 256 int main(int argc, char *argv[]) { int ret, sfd; char buf[BUF_SIZE]; if (argc != 3) { fprintf(stderr, "%s dstHost dstPort\n", argv[0]); return 1; } // struktura zawierająca adres na który wysyłamy // użycie funkcji getaddrinfo pozwala na zapewnienie obsługi zarówno IPv4 jak i IPv6, // a także podawanie adresów w postaci numerycznej bądź przy użyciu nazw // alternatywnie można ręcznie przygotowywać struktury sockaddr_in / sockaddr_in6 // z użyciem np. htons(), inet_aton(), ... struct addrinfo *dstAddrInfo; if((ret = getaddrinfo(argv[1], argv[2], NULL, &dstAddrInfo)) != 0) { fprintf(stderr, "Error in getaddrinfo(): %s\n", gai_strerror(ret)); return 1; } // mogliśmy uzyskać kilka adresów, więc próbujemy używać kolejnych do skutku struct addrinfo *aiIter; for (aiIter = dstAddrInfo; aiIter != NULL; aiIter = aiIter->ai_next) { // utworzenie gniazda sieciowego ... SOCK_STREAM oznacza TCP sfd = socket(aiIter->ai_family, SOCK_STREAM, 0); if (sfd<0) { perror("Error in socket()"); continue; } // łączymy z serwerem if (connect(sfd, aiIter->ai_addr, aiIter->ai_addrlen)) { perror("Error in connect()"); close(sfd); sfd = -1; continue; } // udało się połączyć ... przerywamy sprawdzanie kolejnych adresów break; } freeaddrinfo(dstAddrInfo); if (sfd<0) { fprintf(stderr, "Can't connect\n"); return 1; } const char* msg = "Ala ma Kota\n"; if (send(sfd, msg, strlen(msg), 0) < 0) { // takie wywołanie send() jest równoważne wywołaniu write() perror("Error in send()"); return 1; } // ustalamy limit czasu oczekiwania na dane 13s struct timeval timeOut; timeOut.tv_sec = 13; timeOut.tv_usec = 0; // tworzymy zestaw deskryptorów dla funkcji select() // i umieszczamy w nim deskryptor gniazda sieciowego fd_set fdSet; FD_ZERO(&fdSet); FD_SET(sfd, &fdSet); // czekamy na dane w którymś z deskryptorów wchodzących // w skład zestawu lub timeout select(FD_SETSIZE, &fdSet, 0, 0, &timeOut); // select zmieni wartość fdSet i timeOut ... // będą one zawierały odpowiednio zbiór deskryptorów gotowych // do czytania i czas jaki pozostał do "wytimeoutowania" if (FD_ISSET (sfd, &fdSet)) { // jeżeli sfd jest w zbiorze gotowych do czytania to czytamy ret = read(sfd, buf, BUF_SIZE); if (ret < 0) { perror("Error in read()"); } else if (ret == 0) { puts("connection close by peer"); } else { buf[ret]='\0'; printf("odebrano %d bajtów: %s\n", ret, buf); } } else { puts("timeout"); } // w zależności od wymagań implementowanego protokołu można kontynuować // czekanie na kolejne dane z TCP i/lub jakiegoś innego wejścia (np. stdin), // można wysłać kolejne dane (żądanie) do serwera, itd ... // // w przypadku oczekiwania na dalsze dane (ponownego wywołania select) należy // pamiętać o ponownym ustawieniu fdSet i timeOut // zamkniecie gniazda close(sfd); }
import socket, select, sys if len(sys.argv) != 3: print("USAGE: " + sys.argv[0] + " dstHost dstPort", file=sys.stderr) exit(1); # struktura zawierająca adres na który wysyłamy dstAddrInfo = socket.getaddrinfo(sys.argv[1], sys.argv[2], proto=socket.IPPROTO_TCP) # mogliśmy uzyskać kilka adresów, więc próbujemy używać kolejnych do skutku for aiIter in dstAddrInfo: try: print("try connect to:", aiIter[4]) # utworzenie gniazda sieciowego ... SOCK_STREAM oznacza TCP sfd = socket.socket(aiIter[0], socket.SOCK_STREAM) # połączenie ze wskazanym adresem sfd.connect(aiIter[4]) except: # jeżeli się nie udało ... zamykamy gniazdo if sfd: sfd.close() sfd = None # i próbujemy następny adres continue break; if sfd == None: print("Can't connect", file=sys.stderr) exit(1); # wysyłanie sfd.sendall("Ala ma Kota\n".encode()) # czekanie na dane i odbiór danych while True: rdfd, _, _ = select.select([sfd], [], [], 13.0) if sfd in rdfd: d = sfd.recv(4096) d = d.decode() print(d, end="") # odbiór pustego pakietu lub pakietu zawierającego # jedynie pustą linię kończy działanie if d == "" or d == "\n" or d == "\r\n": break else: # timeout kończy działanie break # zamykanie połączenia sfd.shutdown(socket.SHUT_RDWR) sfd.close()
bind
)listen
)accept
)accept()
zwraca nowe gniazdo związane z nawiązanym połączeniem TCP. Ma ono ustawiony adresem IP i portem używany przez klienta oraz inny (niż używany do nasłuchiwania) numer portu po stronie serwera.
#include <sys/socket.h> #include <netinet/ip.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <signal.h> #include <sys/wait.h> #define BUF_SIZE 256 #define QUERY_SIZE 3 #define MAX_CHILD 5 int childNum = 0; void onChildEnd(int sig) { puts("odebrano sygnał o śmierci potomka"); --childNum; waitpid(-1, NULL, WNOHANG); } int main(int argc, char *argv[]) { int res; char buf[BUF_SIZE]; if (argc != 2) { fprintf(stderr, "USAGE: %s listenPort\n", argv[0]); return EXIT_FAILURE; } // obsługa sygnału o zakończeniu potomka signal(SIGCHLD, &onChildEnd); // utworzenie gniazda sieciowego ... SOCK_STREAM oznacza TCP int sfd = socket(PF_INET6, SOCK_STREAM, 0); if (sfd<0) { perror("Error in socket()"); return EXIT_FAILURE; } // ustawienie opcji gniazda ... IPV6_V6ONLY=0 umożliwia korzystanie z tego // samego socketu dla IPv4 i IPv6 (rozszerzenie Linux-owe - na niektórych // systemach nie jest możliwe dualne słuchanie przy pomocy socketu IPv6) int opt = 0; if (setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) < 0) { perror("setsockopt(IPV6_V6ONLY=0)"); return EXIT_FAILURE; } // utworzenie struktury opisującej adres struct sockaddr_in6 serwer; serwer.sin6_family=AF_INET6; serwer.sin6_port=htons(atoi(argv[1])); // host to network short (porządek bajtów), są też dla long i network to host serwer.sin6_addr=in6addr_any; // in6addr_any oznacza że nasłuchujemy na każdym adresie IP danego hosta // (zamiast tego można określić konkretny adres) // przypisanie adresu ... if (bind(sfd, (struct sockaddr *) &serwer, sizeof(struct sockaddr_in6)) < 0) { perror("Error in bind()"); return EXIT_FAILURE; } // określenie gniazda jako używanego do nasłuchiwania połączeń przychodzących if (listen(sfd, QUERY_SIZE) < 0) { perror("Error in listen()"); return EXIT_FAILURE; } while(1) { // odebranie połączenia struct sockaddr_in6 from; socklen_t fromLen=sizeof(struct sockaddr_in6); int sfd2 = accept(sfd, (struct sockaddr *) &from, &fromLen); // weryfikacja ilości potomków if (childNum >= MAX_CHILD) { printf("za dużo potomków\n"); // można tutaj także wysłać informacje o błędzie serwera do klienta close(sfd2); continue; } // aby móc obsługiwać wiele połączeń rozgałęziamy proces int pid = fork(); if (pid == 0) { // proces potomny ... zajmuje się tylko nowym połączeniem, // a nie nasłuchaniem więc zamykamy sfd close(sfd); // konwersja adresu na postać napisową char fromStr[64]; snprintf(fromStr, 64, "%s:%d", inet_ntop(AF_INET6, &(from.sin6_addr), buf, BUF_SIZE), ntohs(from.sin6_port) ); // obsługa połączenia printf("połączenie od: %s\n", fromStr); FILE * net; net=fdopen(sfd2, "r+"); // należałoby tutaj używać czekania z timeout'em ... // inaczej usługa jest podatna na ataki DoS while(fgets(buf, BUF_SIZE, net)) { printf("odebrano od %s: %s\n", fromStr, buf); fputs(buf, net); } fclose(net); printf("koniec połączenia od: %s\n", fromStr); // zakończenie połączenia i potomka close(sfd2); return 0; } if (pid == -1) perror("Error in fork()"); ++childNum; close(sfd2); } // zamkniecie gniazda close(sfd); }
import socket, select, signal, sys, os MAX_CHILD = 5 QUERY_SIZE = 3 TIMEOUT = 13 BUF_SIZE = 4096 if len(sys.argv) != 2: print("USAGE: " + sys.argv[0] + " listenPort", file=sys.stderr) exit(1); # obsługa sygnału o zakończeniu potomka childNum = 0 def onChildEnd(s, f): print("odebrano sygnał o śmierci potomka") global childNum childNum -= 1 os.waitpid(-1, os.WNOHANG); signal.signal(signal.SIGCHLD, onChildEnd) # utworzenie gniazd sieciowych ... SOCK_STREAM oznacza TCP sfd_v4 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sfd_v6 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) # ustawienie opcji gniazda ... IPV6_V6ONLY=1 wyłącza korzystanie # z tego samego socketu dla IPv4 i IPv6 sfd_v6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) # przypisanie adresów ... # '0.0.0.0' oznacza dowolny adres IPv4 (czyli to samo co INADDR_ANY) # '::' oznacza dowolny adres IPv6 (czyli to samo co in6addr_any) sfd_v4.bind(('0.0.0.0', int(sys.argv[1]))) sfd_v6.bind(('::', int(sys.argv[1]))) # określenie gniazd jako używanych do odbioru połączeń przychodzących # (długość kolejki połączeń ustawiona na wartość QUERY_SIZE) sfd_v4.listen(QUERY_SIZE) sfd_v6.listen(QUERY_SIZE) # czekanie na połączenia z użyciem select() w nieskończonej pętli while True: sfd, _, _ = select.select([sfd_v4, sfd_v6], [], []) for fd in sfd: # odebranie połączenia sfd_c, sAddr = fd.accept() # weryfikacja ilości potomków if childNum >= MAX_CHILD: print("za dużo potomków - odrzucam połączenie od:", sAddr); sfd_c.send("Internal Server Error\r\n".encode()) sfd_c.close() break # aby móc obsługiwać wiele połączeń rozgałęziamy proces pid = os.fork() if pid > 0: # rodzic - zwiększamy licznik potomków childNum += 1 else: # potomek - obsługa danego połączenia print("połączenie od:", sAddr) while True: # czekanie na dane z timeout'em # aby zabezpieczyć się przed atakiem DoS rd, _, _ = select.select([sfd_c], [], [], TIMEOUT) if sfd_c in rd: data = sfd_c.recv(BUF_SIZE) if data: print("odebrano od", sAddr, ":", data.decode()); sfd_c.send(data) else: print("koniec połączenia od:", sAddr) break else: print("timeout połączenia od:", sAddr) break # zamykanie połączenia sfd_c.shutdown(socket.SHUT_RDWR) sfd_c.close() sys.exit()
ulimit -s
), lub w trakcie jego pracy (przy pomocy setrlimit(RLIMIT_STACK, ...)
), pod warunkiem że mieści się w ramach ogólnosystemowego limitu. Zwiększenie rozmiaru stosu wpływa na rozmiar pamięci wirtualnej przydzielonej procesowi, jednak (samo w sobie) nie wpływa na rzeczywiste zużycie pamięci przez proces.
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/resource.h> char buf[64], *bufS; int fun() { int i, j, x[10000000]; for (i=0; i<17; i+=2) { for (j=0; j<20000; ++j) x[i*10000+j] = j; sleep(1); system(buf); } } int main() { snprintf(buf, 64, "ps -o 'vsize,size,rss,cmd' -p %d", getpid()); system(buf); puts("zwiększam rozmiar stosu"); struct rlimit rl; rl.rlim_cur = 2000000000; rl.rlim_max = rl.rlim_cur; if(setrlimit(RLIMIT_STACK, &rl)) perror("Error in setrlimit()"); system(buf); bufS = buf + strlen(buf); snprintf(bufS, 64 - (bufS-buf), " --no-headers"); fun(); *bufS = '\0'; system(buf); *bufS = ' '; fun(); }
new
i funkcje z rodziny malloc
. Alokacja pamięci poza stosem powoduje konieczność jawnego zwalniania pamięci (nie jest ona zwalniana wraz z zniknięciem zmiennej) przy pomocy odpowiednio operatora delete
lub funkcji free
.#include <stdio.h> #include <stdlib.h> #include <string.h> #include <string> #include <inttypes.h> struct Abc { std::string a; int b; private: short c; public: Abc() { puts("konstruktor ABC"); c = 123; } ~Abc() { puts("destruktor ABC"); } void print() { printf("a: %s, b: %d, c:%d\n", a.c_str(), b, c); } }; struct Xxx { Abc abc1; Abc abc2; int tab[9]; int xx1; int xx2; }; int main(int argc, char *argv[]) { int a[5], aa; // alokacja pamięci int *bb = (int*)malloc(sizeof(int)); int *cc = new int; int *c = new int[20]; // tablica alokowana dynamicznie w stylu C++ Abc *dd = new Abc; printf("na stosie: %p %p %p %p\n", &argv, &argc, &aa, a); printf("poza stosem: %p %p %p %p\n", bb, cc, c, dd); // zwalnianie pamięci free(bb); delete cc; delete c; delete dd; // alokujemy większy obszar pamięci przy pomocy malloc'a char* x = (char*)malloc(1024); // jest też realloc() umożliwiający zmianę rozmiaru zaalokowanego obszaru // zerujemy zaalokowaną pamięć ... na ogół nie jest to potrzebne, // ale w tym przykładzie może się przydać memset(x, 0, 1024); // często przydatna jest też funkcja memcpy() kopiująca wskazany // fragment pamięci w inne miejsce // pamięć zaalokowaną z użyciem malloc'a możemy dzięki // artmetyce wskaźnikowej oraz rzutowaniu typów wskaźnikowych // interpretować w dowolny sposób ... Abc *d1, *d2; int *b1, *b2; d1 = (Abc*)x; d2 = (Abc*)(x) + 1; b1 = (int*)(x + 2 * sizeof(Abc)); b2 = b1 + 10; printf("rozmiar Abc: %ld == %ld == %ld\n", sizeof(Abc), (uint64_t)d2-(uint64_t)d1, (uint64_t)b1-(uint64_t)d2 ); d1->b = 15; d1->print(); // jak widać powstał obiekt typu Abc, ale nie wykonał się konstruktor ... // ani konstruktory składowych (jak std::string), więc wywołanie: // d1->a = "aa bb cc"; // może się nie udać (zakończyć się np. Segmentation fault) // // aby uniknąć związanych z tym problemów możemy skorzystać z wariantu // operatora new który utworzy obiekt w zaalokowanej pamięci: new(d2) Abc; d2->a = "aa bb cc"; d2->print(); for (aa=0; aa<10; ++aa) b1[aa] = aa + 16; *b2 = 136917; // możemy też na taki obszar pamięci (nawet już zapełniony) // patrzeć np. jak na strukturę Xxx* z = (Xxx*)x; printf("%d == %d %d == %d %d == %d\n", z->tab[2], b1[2], z->xx1, b1[9], z->xx2, *b2 ); free(x); // takie zwolnienie nie wywoła destruktorów dla d1 i d2 }
#include <time.h> #include <stdlib.h> #include <string.h> #include <stdio.h> size_t time2str( const time_t* time, const char* timeZone, const char *format, char *buf, size_t bufLen, struct tm *tmPtr ); int main() { time_t czas1 = time(0); printf("Od początku epoki upłynęło %d sekund\n", czas1); // początek epoki to: 1970-01-01 00:00:00 UTC // wynik jest niezależny od lokalnej strefy czasowej // (zawsze wyrażany jest w UTC) // jeżeli potrzebna jest większa precyzja to można użyć np.: struct timespec czas2; clock_gettime(CLOCK_REALTIME, &czas2); printf("Od poczotku epoki upłyneło %d sekund i %d ns\n", czas2.tv_sec, czas2.tv_nsec); // na niektórych platformach (np. Linux) w clock_gettime() // wspierane są także inne zegary niż czasu rzeczywistego // np. podające czas monotoniczny lub od uruchomienia systemu char buf[128]; // korzystamy z własnej funkcji tworzącej napis w oparciu o // format zmiennej czasowej ... co prawda do UTC jest gmtime(), // ale w połączeniu z strftime potrafi dawać złe wyniki // (np. dla "%s") ponadto funkcja taka pozwala także na // obsługę innych stref czasowych time2str( &(czas2.tv_sec), "UTC", "%Y-%m-%d %H:%M:%S %Z (%s)", buf, sizeof(buf), NULL ); printf("Mamy teraz: %s\n", buf); time2str( &(czas2.tv_sec), NULL, "%Y-%m-%d %H:%M:%S %Z (%s)", buf, sizeof(buf), NULL ); printf("Mamy teraz: %s\n", buf); } size_t time2str( const time_t* time, const char* timeZone, const char *format, char *buf, size_t bufLen, struct tm *tmPtr ) { // jeżeli podano tmPtr != NULL użyj podanej struktury, // w przeciwnym razie użyj lokalnej struct tm tmTmp; if (!tmPtr) { tmPtr = &tmTmp; } // jeżeli podano strefę czasową to // zapamiętaj oryginalną strefę i ustaw nową char oldTZ[128]; if (timeZone) { strncpy(oldTZ, getenv("TZ"), 128); setenv("TZ", timeZone, 1); tzset(); } // pobierz rozłożony czas w ustawionej strefie czasowej localtime_r(time, tmPtr); // zapisz sformatowany napis do bufora bufLen = strftime(buf, bufLen, format, tmPtr); // przywróć oryginalną strefę czasową if (timeZone) { setenv("TZ", oldTZ, 1); tzset(); } return bufLen; }
import time import os def time2str(t, tz=None, fmt="%Y-%m-%d %H:%M:%S %Z (%s)"): if tz: oldTZ = os.environ['TZ'] os.environ['TZ'] = tz time.tzset() tm = time.localtime(t) s = time.strftime(fmt, tm) if tz: os.environ['TZ'] = oldTZ time.tzset() return [s, tz] czas = time.time() print("Od początku epoki upłynęło " + str(czas) + " sekund") # początek epoki to: 1970-01-01 00:00:00 UTC # wynik jest niezależny od lokalnej strefy czasowej # (zawsze wyrażany jest w UTC) tt = time2str(czas, tz="UTC") print("Mamy teraz: " + tt[0]); tt = time2str(czas) print("Mamy teraz: " + tt[0]); from datetime import datetime, timezone dt = datetime.fromtimestamp(czas, tz=timezone.utc) print("Mamy teraz: " + dt.strftime("%Y-%m-%d %H:%M:%S %Z (%s)")) # w celu pobrania aktualnego czasu zamiast fromtimestamp() # można skorzystać z metody now()
sleep
lub podobnych celem przeczekania podanego czasu lub skorzystać z timerów celem otrzymania sygnału po zadanym czasie.// przy kompilacji konieczne jest dodanie: // -lrt -lpthread #include <time.h> #include <signal.h> #include <stdio.h> #include <unistd.h> #include <errno.h> void onTimer(int sig) { puts("timer !!!"); // drzemka 0.35s // do spania można wykorzystać usleep(), sleep() lub nanosleep() // usleep i sleep działają bardzo podobnie // (z tym że pierwszy przyjmuje czas w sekundach, a drugi w mikrosekundach) usleep(350000); } int main() { // obslugujemy sygnał SIGALRM signal(SIGALRM, &onTimer); // najprostrzy timer ... alarm(4); pause(); // timery POSIX (wiecej man timer_settime) // 1) tworzymy timer timer_t timer; timer_create(CLOCK_REALTIME, NULL, &timer); // podanie wskaźnika na strukturę sigevent zamiast NULL pozwala na określenie // innego sygnału jaki ma wygenerować timer lub obsługę tak jak wątku // (więcej w `man 7 sigevent`) // 2) konfiguryujemy timer struct itimerspec tSpec; // pierwsze wykonanie timera po 100ms tSpec.it_value.tv_sec=0; tSpec.it_value.tv_nsec=100000000; // kolejne co 1s // (jezeli it_interval będzie wyzerowane to timer wykona się tylko raz) tSpec.it_interval.tv_sec=1; tSpec.it_interval.tv_nsec=0; // 3) uruchamiamy timer // drugim argumentem są flagi - flaga TIMER_ABSTIME pozwala na // ustawienie timera na czas absolutny (a nie okres czasu) timer_settime(timer, 0, &tSpec, NULL); char i=0; do { struct itimerspec tRest; timer_gettime(timer, &tRest); printf("do timera pozostało: %dns\n", tRest.it_value.tv_nsec); pause(); // czekamy na timer printf("aktualna liczba utraconych tyknięć timera wynosi %d\n", timer_getoverrun(timer)); } while(i++<3); // powyższa metoda zapewnia rozpoczynanie w równych odstępach czasu ... // w przypadku zastosowania sleep() należałoby obliczać czas przez który // wykonywał się kod i odejmować go od czasu który podajemy do sleep() // drzemka // nanosleep pozwala na określenie sekund i milisekund oraz zwraca czas // różnicę pomiędzy planowanym czasem spania a rzeczywistym czasem // od wejścia do powrotu z funkcji nanosleep() - uwzględniając np. czas // obsługi sygnału który przerwał działanie funkcji nanosleep() struct timespec drzemka, pobudka; drzemka.tv_sec=2; drzemka.tv_nsec=100000000; // chcemy spac 2.1s ale obudzi nas timer // to ile zesmy niedospali zostanie zapisane w pobudka if (nanosleep(&drzemka, &pobudka)<0 && errno == EINTR) printf("niedospalismy: %ds i %dns\n", pobudka.tv_sec, pobudka.tv_nsec); // usuniecie timera timer_delete(timer); }
/* PLIK: py_api.cpp kompilacja: g++ --std=c++11 -shared -fPIC -I/usr/include/python3.7m/ py_api.cpp \ -o MyPyAPI.so -lpython3.7m -lboost_python37 uwaga: - kolejność argumentów może być istotna - plik wynikowy powinien mieć taką samą nazwę jak moduł zadeklarowany przy pomocy BOOST_PYTHON_MODULE() */ #include <iostream> #include <string> #include <list> std::string f1(int a) { for (int i=0; i<a; ++i) std::cout << "Ala ma kota\n"; return "Kot ma Alę"; } struct K1 { int a; static K1* obj; int f1(int b) { return a + b; } static K1* get() { return obj; } }; void f2(K1& k, int n) { std::cout << "run f2(): " << k.f1(2*n) << "\n"; k.a += 1; } K1* K1::obj = NULL; #include <boost/python.hpp> BOOST_PYTHON_MODULE(MyPyAPI) { boost::python::def("f1", f1); boost::python::class_<K1>("Klasa") .def_readwrite("a", &K1::a) .def("f1", &K1::f1) // w pythonie w odróżnieniu od C++ referencja do klasy jest jawnym // argumentem metod nie statycznych zatem f2 które przyjmuje jako // pierwszy argument referencję do obiektu klasy K1 możemy użyć // jako metody klasy pythonowej .def("f2", f2) // typo z funkcji do pythona zwracana jest wartość zmiennej (a nie // referencja do zmiennej C++) jednak dla funkcji zwracających // wskaźnik lub referencję na ogół chcemy aby zwracana była właśnie // referencja do istniejącej zmiennej C++ .def("get", &K1::get, boost::python::return_value_policy< boost::python::reference_existing_object >() ) ; // f2 możemy też użyć jako niezależnej funkcji boost::python::def("f22", f2); // podobnie jak f1 boost::python::def("f11", &K1::f1); }
# skrypt wykorzystujący moduł # MyPyAPI stworzony w C++ # dodajemy bierzący katalog do ścieżki w której python szuka bibliotek import sys sys.path.append('./') # importujemy naszą bibliotekę (z pliku MyPyAPI.so) import MyPyAPI # uruchomienie funkcji f1 i odebranie wyniku ret = MyPyAPI.f1(2) print(ret) # utworzenie obiektu klasy K1 (Klasa) kk = MyPyAPI.Klasa() # oraz jego używanie ... kk.a = 3 print(kk.f1(2)) kk.f2(1) MyPyAPI.f22(kk, 1) print("kk.a =", kk.a) print(MyPyAPI.f11(kk, 1))
# skrypt używający biblioteki C # bez API pythonowego # skrypt powoduje włączenie NumLock # przy pracy w trybie graficznym # zgodnym z X11 from ctypes import * X11 = cdll.LoadLibrary("libX11.so.6") X11.XOpenDisplay.restype = c_void_p display = X11.XOpenDisplay(None); X11.XkbLockModifiers( c_void_p(display), c_uint(0x0100), c_uint(16), c_uint(16) ) X11.XCloseDisplay(c_void_p(display))
/* PLIK: py_run.cpp kompilacja: g++ -I/usr/include/python3.7m/ py_run.cpp -lpython3.7m -lboost_python37 uwaga: - kolejność argumentów może być istotna - plik używa "py_api.cpp" z poprzedniego przykładu - plik wywołuje skrypt "py_run.script.py" z bieżącego katalogu */ // korzystamy z wcześniej przygotowanego (w "API pythonowe biblioteki C++") interfejsu pythonowego #include "py_api.cpp" int main(int, char **) { K1 *o1 = new K1(); o1->a = 1; K1 *o2 = new K1(); o2->a = 2; K1::obj = o1; std::cout << "o1->a = " << o1->a << " o2->a = " << o2->a << "\n"; // initialise python Py_Initialize(); try { // initialise and import MyPyAPI module PyObject* module = PyInit_MyPyAPI(); PyDict_SetItemString(PyImport_GetModuleDict(), "MyPyAPI", module); Py_DECREF(module); PyRun_SimpleString("import MyPyAPI" ); // poprzez PyRun_SimpleString można też uruchamiać inne fragmenty kodu pythonowego // prepare to run scripts boost::python::object main = boost::python::import("__main__"); boost::python::object global(main.attr("__dict__")); // export object to python global["ck1"] = boost::python::ptr(o1); // run file boost::python::object result = boost::python::exec_file( "./py_run.script.py", global, global ); // import object from python boost::python::object script = global["script1"]; // run python function with args from C++ if(!script.is_none()) { // run scripts std::cout << "RUN\n"; std::cout << "return = " << boost::python::extract<int>( script(boost::python::ptr(o2)) ) << "\n"; } } catch(boost::python::error_already_set &) { PyErr_Print(); exit(-1); } std::cout << "o1->a = " << o1->a << " o2->a = " << o2->a << "\n"; delete o1; delete o2; return 0; }
# PLIK: py_run.script.py # uruchamiany przez kod C++ z py_api.cpp print("początek pliku .py") ck0 = MyPyAPI.Klasa.get() print("ck0:", ck0.f1(0)) print("ck1:", ck1.f1(0)) ck0.a=4 print("ck0:", ck0.f1(0)) print("ck1:", ck1.f1(0)) def script1(arg): print("run script1") print("ck0:", ck0.f1(0)) print("ck1:", ck1.f1(0)) print("arg:", arg.f1(0)) arg.a=13 print("ck0:", ck0.f1(0)) print("ck1:", ck1.f1(0)) print("arg:", arg.f1(0)) print("end script1") return arg.a + ck0.a; print("koniec pliku .py")
// przy kompilacji konieczne jest dodanie: // -lsqlite3 #include <sqlite3.h> #include <stdio.h> /* bazę można utworzyć zarówno w poniższym kodzie poleceniami SQL, * jak też z poziomu powłoki: cat << EOF | sqlite3 example.db CREATE TABLE posts (pid INT PRIMARY KEY, uid INT, text TEXT); INSERT INTO posts VALUES (1, 21, 'abc ..'); INSERT INTO posts VALUES (3, 2671, 'test'); EOF */ int main() { sqlite3 *db; if ( sqlite3_open("example.db", &db) ) { fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); sqlite3_close(db); return 1; } sqlite3_stmt *stmt; int ret = sqlite3_prepare_v2(db, "SELECT uid, text FROM posts", -1, &stmt, NULL ); while ( ( ret = sqlite3_step(stmt) ) == SQLITE_ROW ) { int uid = sqlite3_column_int(stmt, 0); const char* text = (const char*) sqlite3_column_text(stmt, 1); printf("uid = %d, text: %s\n", uid, text); } }
import sqlite3 import os.path if os.path.isfile('example.db'): create = False else: create = True conn = sqlite3.connect('example.db') c = conn.cursor() if create: print("create new db") c.execute("CREATE TABLE users (uid INT PRIMARY KEY, name TEXT);") c.execute("CREATE TABLE posts (pid INT PRIMARY KEY, uid INT, text TEXT);") c.execute("INSERT INTO users VALUES (21, 'user A');") c.execute("INSERT INTO users VALUES (2671, 'user B');") c.execute("INSERT INTO posts VALUES (1, 21, 'abc ..');") c.execute("INSERT INTO posts VALUES (2, 21, 'qwe xyz');") c.execute("INSERT INTO posts VALUES (3, 2671, 'test');") conn.commit() maxUid = 100 for r in c.execute("SELECT * FROM users WHERE uid < ?;", (maxUid,)): print(r) for r in c.execute("""SELECT u.name, p.text FROM users AS u JOIN posts AS p ON (u.uid = p.uid);"""): print(r)
<?php $sql_conn = mysqli_connect('server', 'user', 'password', 'database'); if (!$sql_conn) { echo "ERROR: " . mysqli_connect_error(); exit; } else { echo "Connected to MySQL: " . mysqli_get_host_info($sql_conn); } $query = $db->query("SELECT u.name, p.text FROM users AS u JOIN posts AS p ON (u.uid = p.uid);"); while ($row = $query->fetch_array()) { print($row["name"] . ": " . $row["text"] . "\n"); } ?>
# interfejs pseudo graficzny z użyciem biblioteki curces ... # a tak naprawdę programu dialog z niej korzystającego from dialog import Dialog from time import sleep d = Dialog(dialog="dialog") # tak naprawdę jest to wraper # wykorzystujący program o nazwie "dialog" # # jest w stanie (w ograniczonym zakresie) # współpracować z innymi tego typu narzędziami # (także działającymi w trybie graficznym) # takimi jak: whiptail, kdialog, ... # d = Dialog(dialog="kdialog", compat="kdialog") d.set_background_title("ABC ...") res = d.yesno("Tak czy NIE?") if res == d.OK: d.msgbox("TAK !!!") else: d.msgbox("NIE !!!") d.gauge_start("proszę czekać ... będzie rozmowa ...") for i in range(0, 101, 5): d.gauge_update(i) sleep(1)
# interfejs pseudo graficzny z użyciem Qt4 import sys from PyQt4 import QtGui, QtCore app = QtGui.QApplication(sys.argv) win = QtGui.QWidget() win.resize(250, 150) win.move(300, 300) win.setWindowTitle('ABC ...') def showDialog(): reply = QtGui.QMessageBox.question(None, 'Message', "Tak czy nie?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: print("YES") elif reply == QtGui.QMessageBox.No: print("NO") else: print("cos innego?!") button = QtGui.QPushButton('Nacisnij mnie', win) button.clicked.connect(showDialog) button.resize(button.sizeHint()) button.move( 250/2-button.sizeHint().width()/2, 150/2-button.sizeHint().height()/2 ) win.show()
# interfejs pseudo graficzny z użyciem Gtk 3 import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk window = Gtk.Window(title="Hello World") def showDialog(caller): dialog = Gtk.MessageDialog(window, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, "Ważne pytanie !!!") dialog.format_secondary_text("Tak czy nie?") response = dialog.run() if response == Gtk.ResponseType.YES: print("TAK !!!") elif response == Gtk.ResponseType.NO: print("NIE !!!") else: print("bez odpowiedzi!?") dialog.destroy() button1 = Gtk.Button("Nacisnij mnie") button1.connect("clicked", showDialog) window.add(button1) window.show_all() window.connect("delete-event", Gtk.main_quit) Gtk.main()
#include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> #include <linux/i2c-dev.h> #ifndef I2C_FUNC_I2C #include <i2c/smbus.h> #endif #include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> // wymagany pakiet libi2c-dev, kompilacja: // dla libi2c-dev < 4: // gcc komunikacja_I2C.c // dla libi2c-dev >= 4: // gcc -li2c komunikacja_I2C.c void help(const char *prog_name) { fprintf(stderr, "USAGE (without on chip register addres):\n"); fprintf(stderr, " - read: %s i2c_dev addr r -\n", prog_name); fprintf(stderr, " - write: %s i2c_dev addr w - data\n", prog_name); fprintf(stderr, "\n"); fprintf(stderr, "USAGE (with on chip register addres):\n"); fprintf(stderr, " - read: %s i2c_dev addr r reg_addr\n", prog_name); fprintf(stderr, " - write: %s i2c_dev addr w reg_addr data\n", prog_name); fprintf(stderr, "\n"); fprintf(stderr, "i2c_dev = /dev/i2c-... device path\n"); fprintf(stderr, "addr = I2C _devide_ address\n"); fprintf(stderr, "reg_addr = register (data) address in I2C chip\n"); fprintf(stderr, "data = data to write to device\n"); } int main(int argc, char *argv[]) { int fd, i2c_addr, reg_addr, res, d; if (argc < 4) { help(argv[0]); return 1; } // otwarcie urządzenia fd = open(argv[1], O_RDWR); if (fd < 0) { fprintf(stderr, "Error open I2C device file (%s): %s\n", argv[1], strerror(errno)); return 2; } // ustawienie adresu slave i2c_addr = strtol(argv[2], NULL, 0); res = ioctl(fd, I2C_SLAVE, i2c_addr); if (res < 0) { fprintf(stderr, "ERROR set device address to 0x%02x: %s\n", i2c_addr, strerror(errno)); return 3; } if (argv[3][0] == 'r' && argc == 5) { // odczyt if (argv[4][0] != '-') { // jeżeli jest podany to ustawiamy adres rejestru do odczytu // warto zauważyć że jest to robione jako WRITE do urządzenia I2C reg_addr = strtol(argv[4], NULL, 0); res = i2c_smbus_write_byte(fd, reg_addr); if (res < 0) { fprintf(stderr, "ERROR write (register address) %d to i2c device 0x%02x on %s: %s\n", reg_addr, i2c_addr, argv[1], strerror(errno)); return 4; } } // czytamy dane z urządzenia res = i2c_smbus_read_byte(fd); if (res < 0) { fprintf(stderr, "ERROR read from i2c device 0x%02x on %s: %s\n", i2c_addr, argv[1], strerror(errno)); return 5; } printf("0x%02x\n", res); } else if (argv[3][0] == 'w' && argc == 6) { // zapis d = strtol(argv[5], NULL, 0); if (argv[4][0] != '-') { // jeżeli jest podany adres rejestru do zapisu to go używamy i wykonujemy zapis reg_addr = strtol(argv[4], NULL, 0); res = i2c_smbus_write_byte_data(fd, reg_addr, d); if (res < 0) { fprintf(stderr, "ERROR write %d to i2c device 0x%02x, register 0x%02x on %s: %s\n", d, i2c_addr, reg_addr, argv[1], strerror(errno)); return 5; } } else { // w przeciwnym razie po prostu zapisujemy dane do urządzenia res = i2c_smbus_write_byte(fd, d); if (res < 0) { fprintf(stderr, "ERROR write %d to i2c device 0x%02x on %s: %s\n", d, i2c_addr, argv[1], strerror(errno)); return 5; } } } else if (argv[3][0] == 'R' && argc == 5 && argv[4][0] != '-') { // odczyt z rejestru jedną funkcją reg_addr = strtol(argv[4], NULL, 0); res = i2c_smbus_read_byte_data(fd, reg_addr); // odczyt z rejestru o danym adresie może być wykonany także jedną funkcją (analogicznie jak zapis) if (res < 0) { fprintf(stderr, "ERROR read from i2c device 0x%02x, register 0x%02x on %s: %s\n", i2c_addr, reg_addr, argv[1], strerror(errno)); return 5; } printf("0x%02x\n", res); } else { help(argv[0]); return 1; } close(fd); }
char*
i funkcje z rodziny string.h
) operuje się obiektami zawierającymi w sobie dane i posiadającym metody na nich operujące (np. napis typu std::string
).
Z obiektowością związane są zagadnienia takie jak: określanie interfejsów klas odnoszących się jedynie do kluczowych dla danego zagadnienie elementów i ukrywaniu pozostałych aspektów wewnątrz implementacji klasy, wydzielanie wspólnych cech obiektów różnego typu do klas bazowych po których dziedziczą.
// klasa abstrakcyjna - definiująca interfejs dla jakiejś grupy obiektów // fakt bycia klasą abstrakcyjną wynika z niezdefiniowanej metody wirtualnej // z faktu tego wynika niemożność utworzenia obiektów będących bezpośrednimi // instancjami takiej klasy class Ksztalt { public: virtual float objetosc() = 0; // użycie virtual wymusza wywoływanie metody z klasy potomnej // kiedy odwołujemy się przy użyciu klasy bazowej }; // klasy dziedzicząca po klasie Ksztalt class Kula : public Ksztalt { float promien; public: Kula(float r) { promien = r; } float objetosc() { return 1.25 * 3.14 * promien * promien * promien; } }; class Szescian : public Ksztalt { float bok; public: Szescian(float a) { bok = a; } float objetosc() { return bok*bok*bok; } }; struct Kolor { char r, g, b; }; class Cena { public: Cena(float c); }; class Material { }; // klasa dziedzicząca po kilku klasach bazowych struct Opakowanie : public Ksztalt, public Kolor, public Cena { Ksztalt *ksztalt; Material *material; public: Opakowanie(Ksztalt *k, const Kolor& kolor, float c) : // wywołanie konstruktorów klasy bazowej // jawnego dla Cena i kopiującego dla Kolor Cena(c), Kolor(kolor) { // inicjalizacja zmiennej wskaźnikowej ksztalt // dzięki rozwiązaniu z zmienną przechowującą informację o kształcie // nie ma potrzeby tworzenia różnych klas Opakowanie dla różnych kształtów ksztalt = k; material = 0; } Opakowanie(Ksztalt *k, float c) : // wywołanie konstruktora klasy bazowej Cena // oraz inicjalizacja zmiennych ksztalt i material Cena(c), ksztalt(k), material(0) { // inicjalizacja pól odziedziczonych po strukturze Kolor r = 0x96; g = 0xFF; b = 0x03; } float objetosc() { return ksztalt->objetosc(); } // jest to alternatywna w stosunku co do dziedziczenia // metoda rozszerzania interfejsu jakiejś klasy // dziedziczenie (w przypadku dziedziczenia public) dodaje metody // klasy bazowej bezpośrednio do interfejsu klasy dziedziczącej // natomiast to rozwiązanie pozwala na pobranie poszczególnych klas // opisujących złożony obiekt niezależnie i operowaniu na ich interfejsie Material* getMaterial() { return material; } void setMaterial(Material *m) { material = m; } };
// Szablon funkcji wypisującej na standardowe wyjście // listę dowolnego typu. Wymagane jest tylko aby dla tego // typu był zdefiniowany operator << z std::iostream. template <typename T> void wypiszListe(std::list<T>& l) { for (auto i : l) { std::cout << i << "\n"; } } // przykład użycia void wypisz() { std::list<int> x={1, 3, 7, 2, 3}; wypiszListe(x); std::list<float> z={2.7, 5.0, 3.1, 3.9}; wypiszListe(z); }
#include <iostream> struct Product { virtual void info() = 0; virtual ~Product() {} }; struct ProductA : public Product { virtual void info() { std::cout << "ProductA: a, b, c\n"; } }; struct ProductB : public Product { virtual void info() { std::cout << "ProductB: a, f, g\n"; } }; struct ProductFactory { static Product* getProduct(const std::string& type) { if (type == "A") return new ProductA(); else if (type == "B") return new ProductB(); else return NULL; } }; int main(int argc, char *argv[]) { std::string id = "A"; if (argc > 1 && argv[1][0] == 'B') id = "B"; Product* p = ProductFactory::getProduct(id); p->info(); delete p; }
#include <iostream> struct Product { virtual void info() = 0; virtual ~Product() {} }; struct ProductA: public Product { virtual void info() { std::cout << "ProductA: a, b, c\n"; } }; struct ProductB: public Product { virtual void info() { std::cout << "ProductB: a, f, g\n"; } }; struct Factory { static Factory* getFactory(const std::string& type); virtual Product* getProduct() = 0; // fabryka taka może z łatwością produkować kilka // różnych produktów (całą rodzinę produktów) virtual ~Factory() {} }; struct FactoryA: public Factory { virtual Product* getProduct() { return new ProductA(); } }; struct FactoryB: public Factory { virtual Product* getProduct() { return new ProductB(); } }; Factory* Factory::getFactory(const std::string& type) { if (type == "A") return new FactoryA(); else if (type == "B") return new FactoryB(); else return NULL; } int main(int argc, char *argv[]) { std::string id = "A"; if (argc > 1 && argv[1][0] == 'B') id = "B"; Factory* f = Factory::getFactory(id); Product* p = f->getProduct(); p->info(); delete p; delete f; }
#include <iostream> struct Product { void info() { std::cout << "Product:\n"; std::cout << " " << el1 << "\n"; std::cout << " " << el2 << "\n"; } void setElement1(const std::string& e) { el1 = e; } void setElement2(const std::string& e) { el2 = e; } private: std::string el1, el2; }; struct ProductBuilder { virtual ~ProductBuilder() {} void createProduct() { p = new Product(); } Product* getProduct() { return p; } // w takiej implementacji nie odebranie lub // nie skasowanie produktu przez klienta będzie // prowadziło do wycieków pamięci virtual void buildElement1(const std::string& d) = 0; virtual void buildElement2() = 0; protected: Product* p; }; struct ProductDirector { void setData(const std::string& d) { this->d = d; } void setBuilder(ProductBuilder* b) { this->b = b; } void createProduct() { b->createProduct(); b->buildElement1(d); b->buildElement2(); } private: ProductBuilder* b; std::string d; }; struct ProductBuilderA : public ProductBuilder { void buildElement1(const std::string& d) { p->setElement1(d + d); }; void buildElement2() { p->setElement2("ab"); }; }; struct ProductBuilderB : public ProductBuilder { void buildElement1(const std::string& d) { p->setElement1("b" + d); }; void buildElement2() { p->setElement2("bb"); }; }; int main(int argc, char *argv[]) { ProductBuilder* b; if (argc > 1 && argv[1][0] == 'B') b = new ProductBuilderB(); else b = new ProductBuilderA(); ProductDirector* d = new ProductDirector(); d->setData("x"); d->setBuilder(b); d->createProduct(); Product* p = b->getProduct(); p->info(); delete p; delete d; delete b; }
#include <iostream> class Singleton { private: // konstruktor, konstruktor kopiujący oraz operator // przypisania są prywatne aby uniemożliwić tworzenie // lub kopiowanie obiektów tej klasy z zewnątrz Singleton(); Singleton(const Singleton&) {} Singleton& operator=(const Singleton&); // destruktor też prywatny ... dla zabezpieczenia przed // możliwością zrobienia delete na uzyskanym wskaźniku ~Singleton() {} // klasa zawiera prywatny statyczny wskaźnik na siebie // (na swoją jedyną instancję) static Singleton* objPtr; public: // a także metodę służącą pobraniu tego wskaźnika // (i utworzeniu obiektu gdy nie istnieje) static Singleton* getPtr() { // alternatywnie zamiast wskaźnika można użyć // statycznego obiektu tej klasy zadeklarowanego // wewnątrz tej funkcji if (!objPtr) objPtr = new Singleton(); return objPtr; } }; Singleton* Singleton::objPtr = 0; Singleton::Singleton() { std::cout << "konstruktor Singleton\n"; } int main() { Singleton* a = Singleton::getPtr(); Singleton* b = Singleton::getPtr(); std::cout << a << " == " << b << "\n"; }
#include <iostream> struct Product { virtual ~Product() {} virtual void info() = 0; }; struct ProductA : public Product { void info() { std::cout << "Product:\n"; std::cout << " ab\n"; } }; struct DecoratorX : public Product { DecoratorX(Product* p) { this->p = p; } void info() { p->info(); std::cout << " xx\n"; } protected: Product* p; }; struct DecoratorY : public Product { DecoratorY(Product* p) { this->p = p; } void info() { p->info(); std::cout << " yy\n"; } protected: Product* p; }; int main(int argc, char *argv[]) { Product* p = new ProductA(); Product* p1 = new DecoratorX(p); Product* p2 = new DecoratorY(p1); p1->info(); p2->info(); delete p2; delete p1; delete p; }
#include <iostream> #include <list> struct Product { virtual void info() = 0; virtual ~Product() {}; }; struct ProductA : public Product { virtual void info() { std::cout << "ProductA: a, b, c\n"; } }; struct ProductB : public Product { virtual void info() { std::cout << "ProductB: a, f, g\n"; } }; class ProductComposite : public Product { public: std::list<Product*> elements; virtual void info() { for (auto e : elements) e->info(); } ~ProductComposite() { for (auto e : elements) delete e; } }; int main(int argc, char *argv[]) { ProductComposite* pc = new ProductComposite(); pc->elements.push_back(new ProductA()); pc->elements.push_back(new ProductB()); pc->elements.push_back(new ProductA()); pc->info(); delete pc; }
#include <iostream> #include <map> struct Obserwator { virtual bool onUpdate(int eventID) = 0; }; struct Obserwowany { void addListener(Obserwator* l, int key) { // sprawdź czy jest już zarejestrowany auto subset = listeners.equal_range(key); for (auto iter=subset.first; iter!=subset.second; ++iter) if (iter->second == l) return; // zarejestruj listeners.insert(std::pair<int, Obserwator*>(key, l)); } void remListener(Obserwator* l) { for (auto iter=listeners.begin(); iter!=listeners.end(); ++iter) { if (iter->second == l) { listeners.erase(iter); return; } } } // klasyczny wzorzec listenera void callListenersAll(int eventID) { // mapa zapewnia wywoływanie listenerów wg kolejności kluczy for (auto& l : listeners) { l.second->onUpdate(eventID); } } // wzorzec listenera połączony z łańcuchem zobowiązań void callListeners(int eventID) { // mapa zapewnia wywoływanie listenerów wg kolejności kluczy for (auto& l : listeners) { if (l.second->onUpdate(eventID)) break; } } protected: std::multimap<int, Obserwator*> listeners; }; struct ObserwatorA : public Obserwator { virtual bool onUpdate(int eventID) { std::cout << " ObserwatorA z id=" << eventID << "\n"; if (eventID == 4) return true; return false; }; }; struct ObserwatorB : public Obserwator { virtual bool onUpdate(int eventID) { std::cout << " ObserwatorB z id=" << eventID << "\n"; if (eventID == 7) return true; return false; }; }; int main(int argc, char *argv[]) { Obserwowany* o = new Obserwowany(); Obserwator* o1 = new ObserwatorA(); Obserwator* o2 = new ObserwatorA(); Obserwator* o3 = new ObserwatorB(); o->addListener(o1, 30); o->addListener(o2, 10); o->addListener(o3, 20); std::cout << "callAll:\n"; o->callListenersAll(7); std::cout << "call:\n"; o->callListeners(7); delete o; delete o1; delete o2; delete o3; }
#include <iostream> #include <map> struct Receiver { virtual void receive(std::string msg) = 0; }; struct Mediator { void addReceiver(Receiver* l, int key) { // sprawdź czy jest już zarejestrowany auto subset = receivers.equal_range(key); for (auto iter=subset.first; iter!=subset.second; ++iter) if (iter->second == l) return; // zarejestruj receivers.insert(std::pair<int, Receiver*>(key, l)); } void remReceiver(Receiver* l) { for (auto iter=receivers.begin(); iter!=receivers.end(); ++iter) { if (iter->second == l) { receivers.erase(iter); return; } } } bool send(std::string msg, int dstID) { // gdyby zamiast natychmiastowego wywoływania receive() u adresatów // zaimplementować gromadzenie wiadomości w jakiejś strukturze // i osobną funkcję wysyłającą kolejne wiadomości z tej struktury // to wzorzec ten pozwoliłby na realizację kolejki wiadomości // (pętli komunikatów) // // struktura przechowująca zakolejkowane do wysłania wiadomości może // uwzględniać kolejność ich odebrania przez Mediatora lub wartość // priorytetu zawartego w wiadomości (kolejka priorytetowa) auto subset = receivers.equal_range(dstID); for (auto& iter=subset.first; iter!=subset.second; ++iter) { iter->second->receive(msg); } } protected: std::multimap<int, Receiver*> receivers; };
class PID { // poprzednia różnica między wartością zadaną a otrzymaną // (poprzedni błąd regulacji / uchyb) float lastDiff; // poprzednia wartość otrzymana (zmienna procesu) float lastVal; // część całkująca, akumulowana pomiędzy krokami float integral; public: // nastawa - wartość zadana float setPoint; // wartość wyjścia dla sterowania krokowego // (akumulacja w układzie realizującym) float outStep; // wartość wyjścia dla sterowania sygnałem float outValue; // parametry regulatora PID float Kp, Ki, Kd; // limity wartości sterowanej float outValueMin, outValueMax; // konstruktor - inicjalizacja parametrów PID ( float initVal, float sp, float kp, float ki, float kd, float min, float max ) : lastDiff(0), lastVal(initVal), integral(0), setPoint(sp), outStep(0), outValue(0), Kp(kp), Ki(ki), Kd(kd), outValueMin(min), outValueMax(max) {} int doStep(float inputVal, float stepTime) { // obliczmy aktualny błąd regulacji // (na podstawie odczytanej wartości wejściowej) float diff = setPoint - inputVal; // wyłączamy regulację gdy prowadziłby do przesterowania if (outValue > outValueMax && diff > 0) return 1; if (outValue < outValueMin && diff < 0) return -1; // całkowanie przybliżamy jako jako suma pól trapezów integral += (diff + lastDiff) / 2 * stepTime; // różniczkowanie przybliżamy jako tangens nachylenia // prostej pomiędzy poprzednim krokiem a obecnym // celem złagodzenia odpowiedzi na zmiany wartości zadanej // różniczkujemy sygnał wejściowy float derivative = -(inputVal - lastVal) / stepTime; // a nie błąd regulacji: derivative = (diff - lastDiff) / stepTime; // obliczenie wartości sygnału sterującego na podstawie tego kroku outStep = Kp*diff + Ki*integral + Kd*derivative; // akumulacja sygnału sterującego outValue += outStep; // zapamiętanie aktualnego błędu regulacji // jako poprzedni dla następnego kroku lastDiff = diff; // zapamiętanie aktualnej wartości wejściowej // jako poprzedniej dla następnego kroku lastVal = inputVal; return 0; } }; int main() { PID pid(readInput(), 21.0, 1.0, 0.0, 0.0, -100.0, 100.0); // okres działania algorytmu [us] unsigned int stepTime = 250000; // pętla algorytmu while(1) { // odczyt wejścia float curVal = readInput(); // obliczenie wartości sterującej z użyciem PID pid.doStep( curVal, stepTime ); // wystawienie wartości sterującej setOutput(pid.outValue); // odczekanie do następnego kroku usleep(stepTime); } }
::
.2001:db8::a17/48
oznacza że pierwsze 48 bity stanowią adres sieci a kolejne 128-48 = 80 bitów stanowi adres hosta w tej sieci.2001:db8::/48
. Informacja taka jest wystarczająca do sprawdzenia czy dowolny inny adres IP należy do tej sieci czy nie.# adresacja IPv6 from ipaddress import * a1 = IPv6Address("2001:0db8::17:15") aa1 = int(a1) print("adress IPv6 jest 128 bitową liczbą całkowitą np.: " + str(a1) + " == " + hex(aa1)) n0 = IPv6Network("::/112"); m1 = n0.netmask mm1 = int(m1) p1 = n0.prefixlen print("Maska podsieci IPv6 jest 128 bitową liczbą całkowitą np.: " + str(m1) + " == " + hex(mm1)) print("Jako że maska jest liczbą, która zapisana binarnie, zawsze zawiera ciągły ciąg bitów") print("o wartości 1, a po nim ciągły ciąg bitów o wartości 0 (mogą być zerowej długości), to") print("często stosowany jest zapis polegający na podawaniu długości prefiksu: /" + str(p1)) print("jest to ilość bitów o wartości 1 w masce, czyli im większy prefix tym mniejsza sieć.") n1 = IPv6Network("2001:0db8::17:15/112", strict=False); nn1 = int(n1.network_address) print("Aby obliczyć adres sieci (czyli wspólną dla wszystkich hostów w danej sieci część") print("adresu IP) należy wykonać binarny AND pomiędzy adresem IP hosta a maską podsieci.") print("Dla powyższego przykładu:") print(hex(mm1 & aa1) + " == " + str(n1) + " == " + hex(nn1)) # aby sprawdzić czy adres IP należy do danej sieci trzeba obliczyć adres sieci tego hosta # w oparciu o maskę sieci którą sprawdzamy def sprawdzSiec(n, a): nn = int(a) & int(n.netmask) if nn == int(n.network_address): print(str(a) + " należy do sieci " + str(n)) else: print(str(a) + " NIE należy do sieci " + str(n)) sprawdzSiec(n1, IPv6Address("2001:0db8::17:ab13")) sprawdzSiec(n1, IPv6Address("2001:0db8::13:a"))
# adresacja IPv4 from ipaddress import * a1 = IPv4Address("192.168.34.17") aa1 = int(a1) print("adress IPv4 jest 32 bitową liczbą całkowitą np.: " + str(a1) + " == " + hex(aa1)) n0 = IPv4Network("0.0.0.0/25"); m1 = n0.netmask mm1 = int(m1) p1 = n0.prefixlen print("Maska podsieci IPv4 jest 32 bitową liczbą całkowitą np.: " + str(m1) + " == " + hex(mm1)) print("Jako że maska jest liczbą, która zapisana binarnie, zawsze zawiera ciągły ciąg bitów") print("o wartości 1, a po nim ciągły ciąg bitów o wartości 0 (mogą być zerowej długości), to") print("często stosowany jest zapis polegający na podawaniu długości prefiksu: /" + str(p1)) print("jest to ilość bitów o wartości 1 w masce, czyli im większy prefix tym mniejsza sieć.") n1 = IPv4Network("192.168.34.17/25", strict=False); nn1 = int(n1.network_address) print("Aby obliczyć adres sieci (czyli wspólną dla wszystkich hostów w danej sieci część") print("adresu IP) należy wykonać binarny AND pomiędzy adresem IP hosta a maską podsieci.") print("Dla powyższego przykładu:") print(hex(mm1 & aa1) + " == " + str(n1) + " == " + hex(nn1)) # aby sprawdzić czy adres IP należy do danej sieci trzeba obliczyć adres sieci tego hosta # w oparciu o maskę sieci którą sprawdzamy def sprawdzSiec(n, a): nn = int(a) & int(n.netmask) if nn == int(n.network_address): print(str(a) + " należy do sieci " + str(n)) else: print(str(a) + " NIE należy do sieci " + str(n)) sprawdzSiec(n1, IPv4Address("192.168.34.13")) sprawdzSiec(n1, IPv4Address("192.168.34.199"))
ciekawi.icm.edu.pl.
), którą najczęściej pomija się w zapisieNS
– informacja o serwerach obsługujących DNS danej domenyA
– mapowanie nazwy na adres IPv4AAAA
– mapowanie nazwy na adres IPv6MX
– informacja o serwerach obsługujących pocztę danej domenySRV
– informacje o hoście świadczącym usługę (usługa określana jest w nazwie domeny o którą pytamy)PTR
– mapowanie adresów IP na nazwy domenowe, realizowane w specjalnym drzewie in-addr.arpa
(dla IPv4) lub ip6.arpa
(IPv6),
gdzie adres IP zapisywany jest w odwróconej kolejności po bajcie dla IPv4 lub cyfrze szesnastkowej dla IPv6
(zobacz wynik polecenia host
z adresem IPv4 i IPv6)
TXT
– informacje dodatkowe (np. jakie serwery pocztowe, są upoważnione do wysyłania poczty z tej domeny)SOA
– informacje podstawowe o strefie opisującej domenęCNAME
– alias na inną domenę (domena którą aliasujemy nie może mieć innych wpisów, nawet SOA)/sbin/init
(którego podstawowym zadaniem jest zamontowanie właściwego rootfs). Po jego zakończeniu (lub od razu gdy nie używamy initrd) uruchamiany jest program wskazany w opcji init=
jądra (domyślnie typowo /sbin/init
) z rootfs wskazanego w opcji root=
jądra. W opcji init=
można wskazać dowolny program lub skrypt (uruchomiony zostanie z prawami root'a)./
), w którym zamontowany jest główny system plików (rootfs), inne systemy plików mogą być montowane w kolejnych katalogach. Do najistotniejszych katalogów należy zaliczyć: /root
, aby był dostępny nawet gdy takie montowanie nie doszło do skutku)/run
przeznaczony do trzymania danych tymczasowych działających usług takich jak numery pid, blokady, itp/sys
zawierający informacje i ustawienia dotyczące m.in. urządzeń|
, np: ls --help | less
. Konstrukcja ta przekieruje wynik komendy ls
uruchomionej z opcją --help
do komendy less
.>
lub~>>
, gdy chcemy dopisywać do pliku) lub pobrać standardowe wejście z pliku (przy pomocy <
). 2>
pozwala na przekierowanie standardowego wyjścia błędu do pliku.2>&1
w celu przekierowania strumienia drugiego do pierwszego. Następnie możemy użyć ~|
aby przekierować połączony strumień do następnej komendy. Jeżeli chcemy przekierować go do pliku połączenie strumieni powinno mieć miejsce po przekierowaniu pierwszego z nich do pliku, np.: ls . NieIstniejącyPlik >log.txt 2>&1
. Bash pozwala użyć >&
i |&
, które przekierowują oba strumienie odpowiednio do pliku lub standardowego wejścia innego polecenia, ale jest to rozszerzenie wykraczające poza standardową składnię sh.main
). Zero oznacza że polecenie zakończyło się sukcesem (np. znaleziono szukane pliki), wartość nie zerowa że zakończyło się porażką (np. nie ma pasujących plików) lub błędem (np. składnia wprowadzonego polecenia była niepoprawna).a && b
– polecenie b wykona się gdy a zakończyło się sukcesem (zwróciło kod 0)a || b
– polecenie b wykona się gdy a zakończyło się porażką lub błędem (zwróciło kod różny od~0)a ; b
– polecenie b po zakończeniu polecenia a (bez względu na jego kod powrotu)a & b
– polecenie b będzie wykonywane równocześnie z a (dokładniej polecenie a zostanie uruchomione w tle, a na terminal zajmie polecenie b)&
mogą być dodane do polecenia także gdy nie ma kolejnego w ciągu:
a&
uruchomi polecenie a w tle i odda linię poleceń,a;
uruchomi polecenie a (dokładnie tak samo jakby nie było tego średnika).apropos -s 1 ''
pozwala zapoznać się z spisem wszystkich stron w rozdziale pierwszym (co jest w którym rozdziale można zobaczyć w man man
)
--help
lub -h
, które wyświetlają informację na temat ich użycia. W tym miejscu warto też zauważyć że typowo opcje krótkie (pojedyncza litera) poprzedzane są pojedynczym myślnikiem (i po jednym myślniku może wystąpić kilka kolejnych opcji), natomiast opcje długie poprzedzane są dwoma myślnikami.|
. Np. a [b] c|d
oznacza iż polecenie a
wymaga argumentu postaci c
albo d
, który może być poprzedzony argumentem b
.?
oznaczający dowolny znak*
oznaczający dowolny (także pusty) ciąg znaków[a-z AD]
oznaczający dowolny znak z wymienionych w zbiorze ujętym w nawiasach kwadratowych, zbiór może być definiowany z użyciem zakresów, np. a-z AD oznacza dowolną małą literę od a do z włącznie, spację, dużą literą A lub D[!a-z]
oznaczający dowolny znak z wyjątkiem znaków wymienionych w podanym zbiorze, zbiór może być definiowany z użyciem zakresów, np. a-z oznacza dowolną małą literę od a do z włącznie\
, np. aaa\ bbb
) lub ujęcie całego napisu w cudzysłowie pojedynczym ('
, np. 'aaa bbb'
) lub podwójnym ("
, np. "aaa bbb"
). Oba typy cudzysłowów zabezpieczają przed rozwijaniem znaków uogólniających (zastępowaniem napisu ze znakami listą pasujących nazw / ścieżek). Cudzysłów pojedynczy (w odróżnieniu od podwójnego) zabezpiecza także przed interpretacją umieszczonych wewnątrz innych znaków specjalnych takich jak czy odwołania do zmiennych.
-D port
tworzy tunel dynamiczny na wskazanym porcie (może on być użyty jako proxy typu SOCKS np. w Firefoxie w celu zapewnienia dostępu do zasobów WWW dostępnych z serwera SSH a niedostępny z komputera lokalnego)-L portNasłuchiwania:hostZdalny:portZdalny
tworzy tunel przekierowujący dane kierowane na portNasłuchiwania komputera na którym działa klient ssh do portu portZdalny na serwerze hostZdalny poprzez serwer SSH (przydatne gdy hostZdalny jest osiągalny z hostSSH ale nie z komputera lokalnego)-R portNasłuchiwania:hostZdalny:portZdalny
tworzy tunel przekierowujący dane kierowane na portNasłuchiwania komputera na którym działa serwer ssh do portu portZdalny na serwerze hostZdalny dostępnego z klienta SSH (podobnie jak -L, tyle że w drugą stronę - nasłuchiwanie po stronie serwera a host zdalny po stronie klienta)-p port
określa inny niż domyślny port serwera SSH-X
aktywuje przekazywanie komend X serwera ze strony zdalnej do klienta (pozwala na uruchomienie po stronie zdalnej aplikacji z GUI, które zostanie wyświetlone na lokalnym X serwerze)
-i
pozwala na określenie co ile sekund ma aktualizować wynik polecenia/
), jednak nie jest to zbytnio wygodne. Interpreter poleceń taki jak bash
potrafi "znajdować się" gdzieś w tej strukturze plików i miejsce to nazywane jest bieżącym katalogiem roboczym (Present Working Directory). Względem niego będą wyrażane ścieżki nie zaczynające się od korzenia, może być też oznaczony jawnie przy pomocy pojedynczej kropki./
, bieżący katalog oznaczamy kropką .
, nadrzędny oznaczamy dwiema kropkami ..
,/
oznaczają ścieżki bezwzględne (od korzenia systemu plików), pozostałe oznaczają ścieżkę względną (wyrażoną względem bierzącego katalogu),~
Esc
powrót do trybu komendi
tryb wstawiania; A
tryb wstawiania ze skokiem na koniec linii, o
/ O
tryb wstawiania ze wstawieniem nowej linii po / przed bierzącąR
tryb zastępowaniaInsert
zmiana trybu wstawiania i zastępowaniav
tryb wizualny (umożliwia zaznaczenie przy pomocy strzałek); ctrl+v
tryb wizualny blokowy, V
tryb wizualny liniowy:set paste
włącza :set nopaste
wyłącza tryb wklejania (nie będzie działać automatyczne formatowanie itp.)gv
ponawia ostatnie zaznaczenie trybu wizualnegoy
skopiuj; d
- wytnij (skopiuj i usuń)
po y
, d
można podać np. 20l
lub 20[strzałka w prawo]
co oznacza 20 kolejnych znaków, 2w
oznacza dwa słowa
(więcej o takich punktach skoku poniżej)x
wytnij (skopiuj i usuń) znak (może być poprzedzone ilością znaków do wycięcia); wielkie X
działa analogicznie, tyle że w tyłyy
skopiuj linię; dd
- wytnij (skopiuj i usuń)
w obu wypadkach może być poprzedzone ilością linii do skopiowania/wycięciap
wkleja po; P
- wkleja przed"
i podajemy przed licznikiem, np. "a3dd
wytnie do rejestru a 3 linie), część rejestrów jest używana automatycznie, a niektóre są tylko do odczytu, podgląd aktualnej zawartości rejestrów możliwy jest przy pomocy komendy :registers
/
szukanie w przód, ?
szukanie w tył; *
szukanie w przód słowa pod kursorem, #
szukanie w tył słowa pod kursoremn
wyszukanie następnego wystąpienie; N
wyszukanie poprzedniego wystąpienieG
przejście do wskazanej linii, numer podajemy przed G, 0 oznacza ostatnią linię w pliku, więc 0G
spowoduje przejście do niej:[zakres]s@regexp@napis@[g]
wyszukaj i zastąp wyrażenie regularne regexp przez napis;
zakres może być:
numerem linii,
przedziałem z numerami linii postaci pierwsza,ostatnia
, gdzie:
.
oznacza bieżącą linię,
$
oznacza ostatnią linię w pliku,
wartość numeryczna poprzedzona +
oznacza tyle kolejnych linii od bieżącej, a poprzedzona -
przed bieżącą,
znakiem %
(co oznacza cały plik),
zakresem zaznaczonym w trybie wizualnym;
podanie opcji g powoduje zastępowanie wszystkich wystąpień a nie tylko pierwszego;
znak @
pełni rolę separatora i może zostać zamiast niego użyty inny znak:e ścieżka
otwarcie wskazanego pliku:w
zapis (można także podać ścieżkę pod jaka ma zostać zapisany plik):q
wyjście:q!
wyjście bez zapisywania:wq
zapis i wyjście:n
następny plik; :N
poprzedni plik:split
poziomy podział okna; :vs
pionowy podział okna; Ctrl
+W
a następnie strzałka - przełączanie między oknamiu
, :undo
cofa ostatnią operacjęCtrl+r
, :redo
ponawia cofniętą operacjęd
, y
):l
/ h
/ k
/ j
jeden znak/linię w prawo / lewo / górę / dół (działa tak jak strzałki)0
/ ^
/ $
początek linii / początek tekstu w linii, koniec liniiw
/ b
/ e
następne słowo / poprzednie słowo / koniec słowa; wielkie W
/ B
/ E
działa analogicznie, różni się traktowaniem spacji przy słowief
/ F
następny / poprzedni znak podany po tej komendzie, włącznie z nim (np. dfX
usunie wszystko do najbliższego wystąpienia X wraz z tym X); t
/ T
działa analogicznie, tyle wyłącza podany znak10l
- 10 znaków w prawo, 3F:
- trzeci dwukropek w lewoG
poprzedzane numerem linii do której ma się odbyć skokm
i następie znak ją identyfikujący - utworzenie zakładki w miejscu kursora (np. ma
- utworzy zakładkę a)`
(backtick) / '
(apostrof) skok do zakładki / linii z zakładką podaną po tej komendzie:marks
- lista zakładek; :delmarks
/ :delmarks!
- usunięcie zakładki / usunięcie wszystkich nie automatycznych zakładek:r plik
, wstawienie zawartości pliku:%!xxd
pokazanie wartości numerycznych i umożliwienie edycji pliku jako binarnego; :%!xxd -r
powrót do normalnej edycji>
/ <
zwiększanie / zmniejszanie wcięcia zaznaczonego (w trybie wizualnym) tekstuzc
zwija bieżący blok, zC
zwija bieżący blok aż do najwyższego poziomu, zo
rozwija bieżące zwinięcie, zO
rozwija rekurencyjnie bieżące zwinięcie, zR
rozwija wszystkie zwinięcia w dokumencie:set wrap
włącza :set nowrap
wyłącza zawijania linii w podglądzie-X
nie czyści ekranu przy wychodzeniu z less'a (całość historii wyświetlania pliku pozostaje w historii terminala)-F
automatycznie kończy gdy wyświetlany tekst mieści się na jednym ekranie
-R
przepuszcza surowe sekwencje sterujące terminalem dotyczące kolorów
hexdump -e '1/4 "%010d " 1/2 "%05d " 2/1 "%02x " "\n"' plik
każde kolejne 8 bajtów pliku zostanie zinterpretowane jako:
jedna liczba 32 bitowa liczba całkowita (4 bajty), 1