JavaRush /Курсы /Harvard CS50 /Задание 2. Parlez-vous français?

Задание 2. Parlez-vous français?

Harvard CS50
2 уровень , 10 лекция
Открыта

Условие

Написать программу vigenere.c, которая шифрует сообщение с помощью шифра Виженера.

На вход программы подаем один аргумент командной строки: ключевое слово k, состоящее из букв английского алфавита. Если приложение запускается более чем с одним аргументом или с аргументом не входящим в алфавит, нужно вывести информацию об ошибке с завершением программы. То есть main будет возвращать 1 — в таком случае наши автоматические тесты поймут, что здесь всё хорошо, и это условие учтено. Если всё хорошо, программа должна перейти к запросу строки текста p, который мы и шифруем полученным выше ключом k, напечатать результат и завершить выполнение программы, возвратив значение 0

Уточнение

Нужно сделать так, чтобы в ключе k символы A и a обозначались как 0, B и b как 1, …, Z и z как 25. Программа должна применять шифр Виженера только к буквам текста p. Остальные символы (цифры, знаки препинания, пробелы) нужно вывести без изменений. Если алгоритм собирается применить j-й символ k к i-му символу p, не входящему в алфавит, применяем этот j-й символ ключа к следующему алфавитному символу в тексте; вы не можете просто оставить его и перейти к другому символу в k. Наконец, программа должна сохранить регистр каждой буквы в p

Не знаете, с чего начать?

Задание 2. Parlez-vous français? - 1

Вот вам несколько советов от Замайлы, ассистента курса CS50

К счастью, реализация этой задачки очень похожа на реализацию шифра Цезаря. Только в качестве ключа используется не целое число, а строка. Если вы успешно реализовали шифр имени римского правителя, он может стать прекрасным стартом для второго задания. Вы, вероятно, уже смекнули, что шифр Виженера с одной буквой в качестве ключа — это тот же шифр Цезаря. 

В алгоритме Виженера применяются те же шаги, что и в «Цезаре»: 

  1. Получить ключ
    • кодовое слово — это второй аргумент командной строки argv[1]
    • должен входить в алфавит: функция isalpha
  2. Получить текст
  3. Зашифровать
  4. Напечатать шифрованный текст

Итак, второй аргумент командной строки argv[1] проверим на принадлежность к алфавитным символам. Делаем это с помощью уже знакомой isalpha. Если ключ корректен, получаем от пользователя строку и начинаем шифровать. 

Формула шифра Виженера похожа на формулу шифра Цезаря. Каким образом вы преобразуете букву на соответствующее смещение шифра? Попробуйте сравнить значения по таблице ASCII. 

Задание 2. Parlez-vous français? - 2

Скорее всего, у вас получится отыскать закономерность между буквами и их алфавитными индексами, используя последовательности в таблице. Догадались, как отнять одну букву от другой, чтобы получить желаемый результат? Смещения для больших и малых букв неодинаковы, так что вам придется определить две похожие формулы для определения смещения для строчных и отдельно для прописных букв. 
Также не забудьте, что цикл прохода по тексту должен игнорировать символы, не входящие в английский алфавит. И не забудьте сохранить регистр букв.

Если посмотреть на формулу шифра: 

ci = (pi + kj) % 26

вы увидите две индексные переменные, i и j. Одна сохраняет позицию в исходном тексте, другая — в ключе. Если ваш текст длиннее ключа, индекс по ключу проходит с конца ключа снова в его начало.

Как это сделать? С помощью операции деления по модулю! Результат операции — остаток от деления двух чисел. Практическая польза этой операции в программировании просто огромна! 

Представьте, что многочисленную группу людей нужно разделить на три подгруппы. Один из способов это сделать — попросить их рассчитаться на первый-второй-третий. 

Задание 2. Parlez-vous français? - 3

То есть, первый человек относится к первой группе, второй — ко второй, третий — к третьей, четвертый — снова к первой и так далее. Вы можете использовать деление по модулю чтобы произвести эту же операцию. Пронумеруем те же три группы с нуля. Вот как это делается: 

Задание 2. Parlez-vous français? - 4

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

Попробуйте применить этот принцип для возвращения ключевого слова в начало! Только вместо сортировки по группам вам нужен индекс ключевого слова, чтобы вы могли правильную букву для смещения, не выходя за длину ключа. 

Так как мы автоматизируем некоторые тесты вашего кода, программа должна вести себя так, как показано ниже:

jharvard@appliance (~/Dropbox/pset2): ./vigenere bacon
Meet me at the park at eleven am
Negh zf av huf pcfx bt gzrwep oz

Как еще можно протестировать программу, кроме ручного вычисления зашифрованного текста? Мы добрые: для этого мы написали программу devigenere. Она принимает один и только один аргумент командной строки (ключевое слово), а её работа заключается в том, чтобы принять зашифрованный текст в качестве входных данных и вернуть обычный.

Запустите её: 

~cs50/pset2/devigenere k

Где k — ключевое слово. 
Если вы хотите проверить правильность вашей программы с помощью check50, выполните:

check50 2015.fall.pset2.vigenere vigenere.c

А если хотите оценить нашу реализацию vigenere, наберите:

~cs50/pset2/vigenere
Комментарии (62)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Rass1996 Уровень 1
16 сентября 2022
ПОМОГИТЕ НАЙТИ ОШИБКУ!!!

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

int main(int argc, string argv[])
{
int x;
    if (argc != 2)
    {
    printf ("Необходимо ввести 1 слово латиницей после './vergi', которое выступит кодом \n");
    return 1;
    }
    string text = get_string ("Введи текст латиницей для шифрования:");
    string s = argv[1];

   for (int i = 0, n = strlen(s); i < n; i++)
   {
    if (isalpha (s[i]))
    {
        if (isupper (s[i]))
        {
            x = s[i]-65;
        }
        else
        {
            x = s[i]-97;
        }
        for (int a = 0, b = strlen(text); a < b; a++)
        {
            if (isalpha (text[a]))
            {
                if (isupper (text[a]))
                {
                    int q = text[a];
                    int y = q - 65;
                    int z = (y+x)%26;
                    q = z+65;
                    printf ("%c", q);
                }
                else
                {
                    int q = text[a];
                    int y = q - 97;
                    int z = (y+x)%26;
                    q = z+97;
                    printf ("%c", q);
                }
            }
            else
            {
                printf ("%c", text[a]);
            }
    }
    }
   }
   printf ("\n");
}
weingeneer Уровень 1
11 февраля 2022

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

int only_alpha(string);
string rotate(string, string);

int main(int argc, string argv[])
{
    string key;
    string text;

    if (argc != 2)
    {
        return 1;
    }
    
    if (!only_alpha(argv[1]))   // Проверяем, что строка состоит из букв, иначе возвращаем ошибку
    {
        printf("Usage: ./vigenere <letters>\n");
        return 1;
    }
    key = argv[1];
    
    do
    {
        text = get_string("plaintext: ");  // Проверяем что в качестве строки задан не пустая строка
    }
    while (strlen(text) < 1);

    text = rotate(text, key);   // Шифруем текст

    printf("ciphertext: %s\n", text);
    return 0;
}

int only_alpha(string key)   // Функция проверки строки на отсутствие не буквенных значений.
{                                         // Проверяем каждый символ строки и возвращаем 0 или 1
    for (int i = 0; key[i]; i++)
    {
        if (!isalpha(key[i]))
        {
            return false;
        }
    }
    return true;
}

string rotate(string text, string key)     // Функция шифрования
{
    int counter = 0;                              // Отдельная переменная для индекса ключа
    for (int i = 0; i < strlen(text); i++)
    {
        if (text[i] >= 'A' && text[i] <= 'Z')    // Если буква прописная
        {
            text[i] = (text[i] -'A' + tolower(key[counter]) - 'a') % 26 + 'A'; // tolower - переводит прописную букву к строчной
            counter++;        // Только в случае захода в условие, приращение индекса ключа
        }
        else if(text[i] >= 'a' && text[i] <= 'z')
        {
            text[i] = (text[i] - 'a' + tolower(key[counter]) - 'a') % 26 + 'a';
            counter++;
        }
        counter %= strlen(key);
    }
    return text;
}
user346 Уровень 3
3 августа 2021

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

int main(int argc, string argv[]) {
    if (argc != 2) return 1;                        //проверка аргументов
    for (int i = 0; i < strlen(argv[1]); i++) {     //проверка на наличие букв
        if (isalpha(argv[1][i]) == 0) return 1;
    }
    string s = get_string("Put your text: ");
    int tmp = 0;                                    //счетчик для букв ключа
    for (int i = 0; i < strlen(s); i++) {
        char c = s[i];                              //переменная для символа строки
        if (isalpha(s[i])) {                        //выполнить, если символ является буквой
            int j = tmp % strlen(argv[1]);          //расчет переменной для индекса символа ключа (зависит от длины ключа и возвращает к началу)
            int key = tolower(argv[1][j]) - 97;     //преобразуем букву в строчную и приводим к 0
            if (isupper(s[i])) {
                c = ((s[i] - 65 + key) % 26) + 65;  //шифруем заглавную букву
            }
            if (islower(s[i])) {
                c = ((s[i] - 97 + key) % 26) + 97;  //шифруем строчную букву
            }
            tmp++;                                  //записываем в счечик при успешной шифровке буквы
        }
        printf("%c", c);                            //вывод символа
    }
    printf("\n");
    return 0;
}
Александр # Уровень 0
17 марта 2021
Вариант, как расшифровать адрес сайта в конце видео-лекции:

int main (void)
{
	string cipher = get_string("Please enter the text to decrypt: \n");
	
	int key = 26 - (cipher[0] - 'h');  // операцию вычитания (которая по идее нужна в нашем случае) можно заменить сложением (26 - key)
	
	for (int i = 0, n = strlen(cipher); i < n; i++)
	{
		if (islower(cipher[i]))			
			cipher[i] = (cipher[i] - 97 + key) % 26 + 97;
		else if (isupper(cipher[i]))
			cipher[i] = (cipher[i] - 65 + key) % 26 + 65;
	}
	printf("%s\n", cipher);
}
Artem Уровень 0
27 сентября 2020
какая-то хрень. пока писал код компилятор ругался на string key = argv[1]; (см. как Михаил 24.июля у себя применил). пришлось изощряться, но прогу написал. и работает. после этого специально в код ввёл string key = argv[1]; - компилятор ругаться на него перестал. прямо чудеса какие-то
Михаил Уровень 1
24 июля 2020

#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
    if (argc !=2)    
    {
        printf("В следующий раз введите одно слово\n");
        return 1;
    }
        string key = argv[1];   
        for (int i = 0, n = strlen(key); i < n; i++)
        {
            char k = key[i];    
            if (!isalpha(key[i]))   //если символ в ключе не из англ. алфавита, то выводим ошибку
            {
            printf("В следующий раз используйте английский алфавит\n");
            return 1;
            }
        }
        string s = get_string("Введите строку: ");   
        for (int i = 0, j = 0, n = strlen(s), k = strlen(key); i < n; i++)
        {
            char pi = s[i];   
            int kj = tolower(key[j]) - 97;     /*сохраняем буквы из ключа по очереди в букву kj, преобразовуя ёё в маленькую
                                                 и отнимаем 97, чтобы использовать только символы маленького регистра*/
            if (isalpha(pi))    //если введенные пользователем символы из англ. алфавита...
            {                   //
                char a = 97;   // ...то в переменную a мы сохраняем 'a' из таблицы ASCII, этот символ под номером 97
                if (isupper(pi))   //если введнные пользователем символы большие...
                {                  //
                     a = 65;      //...то в переменную a будет сохранён 'A' из таблицы ASCII, этот символ под номером 65
                }
                int code = pi - a;
                char ci = (code + kj)%26 + a; // ci - буква под номером i в зашифрованном тексте
                printf("%c", ci);
                j = (j + 1) % k;
            }
                else
                {
                printf("%c", s[i]);
                }
        }
     printf("\n");
     return 0;
nilinad_vlr Уровень 2
8 июля 2020
Я создал Git-репозиторий с кодом решения задач. Заходите только в крайнем случае. А если вы зайдете то, лучше прочитайте код, поймите как он работает, а потом напишите свой. https://github.com/nilinad-vlr/CS50.git
даня лузганов Уровень 0
21 июня 2020
Хотел бы прояснить пару вопросов: Ребята, которые как я, меньше недели назад начали заниматься программированием, вы полностью сами справляетесь с заданиями? просто у меня так не получается, долго ломаю голову над заданиями.... В принципе сами задания я понимаю как делать, алгоритм в голове есть, но ошибок море, и чтоб их исправить приходиться лезть в комментарии к каждому заданию, и смотреть, как другой человек поступил в данной ситуации. Итак раза 2-3 за задании. Просто хочу понимать, может если я на начальной стадии не вывожу, делая задания "нечестно", то дальше может будет только хуже. и второй момент, команда isalpha работает только с символами? пытался поставить вначале условие на ключ целиком типо такого - if(isalpha(key)) , где key имеет формат string, выдавало ошибку сегментации, час сидел и не мог понять в чем проблема, пока не подсмотрел в комментариях.... код приложу в комментарии, тут не помещается
даня лузганов Уровень 0
21 июня 2020

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

int main(int argc, string argv[]){
    if(argc != 2){
        return 1;
    }
    string key = argv[1];
    for(int m = 0, b = strlen(key); m<b; m++){
        if (!isalpha(key[m])){
            printf("Введите только английские буквы: \n");
            return 1;
        }
    }
        string qwerty = get_string("введите текст для зашифровки: ");
        printf("шифруем....\n");
        printf("зашифрованное сообщение: ");
        int qwer;
        int s;
        int k = 0;
        int led = strlen (key);
        int ki = tolower (key[k]) - 'a';
        for (int i = 0, n = strlen(qwerty); i < n; i++){
            if (isalpha(qwerty[i])){
                s = ki % led;
                if(isupper(qwerty[i])){
                    qwer = (qwerty[i] - 65 + s) % 26 + 65;
                    printf("%c", qwer);
                    k++;
                }
                if(islower(qwerty[i])){
                    qwer = (qwerty[i] - 97 + s) % 26 + 97;
                    printf("%c", qwer);
                    k++;
                }
            }
            else{
            printf("%c", qwerty[i]);
            }
        }
    printf("\n");
    return 0;
}
Dannelion Уровень 1
17 октября 2020
В комментарии лезть не стоит. Так вы смотрите как сделать данный шаг, а не думаете, как его сделать. Сталкиваетесь с проблемой и не знаете как её решить - возвращайтесь к теории. Если возникают ошибки при работе с функциями стандартных библиотек Си, погуглите сами библиотеки, найдите нужную функцию и посмотрите, что она получает на входе и что имеем на выходе. Да и как работает прочитаете. Я выписывал себе все функции из стандартных библиотек в конспект, который веду по лекциям CS50.
7 сентября 2022
Нет, друг, поверь, ты далеко не одинок в этом плане. Для тех кто вообще с нуля пришёл сюда, это ад, поскольку инфа здесь даётся в крайне сжатом виде и нужно иметь багаж элементарных знаний за плечами, чтобы понимать, куда и как копать.
Daniil Tsabiev Уровень 0
14 июня 2020

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

int main (int argc, string argv[]){
    if(argc != 2){
        return 1;
    }
    string key = argv[1];

    //проверяем слово ключ на содержание других символов помимо букв английского алфавита
    for(int i = 0, n = strlen(key); i<n; i++){
        char k = key[i];
        if (!isalpha(k)){
            printf("You can use english alphabeth only!\n");
            return 1;
        }
    }
    string text = get_string("Enter your text: ");
    for(int i = 0, n = strlen(text), k = strlen(key), j = 0; i<n; i++){
        char pi = text[i]; // символ под номером i в незашифрованном тексте
        int kj = tolower (key[j]) - 'a'; // объявляем букву под номером j в ключ-слове и преобразуем в маленькую
        if(isalpha(pi)){
            char a = 'a';
            if (isupper(pi)){
                a = 'A';
            }
            int g = pi - a;
            char ci = (g+kj)%26 + a; // ci - буква под номером i в зашифрованном тексте
            printf("%c", ci);
            j = (j+1)%k;
        } else{
            printf ("%c", pi);
        }
    }
    printf("\n");
}
elecan92 Уровень 0
31 мая 2020
Пришлось повозиться, лень наводить красоту, но вроде все работает

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

int cryptofunc (char c, int k);

int main(int argc, string argv[]){
    if(argc < 2){
        printf("Ошибка ");
        return 1;
    }

    int keys1[strlen(argv[1])];
    string keys = argv[1];

    for(int j = 0, l = strlen(keys); j < l; j++){

        if (keys[j]>96 && keys[j]<123){
            keys1[j]=keys[j]-97;
        }

        if (keys[j]>64 && keys[j]<91){
            keys1[j]=keys[j]-65;
        }


    }



    int count=0;

    string text = get_string("Введите текст \n");


    for (int i = 0, n = strlen(text); i < n; i++){

        if(isalpha(text[i])) {

            printf("%c", cryptofunc(text[i], keys1[count]));

            count++;
            if (count>=(strlen(argv[1]))){
                count=(0);
            }
        } else {

            printf("%c", text[i]);
        }
    }

    printf("\n");
    return 0;
}

int cryptofunc(char c, int k){

    int c1;

    if(c > 64 && c < 91){
        c1 = c + k;
        if (c1 > 90){
            c1 = c1 - 26;
        }
    }

    if(c > 96 && c < 123){
        c1 = c + k;
        if(c1 > 122){
            c1 = c1 - 26;
        }
    }



    return c1;
}