Продолжаем делать домашние задания по курсу Computer science CS50 (Harvard), и в этот раз нужно написать код программы и зашифровать текстовое послание.
Суть задания ceasar.c заключается в том, чтобы программа запрашивала ввод текста пользователя, а затем выводила ниже строку текста в зашифрованном виде с ключом пользователя. Ключ же указывается при запуске программы в командной строке после называния самой программы argv[]. Так в примере запуска готовой программы пользователь вводит ключ = 13 и свой текст «Hello, world!», а на выходе получает этот текст в виде шифра:
Важно, чтобы в программе соблюдались следующие условия:
- если пользователь вводит заглавные буквы, то в шифре тоже появляются заглавные, если строчные — то строчные;
- если пользователь вводит что-то, кроме букв, то эти символы печатаются в исходом виде;
- ключ для шифрования, который пользователь передает программе при запуске через командную строку, может быть положительным числом, это число не обязательно соответствует количеству букв в алфавите, а может быть и больше, например, 100 — в этом случае исходная буква заменяется не на +100 символ ASCII, а на букву в промежутке от a до z или от A до Z, отстоящую от исходной на 100 шагов вперед;
- формула для расчета индекса сдвига букв при шифровании:
Ci=(Pi+key)%26
,
где Ci — зашифрованная буква, Pi — исходная буква, key — число ключ шифрования.
Решение Игроглаза #1. Решение получилось несколько избыточным в плане дополнительной кодировки символов; с другой стороны оно может пригодится в следующем задании.
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> int only_digits(char *argv[]); int main(int argc, char *argv[]) { int i, n, j, x; int key; // cipher key char text[1000]; // text input char cypher[1000]; // cypher output char AZ[26], az[26]; // alphabetical indexes // in case if user didn't input any console argument if (!(argc == 2)) { printf("Usage: ./caesar key"); return 1; } // check console argument (char) and convert them to (int) key = only_digits(argv); if (key < 0) { printf("Usage: ./caesar key"); return 2; } printf("plaintext: "); for (n = 0; ; n++) // we also count number of input for the next cycle { scanf("%c", &text[n]); // input if (text[n] == '\n') // if input ended - put new line break; } // build A-Z and a-z indexes for (i = 0, j = 65; i < 26; i++) // note: AZ array elements are 0-25 AZ[i] = j++; for (i = 0, j = 97; i < 26; i++) az[i] = j++; printf("ciphertext: "); for (i = 0; i < n; i++) { if (text[i] >= 'A' && text[i] <= 'Z') // print capital { text[i] -= 65; // to move ASCII to 0 text[i] = (text[i] + key)%26; // key formula to move past 'z' x = (int)text[i]; // convert char to int printf("%c", AZ[x]); // print based on index } else if (text[i] >= 'a' && text[i] <= 'z') // print small { text[i] -= 97; text[i] = (text[i] + key)%26; x = (int)text[i]; printf("%c", az[x]); } else printf("%c", text[i]); // print rest characters if (text[i] == '\n') break; } getchar(); getchar(); return 0; } int only_digits(char **argv) { unsigned int i; unsigned int len = strlen(argv[1]); for (i = 0; i < len; i++) { if (argv[1][i] < '0' || argv[1][i] > '9') return -1; else // convert argv 'number' characters to real number return strtol(argv[1], NULL, 10); } // Error: no argv was processed return -2; }
Первый вариант был корректным, но не прошел тестирование cs50. Вот исправленный вариант #2 от Игроглаза:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> int only_digits(char *argv[]); int main(int argc, char *argv[]) { int i, n, j, x; int key; // cipher key char text[1000]; // text input char cypher[1000]; // cypher output char AZ[26], az[26]; // alphabetical indexes // in case if user didn't input any console argument if (!(argc == 2)) { printf("Usage: ./caesar key"); return 1; } // check console argument (char) and convert them to (int) if (only_digits(argv) == 1) { printf("Usage: ./caesar key"); return 1; } // convert argv 'number' characters to real number key = strtol(argv[1], NULL, 10); if (key < 0) { printf("Usage: ./caesar key"); return 1; } printf("plaintext: "); for (n = 0; ; n++) // we also count number of input for the next cycle { scanf("%c", &text[n]); // input if (text[n] == '\n') // if input ended - put new line { break; } } // build A-Z and a-z indexes for (i = 0, j = 65; i < 26; i++) // note: AZ array elements are 0-25 { AZ[i] = j++; } for (i = 0, j = 97; i < 26; i++) { az[i] = j++; } printf("ciphertext: "); for (i = 0; i < n; i++) { if (text[i] >= 'A' && text[i] <= 'Z') // print capital { text[i] -= 65; // to move ASCII to 0 text[i] = (text[i] + key) % 26; // key formula to move past 'z' x = (int)text[i]; // convert char to int printf("%c", AZ[x]); // print based on index } else if (text[i] >= 'a' && text[i] <= 'z') // print small { text[i] -= 97; text[i] = (text[i] + key) % 26; x = (int)text[i]; printf("%c", az[x]); } else { printf("%c", text[i]); // print rest characters } if (text[i + 1] == '\n') { printf("\n"); break; } } return 0; } int only_digits(char **argv) { unsigned int i; unsigned int len = strlen(argv[1]); for (i = 0; i < len; i++) { if (!isdigit(argv[1][i])) { return 1; } } return 0; }
Решение (Штукенция):
#include <cs50.h> #include <stdio.h> #include <ctype.h> #include <string.h> #include <stdlib.h> char rotate (char c, int n); int main(int argc, string argv[]) { // Check if user write key number correctly as string argv[]: if (argc!=2) { printf("Error: command-line arguments\n"); return 1; } int k=1; int i=0; int key=0; for (i=0; argv[k][i];i++) { int k_check = isdigit(argv[k][i]); if (k_check==0) { printf("Usage: ./caesar key\n"); return 1; } } // Turn our key number string argv[1] to integer key_number: char* endPtr; int key_number = strtol(argv[1],&endPtr,10); // Get use input of plaintxtx: printf("plaintext:"); string plaintxtx = get_string(" "); int textlen = strlen(plaintxtx); // Turn characters from plaintxtx to ciphertext with function char c; // character from plaintxtx char c_new; // newmade rotated character int n = key_number; // key that user made printf("ciphertext: "); int j=0; for (j=0;j<=textlen;j++) // print char by char rotated letters { c = plaintxtx[j]; c_new = rotate(c,n); printf("%c", c_new); } printf("\n"); return 0; } char rotate (char c, int n) { int check_c = isalpha(c); // check if char is letter if (check_c==0) // if char is not letter, return as it is return c; else { char c_new=' '; // rotated newmade character int check_upper = isupper(c); // check if char is uppercase if (check_upper>0) { int c_asci = (c); // convert char LETTER to int ASCII number (A->65) int num_letter=(c_asci-65); // find numer of upper letter in alphabet (index) int shifted_index=(num_letter+n)%26; // shift index of letter with key number c_new=shifted_index+65; // convert ASCII number back to new letter return c_new; } else if (check_upper==0) { int c_asci = (c); // convert char LETTER to int ASCII number (a->97) int num_letter=(c_asci-97); // find numer of lower letter in alphabet (index) int shifted_index=(num_letter+n)%26; // shift index of letter with key number c_new=shifted_index+97; // convert ASCII number back to new letter return c_new; } else return 'N'; } }
И после тестирования багов мой выглядит так:
#include <cs50.h> #include <stdio.h> #include <ctype.h> #include <string.h> #include <stdlib.h> char rotate(char c, int n); int main(int argc, string argv[]) { // Check if user write key number correctly as string argv[]: if (argc != 2) { printf("Error: command-line arguments\n"); return 1; } int k = 1; int i = 0; int key = 0; for (i = 0; argv[k][i]; i++) { int k_check = isdigit(argv[k][i]); if (k_check == 0) { printf("Usage: ./caesar key\n"); return 1; } } // Turn our key number string argv[1] to integer key_number: char *endPtr; int key_number = strtol(argv[1], &endPtr, 10); // Get use input of plaintxtx: printf("plaintext:"); string plaintxtx = get_string(" "); int textlen = strlen(plaintxtx); // Turn characters from plaintxtx to ciphertext with function char c; // character from plaintxtx char c_new; // newmade rotated character int n = key_number; // key that user made printf("ciphertext: "); int j = 0; for (j = 0; j < textlen; j++) // print char by char rotated letters { c = plaintxtx[j]; c_new = rotate(c, n); printf("%c", c_new); } printf("\n"); return 0; } char rotate(char c, int n) { int check_c = isalpha(c); // check if char is letter if (check_c == 0) // if char is not letter, return as it is { return c; } else { char c_new = ' '; // rotated newmade character int check_upper = isupper(c); // check if char is uppercase if (check_upper > 0) { int c_asci = (c); // convert char LETTER to int ASCII number (A->65) int num_letter = (c_asci - 65); // find numer of upper letter in alphabet (index) int shifted_index = (num_letter + n) % 26; // shift index of letter with key number c_new = shifted_index + 65; // convert ASCII number back to new letter return c_new; } else if (check_upper == 0) { int c_asci = (c); // convert char LETTER to int ASCII number (a->97) int num_letter = (c_asci - 97); // find numer of lower letter in alphabet (index) int shifted_index = (num_letter + n) % 26; // shift index of letter with key number c_new = shifted_index + 97; // convert ASCII number back to new letter return c_new; } else { return 'N'; } } }