Innanzitutto vi devo ringraziare per il grande aiuto che mi state dando.
Non capisco il perché deve convertire la key in maiuscolo e poi nella table [][] non capisco quale dei due array corrisponda alla caratteri maiuscoli e minuscoli.
A giudicare da quell'if che hai mostrato si capisce che se le lettere minuscole iniziano dalla posizione 26 in avanti.
C:
if (isupper(ch)) {
putc(table[key[count] - 65][ch - 65] + 65, out);
}
else {
putc(table[key[count] - 65][ch - 97 + 26] + 97, out);
}
Poi dalla linea 53 alla 57 (o dalla 96 alla 107 nel metodo decrypt) non riesco proprio a capire il ragionamento dietro a quelle operazione..da quel poco che ho capito prende un carattere per volta con getc(), vede se il carattere è maiuscolo o minuscolo e in base a quello fa determinati calcoli.
Quello che viene fatto qui:
C:
fseek(in, 0, SEEK_END); // cerco di terminare il file
size = ftell(in); // ottiene il puntatore al file corrente
fseek(in, 0, SEEK_SET); // cerca di tornare all'inizio del file
for (i = n; i < size - 1; i++) {
key[i] = key[i - n];
}
while (count != size)
{
ch = getc(in);
if (isupper(ch)) {
putc(table[key[count] - 65][ch - 65] + 65, out);
}
else {
putc(table[key[count] - 65][ch - 97 + 26] + 97, out);
}
count++;
}
prima del for setta il puntatore al file (cursore) alla fine del file; poi legge il numero di bytes. In buona sostanza "size" conterrà la lunghezza del testo che è presente nel file. Poi il puntatore viene riposizionato all'inizio del file, in posizione 0.
L'algoritmo di Vigenere hai capito come funziona?
La prima colonna (quindi la posizione 0 di ogni riga), identifica il carattere della key.
La prima riga identifica invece il carattere da cifrare. Il punto in cui si intersecano le due rette è la lettera che ottieni come carattere cifrato.
La particolarità è che ogni singola riga, a partire dalla seconda, è shiftata di 1 posizione. Quindi la prima riga inizia con la lettera A, la seconda con B, la terza con C e via discorrendo.
Diciamo che hai questo testo da cifrare:
e come key usi invece:
Come noti la lunghezza del testo e quello della key non coincide. Il ciclo for che vedi li sopra va a "colmare" la differenza.
In sostanza il ciclo inizia da n, lunghezza della key, e copia i caratteri stessi della key nelle posizioni successive, sino al raggiungimendo di 'size' (lunghezza del testo).
Quindi alla fine avrai:
Codice:
TESTODIESEMPIO
TOMSHWTOMSH
A questo punto il while usa un indice chiamato "count" per scorrere ogni singolo carattere della key, e di conseguenza del testo (sono lunghi uguali ora).
Tramite
getc()
legge, come dicevi, dal buffer di input, che in questo caso è il file aperto in precedenza.
Il carattere letto viene verificato con isupper per controllare se è maiuscolo o minuscolo: se è maiuscolo entra nell'if (riporto il codice, per comodità):
C:
if (isupper(ch)) {
putc(table[key[count] - 65][ch - 65] + 65, out);
}
else {
putc(table[key[count] - 65][ch - 97 + 26] + 97, out);
}
viene considerato quindi il carattere in posizione
count
(che sarà 0, al primo ciclo): l'elemento della riga della matrice viene ottenuto facendo
key[count] - 65
. Questo perchè viene preso il primo carattere della key, nell'esempio che ho fatto sopra la lettera 'T', e viene sottratto 65 ('A', in ASCII) = 19. La riga selezionata è la nr. 19 della matrice; se guardi la matrice messa anche sotto spoiler da Grave e conterai le righe della colonna 1, vedrai che la 19 sarà la lettera T.
La posizione del carattere da cifrare, quindi il testo in chiaro (letto dal file), lo si ottiene facendo ch-65: questo perchè 'ch' in questo momento è compreso tra 'A' e 'Z'. La matrice in queste posizioni contiene il numero 12: se sommi 12+65=77, che è la lettera M.
Se tiri una linea immaginaria dalla T della riga (perchè è la lettera della key che stiamo considerando) e dalla colonna T, vedrai che il punto di incontro è sulla M.
Poi ch essendo un char, quando viene fatto ch-65, come fa a capire che deve convertire in ascii?
Perdona le tante domande ma tutto ciò mi interessa assai.
Figurati, le domande sono sempre ben accette. ?
Praticamente tutti (non tutti in realtà) i linguaggi consentono di effettuare questa operazione; il cast del tipo avviene in automatico.
Da notare che lui
non capisce che deve convertire in ASCII. Ogni numero (considera ad esempio la tabella linkata sopra da ilfe98) se lo casti esplicitamente in char ha una corrispondenza: ciò che succede è che viene preso il numero e viene mostrato sullo schermo secondo la codifica ASCII. Non tutti i caratteri sono visualizzabili, quindi se stampi caratteri non visualizzabili, vedrai dei simboli strani.
Considera l'Invio ad esempio (l'andare a capo): questo è un carattere non visualizzabile, in Windows è codificato con due byte noti come CR LF (fai riferimento sempre alla tabella ASCII): Carriege Return e Life Feed. In una stringa in C vedrai:
\r\n
. Se li stampi sul terminale e poi scrivi altro, vedrai che va a capo.
P.s. il for come lo ottimizzeresti te?
Cambierei ben più del for. Ho modificato un pò il codice mostrato sul sito:
C:
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
int table[26][26];
void init_matrix()
{
for (int i = 0; i < 26; i++)
{
for (int j = 0; j < 26; j++) {
table[i][j] = (j + i) % 26;
}
}
}
void encrypt(char *filename, char *key, int n)
{
int count = 0, size = 0;
char ch;
FILE *in, *out;
in = fopen(filename, "r");
if (in == NULL)
{
printf("Cannot open source file.\n");
exit(1);
}
out = fopen("CipherText.txt", "w+");
if (out == NULL)
{
printf("Cannot open destination file.\n");
exit(1);
}
fseek(in, 0, SEEK_END); // cerco di terminare il file
size = ftell(in); // ottiene il puntatore al file corrente
fseek(in, 0, SEEK_SET); // cerca di tornare all'inizio del file
for (int i = n; i < size - 1; i++) {
key[i] = key[i - n];
}
while (count != size)
{
ch = getc(in);
int ch_size = isupper(ch) ? 65 : 97;
putc(table[key[count] - 65][ch - ch_size] + ch_size, out);
count++;
}
fclose(in);
fclose(out);
}
void decrypt(char *key)
{
int size = 0, count = 0;
char ch;
FILE *in, *out;
in = fopen("CipherText.txt", "r");
if (in == NULL)
{
printf("Cannot open encrypted file.\n");
exit(1);
}
out = fopen("DecryptedText.txt", "w+");
if (out == NULL)
{
printf("Cannot open destination file.\n");
exit(1);
}
fseek(in, 0, SEEK_END); // cerco di terminare il file
size = ftell(in); // ottiene il puntatore al file corrente
fseek(in, 0, SEEK_SET); // cerca di tornare all'inizio del file
while (count != size)
{
ch = getc(in);
int ch_size = isupper(ch) ? 65 : 97;
for (int i = 0; i < 26; i++)
{
if (table[key[count] - 65][i] == ch - ch_size) {
fputc(i + ch_size, out);
}
}
count++;
}
fclose(out);
fclose(in);
}
// funzione principale
int main()
{
char filename[] = "PlainText.txt";
char key[] = "TOMSHW";
int n = strlen(key);
// converte la chiave in maiuscolo
for (int j = 0; j < n; j++) {
key[j] = toupper(key[j]);
}
init_matrix();
// crittografa usando il codice Vigenère
encrypt(filename, key, n);
// decifrare usando il codice Vigenère
decrypt(key);
return 0;
}
Mi sembra più semplice di prima scritto in questo modo. Prova a dargli uno sguardo, poi domanda pure.
Faccio sempre notare che non ho risolto il bug presente, ovvero:
C:
for (int i = n; i < size - 1; i++) {
key[i] = key[i - n];
}
mi sono infatti accorto che - ovviamente - produce problemi.
EDIT: se fai un print della tabella, ora è:
Codice:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 0
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 0 1
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 0 1 2
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 0 1 2 3
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 0 1 2 3 4
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 0 1 2 3 4 5
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 0 1 2 3 4 5 6
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 0 1 2 3 4 5 6 7
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 0 1 2 3 4 5 6 7 8
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 0 1 2 3 4 5 6 7 8 9
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 0 1 2 3 4 5 6 7 8 9 10
12 13 14 15 16 17 18 19 20 21 22 23 24 25 0 1 2 3 4 5 6 7 8 9 10 11
13 14 15 16 17 18 19 20 21 22 23 24 25 0 1 2 3 4 5 6 7 8 9 10 11 12
14 15 16 17 18 19 20 21 22 23 24 25 0 1 2 3 4 5 6 7 8 9 10 11 12 13
15 16 17 18 19 20 21 22 23 24 25 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
16 17 18 19 20 21 22 23 24 25 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
17 18 19 20 21 22 23 24 25 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
18 19 20 21 22 23 24 25 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
19 20 21 22 23 24 25 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
20 21 22 23 24 25 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
21 22 23 24 25 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
22 23 24 25 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
23 24 25 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
24 25 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
25 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
ovvero una 26x26.
PS. per evitare il bug si può sempre usare l'operazione "modulo". Ora sono da telefono, ma a occhio dovrebbe bastare un "count % n" come indice di key.