Изменить и обернуть ключевые слова целыми числами без цикла в C - PullRequest
0 голосов
/ 13 октября 2019

Я пишу программу, которая принимает строку в командной строке, а затем преобразует каждый символ строки в соответствующую цифру 0-25 алфавита. Затем каждая цифра используется для шифрования каждого символа другой строки, которую вводит пользователь после запроса программы. Каждый буквенный символ второй строки должен соответствовать порядку строки целых чисел, и строка целых чисел будет перенесена, если вторая строка длиннее. Целью программы является использование первой строки в качестве ключа для сдвига каждого символа сообщения (вторая строка).

Пример (желаемый вывод): пользователь запускает программу и вводит ключевое слово: bad

Пользователю предлагается ввести строку буквенных символов и только знаки препинания: Dr. Oz

Программа преобразует ключевое слово «плохо» в 1,0,3

Программа шифрует сообщение в Er. Ra

Что я на самом деле получаю:

… T.B.S. …

Я пробовалмногие вещи, но, к сожалению, я не могу понять, как зациклить и обернуть ключ без повторения второго сообщения. Если вы запустите программу, вы увидите мою проблему.

#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

int shift(char key1);

int main(int argc, string argv[]) // user enter number at cmd prompt
{
    if (argv[1] == '\0')
    {
        printf("Usage: ./vigenere keyword\n");
        return 1;
    }
    string key = argv[1]; // declare second arg as string
    for (int i = 0, n = strlen(key); i < n; i++)
        if (isdigit(key[i]) != 0 || argc != 2)
        {
            printf("Usage: ./vigenere keyword\n");
            return 1;
        }
    string text = get_string("plaintext: ");
    printf("ciphertext: ");
    int k;
    char t;

    for (int j = 0, o = strlen(text); j < o; j++)
    {
        t = text[j];
        for (int i = 0, n = strlen(key); i < n; i++)
        {
            k = shift(key[i]);
            if (isupper(t))
            {
                t += k;
                if (t > 'Z')
                {
                    t -= 26;
                }
            }
            if (islower(t))
            {
                t += k;
                if (t > 'z')
                {
                    t -= 26;
                }
            }
            printf("%c", t);
        }
    }

    printf("\n");
}

int shift(char key1)
{
    int k1 = key1;
    if (islower(key1))
    {
        k1 %= 97;
    }
    if (isupper(key1))
    {
        k1 %= 65;
    }
    return k1;
}

Я ценю любую помощь и предложения, но имейте в виду, что решение должно соответствовать уровню кодирования, предложенному моей программой. Может быть много продвинутых способов написания этой программы, но, к сожалению, мы все еще находимся в начале этого курса, поэтому показ новых методов (которые я обязательно попытаюсь понять) может пойти мне на ум.

1 Ответ

0 голосов
/ 14 октября 2019

Вот модифицированная версия вашего кода с изменениями, основанными на моих комментариях:

#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

int shift(char key1);

int main(int argc, string argv[]) // user enter number at cmd prompt
{
    if (argc != 2 || argv[1][0] == '\0')
    {
        fprintf(stderr, "Usage: ./vigenere keyword\n");
        return 1;
    }
    string key = argv[1]; // declare second arg as string
    for (int i = 0, n = strlen(key); i < n; i++)
    {
        if (!isalpha(key[i]))
        {
            fprintf(stderr, "Usage: ./vigenere keyword\n");
            return 1;
        }
    }
    string text = get_string("plain text: ");
    printf("ciphertext: ");

    int keylen = strlen(key);
    int keyidx = 0;
    for (int j = 0, o = strlen(text); j < o; j++)
    {
        int t = text[j];
        if (isupper(t))
        {
            int k = shift(key[keyidx++ % keylen]);
            t += k;
            if (t > 'Z')
                t -= 26;
        }
        else if (islower(t))
        {
            int k = shift(key[keyidx++ % keylen]);
            t += k;
            if (t > 'z')
                t -= 26;
        }
        printf("%c", t);
    }

    printf("\n");
}

int shift(char key1)
{
    if (islower(key1))
        key1 -= 'a';
    if (isupper(key1))
        key1 -= 'A';
    return key1;
}

Тест для ровно двух аргументов и непустого ключа перемещен в верхнюю часть. Это немного отличается от того, что было предложено в комментариях. Сообщения об ошибках печатаются со стандартной ошибкой, а не со стандартным выводом. Вероятно, я бы заменил второе сообщение об использовании более конкретной ошибкой - the key may only contain alphabetic characters или около того. И ошибки должны включать argv[0] в качестве имени программы, а не в жестком кодировании имени. Цикл проверки ключа проверяет, что ключ является буквенным, вместо того, чтобы проверять, не являются ли они цифрами - имеется больше классов символов, чем цифр и букв. Код использует keyidx и keylen для отслеживания длины ключа и положения в нем. Я использую однобуквенные имена переменных, но обычно только для индексов цикла или простых указателей (обычно указателей на строки);в противном случае я использую короткие полумнемические имена. Существует два вызова shift(), поэтому keyidx увеличивается только тогда, когда вводимый символ является буквой. Есть и другие способы, которыми это может быть закодировано.

Одним очень важным изменением, не предсказанным в комментариях, является изменение типа для t - с char на int. Когда это char, если вы шифруете букву z с буквой в конце алфавита (например, y), значение 'z' + 24 выходит за пределы (подписанного) типа char, преобладающего на машинах Intel, даваяотрицательное значение (чаще всего; формально поведение не определено). Это приводит к фиктивным результатам. Изменение на int устраняет эту проблему. Поскольку значение t в любом случае увеличивается до int при передаче в printf(), печать не наносит вреда. Я использовал подсказку plain text: с пробелом, чтобы вход и выход совпадали на странице.

Я решил не использовать дополнительную локальную переменную k1 в shift(). Я также использовал вычитание вместо модуля, как отмечено в комментариях.

Учитывая программу cc59, созданную из cc59.c, пример запуска:

$ cc59 bad
plain text: Dr. Oz
ciphertext: Er. Ra
$ cc59 zax
plain text: Er. Ra
ciphertext: Dr. Oz
$ cc59 ablewasiereisawelba
plain text: The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs. The five boxing wizards jump quickly. How vexingly quick daft zebras jump. Bright vixens jump; dozy fowl quack. 
ciphertext: Tip uqius fisef fkb uvmpt zzar lpi cehq dkk. Abck nj fkx oqxy jqne zskfn ljbykr bckj. Xpw fezp coxjyk sirivuw rmml ufjckmj. Lkw nmbzrody mytdk dbqx vetzej ncep. Xvthht wtbank rydt; lgzu jzxl qvlgg.
$ cc59 azpweaiswjwsiaewpza
plain text: Tip uqius fisef fkb uvmpt zzar lpi cehq dkk. Abck nj fkx oqxy jqne zskfn ljbykr bckj. Xpw fezp coxjyk sirivuw rmml ufjckmj. Lkw nmbzrody mytdk dbqx vetzej ncep. Xvthht wtbank rydt; lgzu jzxl qvlgg.
ciphertext: The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs. The five boxing wizards jump quickly. How vexingly quick daft zebras jump. Bright vixens jump; dozy fowl quack.
$

Ключи дешифрования были полученысопоставляя буквы «шифрования» в строке 1 с буквами дешифрования в строке 2 данных:

abcdefghijklmnopqrstuvwxyz
azyxwvutsrqponmlkjihgfedcb

При шифровании и дешифровании наиболее простой кислотный тест для кода заключается в том, что программа может расшифровать еесобственный зашифрованный вывод с правильным ключом дешифрования и зашифрованным текстом.

...