martedì 27 gennaio 2015

Quando un parametro speciale ti fa sentire meglio [bash]

Salve!

Come si dice, l'appetito vien mangiando. Vabbè, una cosa alla volta. Stasera, mentre scrivevo il mio "scriptino" quotidiano in bash, mi sono ritrovato di fronte ad una problematica apparentemente semplice. O forse era veramente semplice, ma sapete, sto invecchiando...

Comunque, immaginate di avere una variabile il cui valore sia costituito da tante stringhe, proprio come vi mostro nell'esempio sotto.

programmi="libreoffice vlc remmina liferea"

Bene, il discorso è questo, sto estrapolando da uno script lunghissimo, cerco di essere il più chiaro possibile, però.

In pratica voglio che lo script, ad un certo punto, controlli se nella variabile, di cui sopra, ci sia la stringa "libreoffice" e, solo se c'è, installi alcuni componenti aggiuntivi.

La domanda è la seguente: Come dobbiamo impostare il codice? Provate a dare una risposta senza leggere oltre.

Io ho fatto così:

programmi="libreoffice vlc remmina liferea"
echo "$programmi" | grep "libreoffice"
if [ $? -eq 0 ]; then
    apt-get install libreoffice-help-it libreoffice-l10n-it libreoffice-gtk -y
else
    echo "Niente da fare"
fi


La soluzione, in realtà, è veramente semplice, basta ricordarsi dell'esistenza del parametro speciale $?.

Cosa fa il $?

Restituisce lo stato dell’ultimo comando eseguito in foreground (primo piano).

Il risultato di $? può essere 0 o 1.

Restituisce 0 se è andato tutto bene, in caso contrario, restituisce 1.

Facciamo un esempio.

programmi="vlc remmina liferea"


Come potete vedere, ho eliminato la stringa "libreoffice" dalla variabile programmi. Il comando grep ovviamente non la trova e $? restituisce 1.



Se invece grep la trova, il $? restituisce 0, e dato che l'if confronta $? con lo 0, eseguirà il comando sperato.

Alla prossima!

venerdì 16 gennaio 2015

Modern C++ - Esercizio - Il Cifrario di Cesare

Bentornati sulle pagine del corso di Modern C++, ospitato sulle pagine di Lubit - The Secrets of Ubuntu, diretto amabilmente da Luigi bit3lux Iannoccaro. Fin qui, abbiamo visto insieme alcuni degli elementi principali del C++: vettori, variabili, condizioni e cicli. Oggi vedremo tante, tantissime novitá, per chi si avvicina per la prima volta alla programmazione o ad un linguaggio come il C++.
Luigi ha postato un piccolo script in bash che effettua la codifica/decodifiche di stringhe usando il Cifrario di Cesare. E´ uno dei piú antichi algoritmi di crittografia ad oggi pervenuto. Perchè non realizzarlo in anche in Modern C++?
L'algoritmo (sulla carta) è molto semplice: si sostituisce ad ogni lettera contenuta in una parola, un'altra lettera dell'alfabeto, la quale ha una posizione, definita chiave, successiva a quella della lettera originale.
Ad esempio, se la chiave è 3, come quella usata da Cesare nel suo cifrario, alla lettera A corrisponde la lettera E, alla lettera B corrisponde la lettera E e così via. 

Su Wikipedia esiste giá una versione di questo algoritmo, che vi invito a testare personalmente, ma è scritto seguendo il cosídetto "C-Style", usando printf e vettori ad allocazione statica (ovvero che non si possono modificare in lunghezza) ed una chiave fissa, pari a 3.

Quella che vi presento, è una versione Modern C++ realizzata da me personalmente, che consente di utilizzare una chiave diversa ed effettuare la codifica e la decodifica di una stringa di caratteri:
Il codice è commentato nel dettaglio, spiegando riga per riga, l'operazione eseguita.

Idea di Base

Al fine di realizzare il Cifrario di Cesare, con chiave variabile, ci occorrono due elementi: la stringa da codificaree la chiave: pertanto, avremo bisogno di due parametri, che l'utente andrá ad inserire a riga di comando. In questo caso, si è scelto di inserire tali parametri come argomenti del programma (chiamato per convenienza, caesar), ovvero nel seguente modo:
caesar "stringa da codificare" chiave

Quindi, avremo bisogno di importare tali parametri nel nostro codice e analizzarli. Poichè lo spazio è usato come indicatore di fine stringa (ovvero, non è un carattere alfanumerico), ogni parola separata dallo spazio è interpretata come un parametro aggiuntivo. Al fine di prevenire ció, dovremo inserire l'intero testo delimitato dagli apici "". In tal modo, la stringa all'interno dei delimitatori è interpretata come un unico parametro. Nell'algoritmo, non vengono considerate le vocali accentate.

Come applicare il cifrario di Cesare in modo semplice?
Per prima cosa, occorre definire i limiti dell'alfabeto: la 'a' è il limite inferiore, mentre la 'z' rappresenta il limite superiore. Quando viene valutato un singolo carattere della stringa, occorre valutare le seguenti condizioni:
  1. è un carattere alfanumerico? 
  2. la chiave usata è positiva (cifratura) o negativa (decifratura)?
  3. il nuovo carattere ottenuto dalla trasformazione, è ancora un carattere contenuto tra 'a' e 'z'?
In C++, ma piú in generale in ASCII, un carattere è codificato come un valore numerico. Alla 'a' corrisponde il numero 97, alla 'z' il numero 122. Occorre pertanto valutare se il carattere codificato sia maggiore o uguale a 97 oppure minore o uguale a 122. Se queste condizioni non sono verificate, occorre calcolare la differenza tra il valore attuale e quello del limite corrispondente e ripartire dal limite stesso.
Esempio:

caesar "zorro" 3       #esegue la codifica di zorro con chiave = 3

  • z+3 = 125, ma 125 corrisponde a '}' in ASCII, quindi:
    • differenza = 125 - z  #(nuovo - valore di 'z')
    • valore_attuale = a + (differenza - 1)  #si riparte da 'a', considerando la 'a' stessa
    • quindi valore_attuale = 97 + 3 - 1 = 99 = 'c'
    • quindi z+3 = 'c'
  • o+3 = 114 ('o' = 111 in ASCII)
    • 114 <= 122, quindi o+3 = 'r' = 114
e così via.

Se eseguiamo invece:

caesar "acqua" -3      #esegue la codifica di acqua con chiave = -3 (al contrario) 

  • a-3 = 94 = '^' in ASCII, ma 94 < 97, quindi:
    • differenza = 97 - 94 (valore di 'a' - valore del nuovo carattere)
    • valore_attuale = z - (differenza + 1) #si riparte da z, considerando la z stessa
    • quindi valore_attuale = 122 - 3 + 1 = 120 = 'x'
    • quindi, a-3 = 'x'
  • c-3 = 96 = '`', ma 96 < 97, quindi:
    • differenza = 97 - 96 = 1
    • valore_attuale =  122 -1 +1 = 'z'
    • quindi c-3 = 'z'
e cosí via.

Analizziamo il codice sorgente

Per prima cosa, noterete che includo gli header files cctype e algorithm: sono librerie STL che contengono alcune funzioni utili per l'esecuzione delle operazioni a noi utili: std::isalnum(char x) è una funzione che ha come risultato true se il carattere contenuto in x è un carattere alfanumerico, altrimenti restituisce false; std::transform ci permette di eseguire una operazione in sequenza su ognuno degli elementi di un array. Un array, poichè una stringa è un array di caratteri, dove l'elemento 0 (il primo) corrisponde al primo carattere e l'ultimo elemento del vettore corrisponde all'ultimo elemento della stringa. ATTENZIONE: std::string non è da confondersi con char stringa[], poichè quest'ultimo è un array di tipo char, il cui ultimo elemento è il carattere '\0', indicante il termine di una stringa. Modern C++ usa in modo quasi esclusivo std::string, poichè gli array di tipo char costituiscono una fonte di errori notevoli e non sono espandibili, se non mediante una nuova dichiarazione e allocazione di memoria.

Ho dichiarato la variabile key come variabile globale, ovvero è possibile accedere al valore contenuto nella variabile, da qualsiasi funzione del programma. Ho fatto questo, per poter usare key all'interno della funzione codifica(char &x).

La funzione char codifica(char &x) consente di eseguire l'algoritmo sul singolo carattere della stringa usata come parametro della nostra applicazione.
Essa valuta il valore di x: la notazione char &x indica che come parametro della funzione codifica, occorre fornire un valore di tipo char il cui dato è quello che fa riferimento al valore di x. Vedremo meglio questo aspetto nelle prossime lezioni: basti solo pensare che il parametro &x è passato per riferimento e puó esser modificato, modificando il valore originario del parametro stesso, anche quando la funzione termina. L'alternativa è il passaggio per valore, ovvero si effettua una copia della variabile e al termine della funzione, la copia viene distrutta, mentre il valore originale di x viene preservato.

Tale funzione, poi, restituisce un valore di tipo char, ovvero il carattere codificato con chiave positiva o negativa. Il corpo della funzione è documentato nel codice sorgente stesso e l'algoritmo è stato giá esaminato precedentemente.

La nota importante è la conversione implicita da char ad int. Questo è possibile che avvenga poichè char è rappresentato da 1 byte (=8 bit) e assume valori da -2^8 a (2^8)-1, ovvero da -128 a +127. Il tipo di dato int invece è rappresentato da 4 byte (=8*4=32 bit), ma varia da macchina a macchina (alcune architetture usano il tipo di dato int da 2 byte) e assume valori da -2^32 fino (2^32)- 1, ovvero puó contenere valori molto piú ampi. La conversione da int a char è possibile finchè il vaore di int rientra tra -128 e 127, altrimenti si ottengono risultati disastrosi ed inaspettati.

La funzione mostraHelp() permette di visualizzare un messaggio di testo in uscita, in caso di errori durante l'immissione dei parametri da passare al programma. È di tipo void, ovvero non restituisce alcun valore al termine della sua esecuzione.

All'interno dei main, abbiamo interessanti novitá: per prima cosa, all'interno del blocco switch, importiamo i parametri: come commentato nel codice sorgente, argc indica il numero di parametri (parole) scritte a riga di comando, dove argc=1 rappresenta la posizione e argv[0] rappresenta il nome nome dell'eseguibile che stiamo lanciando, mentre argc > 1 e argv[1], argv[2], ecc. indicano successivi parametri passati al nostro eseguibile.
Il tipo di dato di argv[] è char*, ovvero si tratta di un array di caratteri di dimensione non specificata, che viene definita solamente in base al numero di parametri immessi. È un retaggio del C, che è rimasto in adozione anche in C++.
La funzione atoi, al rigo 85, effettua la conversione da valore alfanumerico a intero (alphanumeric to integer), ovvero converte il parametro della chiave, ovvero il 3, da char a int, questo perchè i parametri sono passati come caratteri a riga di comando.

Al rigo 103, creo una nuova stringa, contente il carattere " da eliminare dall'input (in C++, il caratter ", al fine di usarlo come carattere di I/O, va anticipato dal carattere di escape \, quindi \" = "), al fine di non eseguire l'algoritmo anche su tale carattere: avrei potuto eliminarlo grazie a std::isalnum, ma l'ho lasciato per dimostrare il funzionamento di std::erase: tale funzione infatti, elimina ogni occorrenza di " dalla stringa di input e salva il risultato nella stessa stringa. Quindi, per ogni simbolo della stringa chars, si esegue erase, la quale rimuove iterativamente (remove) dall'inizio della stringa source (source.begin()) fino alla fine della stessa (source.end()) ogni occorrenza del simbolo symbol.
Le funzioni erase e remove (ma anche tante altre funzioni della STL) usano gli iteratori, ovvero un oggetto che punta a determinati elementi di un array: begin() rappresenta l'elemento 0, end() rappresenta la posizione N (con N-1 = ultimo elemento del vettore).
Si usano ancora gli iteratori per la funzione transform, prima per convertire la stringa in lower case (tutti i caratteri in minuscolo), grazie a ::tolower e poi per eseguire la codifica col cifrario di Cesare. Nel caso di transform, si specificano come parametri:
  • inizio della stringa di origine = target.begin()
  • fine della stringa di origine = target.end()
  • inizio della stringa di destinazione = target.begin() -- sovrascrivo la stringa target!
  • funzione che effettua la trasformazione = codifica()
A questo punto, alla funzione codifica viene passato il parametro contente il riferimento al carattere della stringa che si sta valutando: nel caso di "zorro", verranno chiamati codifica('z'), codifica('o'), codifica('r'), codifica('r'), codifica('o').

L'output del nostro programma è il seguente:



Per compilare il progetto, create un nuovo progetto con QtCreator come mostrato qui e nel file CMakeLists.txt cambiate il nome del progetto in Caesar.

Buona codifica!

PS. Tra la mia versione e quella fornita da Wikipedia, quale risulta piú chiara, la versione C++ o quella in Modern C++? Scrivetelo nei commenti!

mercoledì 14 gennaio 2015

Il Codice di Cesare [Brevi Esercizi]

Salve!

Uno degli esempi più significativi di crittografia a chiave simmetrica dell'età classica fu senza dubbio il cifrario di Cesare.

Come funziona codesto codice?

Si tratta di un cifrario a sostituzione monoalfabetica: Ogni lettera di una parola viene sostituita con un'altra in base ad una chiave.

Si dice che Cesare usasse come chiave K=3

Esempio:

Parola in chiaro: Luca
Parola cifrata: OAGD

Stabilito che la chiave è 3, la lettera L va sostituita con la lettera O, la lettera U con la lettera A, la lettera C va sostituita con la G e la lettera A con la lettera D.  Chiave 3 significa che bisogna contare tre posti in avanti nell'alfabeto (italiano, nell'esempio), a partire dalla lettera data, che va esclusa dal computo.

Ho creato uno script in bash, semplice semplice, che cifra e decifra in base al cifrario di Cesare. 

#!/bin/bash
#Cifrario di Cesare
#bit3lux@gmail.com

cifrare() {
#chiave=3
#Alfabeto di 26 lettere, senza spazi.
clear
alfabeto=$(echo {a..z} | sed 's/ //g')
k3="DEFGHIJKLMNOPQRSTUVWXYZABC"
read -p "digita il testo da cifrare: " testo
echo $testo | tr "[:upper:]" "[:lower:]" | tr "$alfabeto" "$k3"
}

decifrare() {
clear
alfabeto=$(echo {a..z} | sed 's/ //g') 
k3="DEFGHIJKLMNOPQRSTUVWXYZABC"
read -p "digita il testo da decifrare: " testo
echo $testo | tr "[:lower:]" "[:upper:]" | tr "$k3" "$alfabeto"
}

clear
echo "Per cifrare digita 0, per decifrare digita 1."
echo
read scelta

if [ $scelta == 0 ]
then
    cifrare
    echo

elif [ $scelta == 1 ]
then
    decifrare
    echo

else
    clear && echo "Opzione non valida, per scegliere digita 0 o 1"
    echo

fi

exit 0

Cifrare
Decifrare
Alla prossima!

venerdì 9 gennaio 2015

Modern C++ - I cicli e le condizioni

Ben ritrovati sulle pagine di Lubit - The Secrets of Ubuntu :-)
Nel post precedente abbiamo visto brevemente come definire una collezione dinamica, chiamata array, di elementi dello stesso tipo e come aggiungere/rimuovere elementi da essa.
Abbiamo anche visto come è possibile accedere agli elementi del vettore mediante un ciclo for. Oggi esamineremo piú nel dettaglio le possibili varianti di cicli esistenti in C++ e le condizioni per le quali è possibile iterare all'interno dei cicli.
Alla base delle condizioni e dei cicli esistono gli operatori di comparazione:
== uguale (identico)
<= minore o uguale
>= maggiore o uguale
< minore
> maggiore
!= diverso
E' possibile combinare tra loro piú operatori di comparazione mediante gli operatori logici:
! NOT restituisce true se l'operando è falso
&& AND restituisce true solo se gli operandi sono veri entrambi
|| OR restituisce true se almeno uno dei due operandi è vero

Le condizioni

Esistono 2 tipologie di condizioni: le condizioni basate su if e le condizioni basate su switch(case). Nel primo caso, si valuta se il risultato di una operazione assuma valore true oppure false (tipo di dato bool), mentre nel secondo caso si valuta il valore specificato nel case.
La sinossi per le condizioni basate su if è la seguente:
if(condizione)
{
   //operazioni
}
else
{
   //altre operazioni
}
dove per condizione ci riferiamo ad una operazione il cui risultato restituito è un valore booleano: true oppure false.
E' possibile scrivere piú condizioni if-else annidate. La regola generale è che il primo else si riferisce sempre all'ultimo if specificato.
La condizione del ramo else puó anche esser omessa, se non necessaria.

La sinossi per le condizioni basate su switch(case), invece, è la seguente:
switch(valore o variabile da valutare)
{
    case valore: operazione(); break;
    case 'valore'(se è un carattere): operazione2(); break;
    default: std::cout << "operazione non supportata" << std::endl;
}
Al termine di ogni caso (case) và specificata la parola chiave break, la quale permette di uscire dal blocco switch e ritornare ad eseguire il programma principale. Senza la parola break, verranno eseguite in cascata tutte le condizioni contenute all'interno del blocco switch. La parola chiave default invece permette di specificare un messaggio o una operazione da eseguire nel caso in cui una delle precedenti casistiche non sia verificata. Si noti che lo switch si sostituisce a blocchi if-else multipli.

Esempio:
int i{10}; //i = 10
if(i == 1) //i è uguale a 1?
{
    std::cout << "La variabile i è 1" << std::endl;
}
else if(i == 2) //i è uguale a 2?
{
    std::cout << "La variabile i è 2" << std::endl;
}
else if(i == 10) //i è uguale a 10?
{
    std::cout << "La variabile i è 10" << std::endl;
}
else
{
    std::cout << "i contiene il valore " << i << std::endl;
 
switch(i) //valuto il contenuto di i:
{
   case 1:  std::cout << "i contiene valore 1" << std::endl; break;
   case 2:  std::cout << "i contiene valore 2" << std::endl; break;
   case 10: std::cout << "i contiene valore 10" << std::endl; break;
   default: std::cout << "i contiene il valore " << i << std::endl;
}
 
In alternativa al classico blocco if-else, è possibile anche scrivere la forma contratta (condizione) ? operazione_se_true() : operazione_se_false();
Esempio:
i == 10 ? cout << "i vale 10" : cout << "i non vale 10";

I Cicli

Esistono 3 tipi di cicli: i cicli for, i cicli while e i cicli do-while.

For
Come abbiamo giá visto nel post precedente, un ciclo for ha la seguente sinossi:
for(valore_iniziale; condizione_finale; incremento)
{
   //operazioni;
}
oppure, in Modern C++, per vettori o container di elementi:
for(elemento:array)
{
   //operazioni;
}
Il ciclo for viene eseguito solo se la condizione finale è true. Non appena essa diventa false, il ciclo for viene interrotto. E' possibile inoltre forzare l'interruzione del ciclo mediante la parola chiave break, la quale permette di concludere il blocco-for immediatamente. Nel caso in cui, invece, si voglia saltare all'iterazione successiva (incrementando lo step), occorre usare la parola chiave continue.
Esempio:
std::vector<int> vettore(10, 10); //10 elementi, di valore 10
int somma = 0;
for(int i = 0; i < vettore.size(); ++i) //per i = 0, se i < 10 esegui operazioni
{
   if(vettore.at(i) == 0)   //se l'elemento i-esimo è 0, salta all'elemento ++i (i = i +1)
       continue;
   if(vettore.at(i) < 0)    //se l'elemento i-esimo è minore di 0, termina il ciclo
       break;

   somma = somma + vettore.at(i); //aggiungi l'elemento i-esimo alla somma
} //al termine di ogni ciclo, la variabile i è incrementata di 1 (++i)

While
In alternativa al ciclo for, si puó utilizzare il ciclo while, che viene eseguito fintanto che una condizione sia vera. Si pensi, ad esempio, ad un valore di soglia da raggiungere prima di interrompere le iterazioni.
La sinossi è la seguente:
while(condizione)
{
   //operazione
}

Do-While
In contrasto al ciclo while, il do-while viene eseguito almeno una volta, prima di verificarne la condizione di uscita.
La sinossi è la seguente: 
do
{
   //operazione
}while(condizione);
Vediamo nel dettaglio un breve esercizio/esempio:
int i{10}; //i = 10
while(i < 10)
{
    std::cout << i << std::endl;
    i++; //equivale a scrivere: i = i+1
}

do
{
    std::cout << i << std::endl;
    i++; //equivale a scrivere: i = i+1
}
while(i < 10);
Qual è il risultato dell'esempio precedente? Scrivete nei commenti il risultato è il perchè della risposta :)

Bonus:
E' possibile eseguire i cicli all'infinito (sconsigliatissimo) nel seguente modo:
for(;;)
{
   //operazioni
}

while(true)
{
   //operazioni
}

Alla prossima!