Руководство по проблеме Подмена в CS50, неделя 2.
Цель: Написать программу на C, которая реализует шифр замены, как показано ниже.
$ ./substitution JTREKYAVOGDXPSNCUIZLFBMWHQ
plaintext: HELLO
ciphertext: VKXXN
Программа должна шифровать только буквы, независимо от того, прописные они или строчные.
$ ./substitution VCHPRZGJNTLSKFBDQWAXEUYMOI
plaintext: hello, world
ciphertext: jrssb, ybwsp
Он должен принимать только один аргумент командной строки, ключ, который будет использоваться для подстановки. Этот ключ должен иметь длину ровно 26 символов, принимать только буквы и не учитывать регистр.
Затем он должен запросить открытый текст и вернуть зашифрованный зашифрованный текст.
Сначала мы импортируем необходимые библиотеки и определяем константу N, которая представляет собой требуемую длину ключа, и строку символов в алфавите.
#include <stdio.h> #include <cs50.h> #include <ctype.h> #include <string.h> const int N = 26; const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Затем объявите функцию main() и убедитесь, что она может принимать только один аргумент командной строки.
int main(int argc, string argv[]) { // Check for correct number of args if (argc != 2) { printf("Please provide one command line argument only.\n"); return 1; }
Если эта проверка пройдена, программа может продолжить работу, и мы можем проверить правильность аргумента командной строки. Это должно соответствовать следующим условиям:
- Можно использовать только буквы.
- Не должно быть повторяющихся букв.
Я начал с объявления пустого целочисленного массива длины N, называемого буквами, который я буду использовать в следующем цикле.
Затем ключ можно перебирать символ за символом, сначала проверяя, есть ли только буквы, используемые с несколькими основными операторами и оператором if.
// Check validity of key content int letters[N]; for (int i = 0, n = strlen(argv[1]); i < n; i++) { // Check only letters are used if (!((argv[1][i] >= 'a' && argv[1][i] <= 'z') || (argv[1][i] >= 'A' && argv[1][i] <= 'Z'))) { printf("Key must contain only letters.\n"); return 2; }
Если эта проверка пройдена, я преобразую весь ключ в верхний регистр с помощью toupper() по причинам, которые станут ясны позже.
// Convert to uppercase else if (argv[1][i] >= 'a' && argv[1][i] <= 'z') { argv[1][i] = toupper(argv[1][i]); }
Теперь можно проверить наличие повторяющихся букв, что я и сделал, используя вложенный цикл for и определенный ранее массив букв. Первая итерация этого цикла пройдет, так как массив букв изначально пуст. Добавляя текущую букву в ключе к массиву букв в конце каждого внешнего цикла, а затем перебирая массив букв во внутреннем цикле, мы можем убедиться, что буквы не повторяются.
// Check for repeated letters for (int j = 0; j < N; j++) { if (argv[1][i] == letters[j]) { printf("Key must not contain repeated letters.\n"); return 3; } } letters[i] = argv[1][i]; }
С этими проверками, переданными в аргументе командной строки, программа теперь может запросить у пользователя шифрование ввода. Здесь также определяется массив пустого зашифрованного текста с длиной l + 1, чтобы оставить место для нулевого терминатора.
// Ask for plaintext string plaintext = get_string("plaintext: "); int l = strlen(plaintext); char ciphertext[l + 1];
Этот простой текст теперь может быть преобразован с использованием определенного ключа шифрования. Это делается путем перебора каждого символа в простом тексте и их последовательного преобразования. Если обычный текстовый символ имеет верхний регистр, символ преобразуется непосредственно из ключа, который, если вы помните, был преобразован во все прописные буквы.
Внутренний цикл проходит по алфавиту, чтобы проверить, является ли символ обычного текста буквой или нет. Если это так, символ простого текста преобразуется в зашифрованный текст с использованием соответствующего индекса ключа шифрования.
// Convert to ciphertext // Loop through each char in plaintext for (int i = 0; i < l; i++) { // Check if uppercase and if so use standard alphabet/key if (isupper(plaintext[i]) != 0) { for (int j = 0; j < N; j++) { if (plaintext[i] == alphabet[j]) { ciphertext[i] = argv[1][j]; break; } } }
Аналогичный процесс можно выполнить, если обычный текстовый символ имеет нижний регистр, что проверяется с помощью функции islower(). Эта функция повторяется для преобразования в символ шифрованного текста нижнего регистра.
// If lowercase use lowercase alphabet and key else if (islower(plaintext[i]) != 0) { for (int j = 0; j < strlen(alphabet); j++) { if (plaintext[i] == tolower(alphabet[j])) { ciphertext[i] = tolower(argv[1][j]); break; } } }
Если условные проверки зашли так далеко, это не должна быть буква, а символ зашифрованного текста будет просто таким же, как и обычный текстовый эквивалент.
// Finally replace non letters else { ciphertext[i] = plaintext[i]; } }
Наконец, добавьте нулевой символ в конец зашифрованного текста, чтобы сделать его строкой, и распечатайте результат.
// Add null char to make it a string ciphertext[l] = '\0'; // Print result and exit printf("ciphertext: %s\n", ciphertext); return 0; } }
И это замена, наше первое, но определенно не последнее знакомство с использованием аргументов командной строки и всем, что с ними связано.