4 (779941)
Текст из файла
Лабораторная работа 4
Шифрование текстовых файлов
Задание
Разработать программу для шифрования и дешифрирования текста, хранящегося в файле, основанную на методе Цезаря1, но более «изощренную»: ключ, используемый для кодирования символа, будет зависеть от позиции символа в исходном тексте. Для того, чтобы шифрование и дешифрирование можно было бы выполнять одним и тем же способом, вместо сложения кода символа с постоянным ключем (как у Цезаря) для переменных ключей используйте операцию «исключающее ИЛИ» (<код символа> ^ <ключ>) .
При кодировании с переменным ключем один и тот же символ исходного текста будет представлен в зашифрованном тексте различными символами, что существенно усложнит «взлом шифра». Ключи должны представлять собой целые числа, определяемые путем преобразования слов кодового блокнота по следующему алгоритму:
-
ключ для k-го по порядку символа в исходном тексте вычисляется как сумма (по модулю 256) кодов символов k-го по порядку слова кодового блокнота.
-
Если кодовый блокнот имеет слов меньше, чем количество символов в исходном тексте, то по исчерпании слов в нём перейти к первому слову и продолжить кодирование.
Целесообразно сначала на основе кодового блокнота сформировать по заданному правилу целочисленный массив ключей, который затем будет использоваться при шифровании. Получение массива ключей оформить в виде отдельной функции.
Исследовать статистическую зависимость символов в закодированном тексте от соответствующих им символов исходного текста (сколько каких кодов одного и того же исходного символа получено) от размера кодового блокнота для заданного исходного текста. Чем меньше полученная статистическая зависимость, тем сложнее распознать текст, не зная алгоритма кодирования и текста кодового блокнота. Статистические результаты по конкретному символу исходного текста представить в виде таблицы 16х16, по аналогии с кодировочными таблицами. Исследование и вывод таблиц результатов следует выполнять в режиме диалога, последовательно вычисляя и выводя результаты для одного запрашиваемого символа.
Для большей достоверности статистических результатов в качестве исходного текста и кодового блокнота использовать текстовые файлы размером не менее 2 Кбайт.
Планируемое время выполнения работы - 8 часов.
Указание.
В программе статистические данные собирать в массиве int stat[256], из которого они распечатываются в виде таблицы 16х16. (Одна таблица для одного символа исходного текста, код которого должен вводиться с клавиатуры по запросу.) Индексы элементов массива равны кодам символов в зашифрованном тексте, в которые в результате кодирования преобразуется выбранный для сбора статистики символ исходного текста, а значения элементов массива равны числу повторных использований этого кода. (Один и тот же исходный символ в закодированном тексте представляется различными кодами в зашифрованном тексте и каждый код может использоваться несколько раз).
Для шифрования файла нет необходимости хранить весь текстовый файл в оперативной памяти. При формировании числового массива ключей следует организовать пословное чтение файла с использованием функций getline() и strtok(), а при шифровании и сборе статистики – посимвольное с помощью функций двоичного чтения данных get(ch) и put(ch). При открытии файлов устанавливать режим ios::binary. Это позволит исключить непредсказуемое влияние на процесс чтения-записи файлов управляющих кодов, которые могут появиться в тексте в результате шифрования.
!!! Работа состоит из трех частей и рассчитана на 12 часов (3 занятия по 4 часа).
На первом занятии выполняются приведенные ниже примеры программ работы с символьными строками и файлами. Особое внимание обратите на используемую последовательность шагов разработки программы и на применение библиотечных функций. Запомните: затраты времени на изучение функций библиотеки языка программирования, имеющих отношение к решаемой задаче, окупятся при разработке программы.
Второе занятие должно завершиться тестированием шифрования и дешифрования различных текстовых файлов.
На третьем занятии выполняется сбор и вывод статистических данных по шифрованию.
Ввод-вывод строк
Для ввода-вывода строк используются как уже известные нам объекты cin и cout, так и функции, унаследованные из библиотеки С. Рассмотрим сначала первый способ:
#include <iostream.h>
int main()
{
const int n = 80;
char s[n];
cin >> s; cout << s << endl;
return ();
}
Как видите, строка вводится точно так же, как и переменные известных нам типов. Запустите программу и введите строку, состоящую из одного слова. Запустите программу повторно и введите строку из нескольких слов. Во втором случае выводится только первое слово. Это связано с тем, что ввод выполняется до первого пробельного символа (то есть пробела, знака табуляции или символа перевода строки '\n')2. Можно ввести слова входной строки в отдельные строковые переменные:
#include <iostream.h>
int main()
{
const int n = 80;
char s[n], t[n], r[n];
cin >>s >>t >> r; cout <<s << endl << t << endl << r << endl;
return 0;
}
Если требуется ввести строку, состоящую из нескольких слов, в одну строковую переменную, используются методы getline или get класса istream, объектом которого является cin. Во втором семестре мы изучим, что такое методы класса3, а пока можно пользоваться ими как волшебным заклинанием, не вдумываясь в смысл. Единственное, что нам пока нужно знать, это синтаксис вызова метода — после имени объекта ставится точка, а затем пишется имя метода:
#include <iostream.h>
int main()
{
const int n = 80;
char s[n];
cin.getline(s, n);
cout << s << endl;
cin.get(s, n);
cout << s << endl;
return 0;
}
Метод getline считывает из входного потока n - 1 символов или менее (если символ перевода строки встретится раньше) и записывает их в строковую переменную s. Символ перевода строки4 также считывается (удаляется) из входного потока, но не записывается в строковую переменную, вместо него размещается завершающий 0. Если в строке исходных данных более n-1 символов, следующий ввод будет выполняться из той же строки, начиная с первого несчитанного символа. Метод get с двумя аргументами работает аналогично, но оставляет в потоке символ перевода строки. В строковую переменную добавляется завершающий 0.
Никогда не обращайтесь к разновидности метода get с двумя аргументами два раза подряд, не удалив \n из входного потока. Например:
cin.get(s, n); // 1- считывание строки
cout << s << endl; // 2- вывод строки
cin.get(s, n); // 3- считывание строки
cout << s << endl; // 4- вывод строки
cin.get(s, n); // 5 - считывание строки
cout <<s << endl; // 6- вывод строки
cout << "Конец - делу венец" <<endl; // 7
При выполнении этого фрагмента вы увидите на экране первую строку, выведенную оператором 2, а затем завершающее сообщение, выведенное оператором 7. Какие бы прекрасные строки вы ни ввели с клавиатуры в надежде, что они будут прочитаны операторами 3 и 5, метод get в данном случае «уткнется» в символ \n, оставленный во входном потоке от первого вызова этого метода (оператор 1). В результате будут считаны и, соответственно, выведены на экран пустые строки (строки, содержащие 0 символов). А символ \n так и останется «торчать» во входном потоке. Возможное решение этой проблемы — удалить символ \n из входного потока путем вызова метода get без параметров, то есть после операторов 1 и 3 нужно вставить вызов cin.get().
Однако есть и более простое решение — использовать в таких случаях метод getline, который после прочтения строки не оставляет во входном потоке символ \n.
Если в программе требуется ввести несколько строк, метод getline удобно использовать в заголовке цикла, например:
#include <iostream.h>
int main()
{
const int n = 80;
char s[n];
while (cin.getline(s, n))
{
cout << s << endl;
… // обработка строки
}
return 0;
}
Пример программы работы с символьными строками.
Написать программу, которая определяет, сколько раз встретилось заданное слово в текстовом файле, длина строки в котором не превышает 80 символов. Текст не содержит переносов слов.
Определим слово как последовательность алфавитно-цифровых символов, после которых следует знак пунктуации, разделитель или признак конца строки. Слово может находиться либо в начале строки, либо после разделителя или знака пунктуации. Это можно записать следующим образом (фигурные скобки и вертикальная черта означают выбор из альтернатив):
слово = {начало строки | знак пунктуации | разделитель} символы, составляющие слово
{конец строки | знак пунктуации | разделитель}
I. Исходные данные и результаты
Исходные данные:
-
Текстовый файл неизвестного размера, состоящий из строк длиной не более 80 символов. Поскольку по условию переносы отсутствуют, можно ограничиться поиском слова в каждой строке отдельно. Для ее хранения выделим строку длиной 81 символ.
-
Слово для поиска, вводимое с клавиатуры. Для его хранения также выделим строку длиной 81 символ.
Результатом работы программы является количество вхождений слова в текст. Представим его в программе в виде целой переменной.
Для хранения длины строки будем использовать именованную константу, а для хранения фактического количества символов в слове — переменную целого типа. Для работы с файлом будем использовать потоковый ввод-вывод.
II. Алгоритм решения задачи
- Построчно считывать текст из файла.
- Просматривая каждую строку, искать в ней заданное слово. При каждом
нахождении слова увеличивать счетчик.
Детализируем второй пункт алгоритма. Очевидно, что слово может встречаться в строке многократно, поэтому для поиска следует организовать цикл просмотра строки, который будет работать, пока происходит обнаружение в строке последовательности символов, составляющих слово.
При обнаружении совпадения с символами, составляющими слово, требуется определить, является ли оно отдельным словом, а не частью другого. (Это один из возможных вариантов решения задачи, не самый лучший. Другой вариант – сначала выделить слово, а затем сравнивать его с заданным). Например, мы задали слово «кот». Эта последовательность символов содержится, например, в словах «котенок», «трикотаж», «трескотня» и «апперкот». Следовательно, требуется проверить символ, стоящий после слова, а в случае, когда слово не находится в начале строки — еще и символ перед словом. Эти символы проверяются на принадлежность множеству знаков пунктуации и разделителей.
III. Программа и тестовые примеры
Разобьем написание программы на последовательность шагов.
Шаг 1. Ввести «скелет» программы (директивы #include, функцию main(), описание переменных, открытие файла). Добавить контрольный вывод введенного слова. Запустив программу, проверить ввод слова и успешность открытия файла. Для проверки вывода сообщения об ошибке следует выполнить программу еще раз, задав имя несуществующего файла.
#include <fstream >
using namespace std;
int main()
{
const int len = 81;
char word[len],line[len];
cout << " Input the word for search: ";
cin >> word;
ifstream fin("text.txt",ios::in);
if (!fin) { cout << "Error of file opening."<< endl;
return 1; }
return 0;
}
Шаг 2. Добавить в программу цикл чтения из файла, внутри цикла поставить контрольный вывод считанной строки:
#include <fstream.h>
int main()
{
const int len = 81;
char word[len], line[len];
cout << "Input the word for search: ";
cin >> word;
ifstream fin("text.txt", ios::in);
Характеристики
Тип файла документ
Документы такого типа открываются такими программами, как Microsoft Office Word на компьютерах Windows, Apple Pages на компьютерах Mac, Open Office - бесплатная альтернатива на различных платформах, в том числе Linux. Наиболее простым и современным решением будут Google документы, так как открываются онлайн без скачивания прямо в браузере на любой платформе. Существуют российские качественные аналоги, например от Яндекса.
Будьте внимательны на мобильных устройствах, так как там используются упрощённый функционал даже в официальном приложении от Microsoft, поэтому для просмотра скачивайте PDF-версию. А если нужно редактировать файл, то используйте оригинальный файл.
Файлы такого типа обычно разбиты на страницы, а текст может быть форматированным (жирный, курсив, выбор шрифта, таблицы и т.п.), а также в него можно добавлять изображения. Формат идеально подходит для рефератов, докладов и РПЗ курсовых проектов, которые необходимо распечатать. Кстати перед печатью также сохраняйте файл в PDF, так как принтер может начудить со шрифтами.