Блог учителя Информатики Московской Марины Николаевны

CS50 week2m

Лекция 1
Лекция 2

Видео:
1. Массивы
2. Возвращение результата
3. Область видимости
4. Глобальные переменные
5. Перенаправления
6. Шифр Цезаря
7. Шифр Виженера
8. RSA

Путеводитель:
1. Функции - 0
2. Функции - 1
3.  Float - 0
4. Float - 1
5. Float - 2
6. String - 0
7. String - 1
8. String - 2
9. ASCII-0
10.ASCII - 2
11. CAPITALIZE-0
12. CAPITALIZE-1
13. CAPITALIZE-2
14. Ages
15. Argv - 0
16. Argv - 1
17. Argv - 2

Конспект семінарського заняття з тижня №2 ви можете переглянути за посиланням http://prometheus.org.ua/cs50/sections/section2.html.

Практическое задание:
1.Инициализация.
У файлі під назвою initials.c напишіть програму, що запитує у користувача його ім’я (використовуючи GetString, щоб отримати його у вигляді string) і потім виводить ініціали у верхньому регістрі без пробілів або крапок із переводом рядку (\n) і нічого більше. Вважайте, що ввід користувача складатиметься лише з літер (верхнього та/або нижнього регістрів) з пробілами. Люди з іменами Joseph Gordon-Levitt, Conan O’Brien, та David J. Malan точно не використовуватимуть вашу програму. Проте, введення користувача може бути трохи кривим, у разі чого є ймовірність присутності одного або більше пробілів на початку та/або кінці вводу або навіть багато-багато пробілів підряд.
Задля автоматизації деяких перевірок вашого коду, програма повинна працювати як у прикладі, наведеному нижче. Підкреслений текст – це ввід користувача, тобто те, що він набрав на клавіатурі.
jharvard@appliance (~/Dropbox/hacker2): ./initials
Zamyla Chan
ZC
jharvard@appliance (~/Dropbox/hacker2): ./initials
robert thomas bowden
RTB

2. Аве, Цезарю!



Згадаємо коротке відео від David DiCiurcio, де розповідається, що шифр Цезаря полягає в тому, щоб циклічно зсунути алфавіт на деякий ключ, який становить кількість літер на які робиться зсув (можете порівняти з http://en.wikipedia.org/wiki/Caesar_cipher). Іншими словами, якщо р - деякий звичайний текст (тобто незашифрований), pi -  i-й символ в р, a k -ключ (невід’ємне ціле число), то кожна літера, ci, в шифрованому тексті c, обчислюється таким чином:
ci = (pi + k) % 26
Може здатися, що ця формула робить шифр складнішим, ніж він є насправді, але це лише гарний шлях точного та стислого вираження алгоритму. А вчені таке люблять.
Наприклад, припустимо, що є секретний ключ k = 13 та звичайний текст р: "Be sure to drink your Ovaltine”. Давайте зашифруємо p та k, щоб отримати шифр c зі зміщенням кожної літери тексту p на 13 позицій
Be sure to drink your Ovaltine!
отримаємо:
Or fher gb qevax lbhe Binygvar!
Ми навмисно вивели все шифром, з одноковою шириною літер, щоб все виглядало гарно. Зверніть увагу на те, що літера O (перша літера в зашифрованому тексті) зміщена на 13 позицій від літери B (перша літера в незашифрованому тексті). Так само й r (друга в зашифрованому тексті) на 13 більша за e (друга в незашифрованому тексті). В той же час, літера f (третя літера в зашифрованому тексті ) на 13 позицій більша за s (третя літера в початковому тексті), в цьому випадку ми переходимо по кругу від z до a. І так далі. Не надто безпечний шифр, але цікавий для реалізації!
Між іншим, шифр Цезаря з ключем 13, як правило, називають ROT13 (http://en.wikipedia.org/wiki/ROT13). Насправді, краще використовувати ROT26, який, як вважають, в два рази безпечніший. У всякому разі, ваша наступна ціль полягає в тому, щоб написати в caesar.c програму,  що шифрує повідомлення за допомогою шифру Цезаря. Програма повинна приймати один аргумент командного рядка: невід'ємне ціле число. Давайте назвемо його k. Якщо вашу програму запускають без будь-яких даних командного рядка, або з більш, ніж одним аргументом, повинно з’являтись повідомлення про помилку і повертатися значення 1 (що в даному випадку означає помилку) як вказано нижче:
return 1;
В іншому випадку, програма повинна перейти до запиту тексту, який потрібно зашифрувати;  символи, які не є літерами алфавіту, будуть виведені без змін. Після виведення зашифрованого тексту програма повинна завершитися, main має повернути 0 наступним чином:
return 0;
Якщо ви явно не повертаєте int всередині main, 0 фактично повертається автоматично (насправді, так як це "тип значення, що повертається", main має  повертати значення типу int, але про це іншим разом). Тепер, коли ви повертаєте 1 явно, найкраще буде повертати 0 (якщо все правильно) в явному вигляді для успішного виконання програми. В той час, як 0 в загальному випадку демонструє успіх, будь-яке інше значення int являє собою помилку. Таким чином, ви можете представити понад чотири мільярди варіантів помилок, так як int, як правило, 32 бітний!
У всякому разі, не дивлячись на те, що існує тільки 26 літер в англійському алфавіті, ви не можете припустити, що k буде меншим або рівним 26; ваша програма повинна працювати зі всіма невід'ємними цілочисельними значеннями k меншими за 231 - 26. Іншими словами, вам не потрібно турбуватися, що програма врешті-решт зламається, коли користувач вибере значення k, що буде занадто велике, щоб поміститися в int. Тепер, навіть якщо k більше, ніж 26, літери будуть залишатися літерами. Наприклад, якщо  k = 27, A не стане символом [, навіть якщо[ знаходиться на 27 місці від A в таблиці ASCII; A перейде до B, так як 27 по модулю 26 (остача від ділення 27 на 26) дорівнює 1. Іншими словами, k = 1 та  k = 27 еквівалентні.
Ваша програма має також враховувати, що при введенні великих літер вони повинні залишитися великим, а при введенні малих - малими.
З чого ж почати? Що ж, так як ця програма має стартувати з того, що в командному рядку вона повинна прийняти значення k, в main треба прописати:
int main(int argc, string argv[])
Нагадаємо, що argv - це масив рядків. Ви можете уявляти масив, як ряд шафок в спортзалі, всередині кожної з яких знаходиться деяке значення (наприклад, шкарпетки). В цьому випадку всередині кожної такої шафки є string. Щоб відчинити перший замок, ми використовуємо синтаксис argv[0], так як індексація масиву починається з 0. Щоб відчинити наступний, ми звертаємося до нього argv[1]. І так далі. Звісно, якщо ми маємо n замків, то нам краще зупинитися на  argv[n - 1], так як argv[n] не існує! (Взагалі він існує, але належить комусь іншому, тому ви не повинні його чіпати.)
Таким чином, ви можете отримати доступ до k так:
string k = argv[1];
якщо припустити, що там щось насправді є! Нагадаємо, що argc має тип int та дорівнює кількості рядків, що знаходяться в argv, так що краще перевірити значення argc перед відкриттям комірки, бо раптом її не існує! В ідеалі, значення argc має бути 2. Чому? Що ж, нагадаємо, що всередині argv[0] типово знаходиться власне ім'я програми. Так, argc завжди буде не менше за 1. Але для цієї програми необхідно, щоб користувач надав аргумент k, тому й argc буде  2. Звичайно, якщо користувач вводить більше, ніж один аргумент до командного рядка, argc може бути більшим за 2.
Те, що користувач вводить ціле число в рядок, ще не означає, що внесене буде автоматично зберігатися в тип int. Навпаки, це значення буде збережене як string, який лиш виглядає як int! Так що ви повинні конвертувати string в справжній int. На щастя, існує функція atoi, яка й створена для таких цілей. Вона застосовується наступним чином:
int k = atoi(argv[1]);
Зверніть увагу, що на цей раз ми оголосили k як справжній int, так що ви можете зробити деякі арифметичні дії з ним. Ось, вже набагато краще. Між іншим, можна припустити, що користувач буде вводити лише цілі числа. Вам не доведеться думати про них, оскільки ввівши, скажімо, foo, atoi поверне 0.
Оскільки atoi оголошена в stdlib.h, потрібно прописати #include на початку коду. Технічно, ваш код компілюватиметься без нього, так як ми вже включили його до cs50.h. Але краще самостійно звикати підключати потрібні бібліотеки.
Отже, як тільки ви отримали k, що зберігається як int, потрібно зробити запит для отримання звичайного тексту. CS50’s має функцією GetString, яка може допомогти з цим.
Після того, як у вас є k та деякий початковий текст, можна приступати до шифрування. Нагадаємо, що ви можете перебирати та надрукувати всі символи в рядку, створивши цикл:
for (int i = 0, n = strlen(p); i < n; i++)
{
    printf("%c", p[i]);
}
Іншими словами, так само, як argv є масивом рядків, string є масивом символів. І тому можна використовувати квадратні дужки для доступу до окремих символів рядка так само, як отримувати окремі рядки в argv. Звичайно, друк кожного з символів не є шифруванням. Тільки, за умови, коли k  = 0. Але ж ми маємо допомогти Цезарю зашифрувати його лист! Аве, Цезар!
Між іншим, потрібно підключити ще один файл, щоб використовувати strlen.