
7 семестр / Готовые отчеты / ММиВА. Лабораторная работа 3
.pdfМИНИСТЕРСТВО ЦИФРОВОГО РАЗВИТИЯ,
СВЯЗИ И МАССОВЫХ КОММУНИКАЦИЙ РОССИЙСКОЙ ФЕДЕРАЦИИ Федеральное государственное бюджетное образовательное учреждение высшего образования «Санкт-Петербургский государственный университет телекоммуникаций им. проф. М. А. Бонч-Бруевича»
(СПбГУТ)
Факультет инфокоммуникационных сетей и систем Кафедра программной инженерии и вычислительной техники
ЛАБОРАТОРНАЯ РАБОТА №3
по дисциплине «Математические методы и вычислительные алгоритмы современных систем связи»
студент гр. ИКПИ-84 |
_______________ |
Коваленко Л. А. |
преподаватель каф. ПИиВТ |
_______________ |
к.п.н., доцент Коробов С. А. |
Санкт-Петербург
2021

ЦЕЛЬ РАБОТЫ
Изучение алгоритма кодирования/декодирования base64.
ПОСТАНОВКА ЗАДАЧИ
Необходимо разработать два приложения для кодирования/декодирования алгоритмом base64 содержимого произвольного файла.
ХОД РАБОТЫ
Реализация кодировщика base64 на языке C приведена в таблице 1.
Таблица 1. Реализация кодировщика base64 на языке C
base64encode.c
// base64encode.c
#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h>
#define BYTES_TO_READ 9999 // Размер буфера чтения (9999 mod 3 = 0)
// Функция возвращает процессорное время процесса в секундах
double get_proc_time(void) { return (double)clock() / CLOCKS_PER_SEC; }
int main(int argc, const char *argv[])
{
//Если нужно измерить время работы, третий аргумент должен быть -t:
//./base64encode <filename> -t
int time_flag = 0;
//Первый аргумент: имя исполняемого файла
//Второй аргумент: имя файла с содержимым для кодирования base64
if (!(argc == 2 || (argc == 3 && (time_flag = strncmp(argv[2], "-t", 2) == 0))))
{
printf("%s <filename>\n", argv[0]); return 1;
}
// Если файл не существует
if (access(argv[1], F_OK) != 0)
{
printf("The file \"%s\" does not exist\n", argv[1]); return 2;
}
//Если файл нельзя открыть на чтение if (access(argv[1], R_OK) != 0)
{
printf("Insufficient permission to read the file \"%s\"\n", argv[1]); return 3;
}
//Попытка открытия файла
const char *filename = argv[1]; FILE *fp = fopen(filename, "rb"); if (!fp)
{
printf("File open error\n"); return 4;
}
//Кодирование алгоритмом base64
//1. Считываются максимум BYTES_TO_READ символов (BYTES_TO_READ % 3 == 0).
//2. Считанные символы кодируются.
//3. Результат кодирования выводится в поток stdout.
//4. Цикл продолжается до тех пор, пока не будут считаны все байты в файле.
2

char base64_map[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; char input[BYTES_TO_READ], output[BYTES_TO_READ * 4 / 3 + 4];
size_t bytes_readed = 0; double start = get_proc_time();
while ((bytes_readed = fread(input, 1, BYTES_TO_READ, fp)) != 0)
{
size_t i = 0, t = 0, last = bytes_readed % 3, without_last = bytes_readed - last; while (i < without_last)
{
char first_byte = input[i++]; char second_byte = input[i++]; char third_byte = input[i++];
output[t++] = base64_map[first_byte >> 2];
output[t++] = base64_map[((first_byte << 4) | (second_byte >> 4)) & 63]; output[t++] = base64_map[((second_byte << 2) | (third_byte >> 6)) & 63]; output[t++] = base64_map[third_byte & 63];
}
if (last > 0)
{
char first_byte = input[i++];
output[t++] = base64_map[first_byte >> 2]; if (last == 1)
{
output[t++] = base64_map[(first_byte << 4) & 63]; output[t++] = '=';
}
else
{
char second_byte = input[i++];
output[t++] = base64_map[((first_byte << 4) | (second_byte >> 4)) & 63]; output[t++] = base64_map[(second_byte << 2) & 63];
}
output[t++] = '=';
}
output[t] = '\0'; if (!time_flag)
printf("%s", output);
}
double stop = get_proc_time(); if (time_flag)
printf("Time: %lf seconds\n", stop - start); return 0;
}
Реализация декодировщика base64 на языке C приведена в таблице 2.
Таблица 2. Реализация декодировщика base64 на языке C
base64decode.c
// base64decode.c
#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h>
#define BYTES_TO_READ 10000 // Размер буфера чтения (10000 mod 4 = 0)
// Функция возвращает процессорное время процесса в секундах
double get_proc_time(void) { return (double)clock() / CLOCKS_PER_SEC; }
int main(int argc, const char *argv[])
{
//Если нужно измерить время работы, третий аргумент должен быть -t:
//./base64decode <filename> -t
int time_flag = 0;
//Первый аргумент: имя исполняемого файла
//Второй аргумент: имя файла с содержимым для декодирования base64
if (!(argc == 2 || (argc == 3 && (time_flag = strncmp(argv[2], "-t", 2) == 0))))
{
printf("%s <filename>\n", argv[0]);
3

return 1;
}
// Если файл не существует
if (access(argv[1], F_OK) != 0)
{
printf("The file \"%s\" does not exist\n", argv[1]); return 2;
}
//Если файл нельзя открыть на чтение if (access(argv[1], R_OK) != 0)
{
printf("Insufficient permission to read the file \"%s\"\n", argv[1]); return 3;
}
//Попытка открытия файла
const char *filename = argv[1]; FILE *fp = fopen(filename, "rb"); if (!fp)
{
printf("File open error\n"); return 4;
}
//Декодирование алгоритмом base64
//1. Считываются максимум BYTES_TO_READ символов (BYTES_TO_READ % 4 == 0).
//2. Считанные символы декодируются.
//3. Результат декодирования выводится в поток stdout.
//4. Цикл продолжается до тех пор, пока не будут считаны все байты в файле.
char base64_map[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; char input[BYTES_TO_READ], output[BYTES_TO_READ * 3 / 4 + 1], four_bytes[4];
size_t bytes_readed = 0, map_size = sizeof(base64_map) / sizeof(base64_map[0]); double start = get_proc_time();
while ((bytes_readed = fread(input, 1, BYTES_TO_READ, fp)) != 0)
{
size_t i = 0, t = 0; while (i < bytes_readed)
{
for (size_t j = 0; i < bytes_readed && j < 4; ++j)
{
four_bytes[j] = input[i++];
char *pos = (char *)memchr(base64_map, four_bytes[j], map_size); if (pos != NULL)
four_bytes[j] = (char)(pos - base64_map);
else
{
printf("Error. Unknown symbol \"%c\".", four_bytes[j]); return 5;
}
}
output[t++] = (char)((four_bytes[0] << 2) | (four_bytes[1] >> 4)); if (four_bytes[2] != 64 /* '=' игнорируется */)
output[t++] = (char)((four_bytes[1] << 4) | (four_bytes[2] >> 2)); if (four_bytes[3] != 64 /* '=' игнорируется */)
output[t++] = (char)((four_bytes[2] << 6) | four_bytes[3]);
}
output[t] = '\0'; if (!time_flag)
printf("%s", output);
}
double stop = get_proc_time(); if (time_flag)
printf("Time: %lf seconds\n", stop - start); return 0;
}
4

Сборочный файл представлен в таблице 3.
Таблица 3. Сборочный файл
Makefile
.PHONY : clean CC = gcc
CFLAGS = -O0 -std=c11 -Wall -Waddress -Wextra -Werror \ -Wpedantic -Wfloat-equal -Waggregate-return \ -old-style-cast -Wshadow -Wundef -Wpointer-arith \ -Wcast-align -Wcast-qual -fsanitize=address \ -Wstrict-prototypes -Wstrict-overflow=5 \ -Wwrite-strings -Wformat=2 -pedantic-errors \ -Wswitch-default -Wswitch-enum -Wconversion \ -Wunreachable-code -Wredundant-decls -Wenum-compare \ -Wduplicated-branches -Wduplicated-cond -Wlogical-op \ -Wsign-conversion
SOURCES = base64encode.c base64decode.c EXECUTABLES = $(SOURCES:.c=)
all: $(SOURCES) $(EXECUTABLES)
.c:
$(CC) $(CFLAGS) $? -o $(?:.c=)
clean:
rm -rf $(EXECUTABLES)
Все используемые в программах заголовочные файлы и функции входят в стандарт POSIX.
Используемый далее в примерах кодирования/декодирования файл file.txt был сгенерирован Python-скриптом, который представлен в табл. 4.
Таблица 4. Python-скрипт генерации файла
file_gen.py
import string, random
s = string.ascii_letters + string.digits + '\n'
with open('file.txt', 'w') as fp: fp.write(''.join(random.choices(s, k=100_000_000)))
Сгенерированный файл состоит из 100.000.000 ascii-символов
(аналогично — байт) латинских букв, цифр и знака перевода строки.
MD5-файла: 6dbf3c3c73fd6a1826d139a053417fda.
Работа программ и их аналога в Linux «base64» представлена на рис. 1.
5

Рисунок 1. Работа программ и их аналога в Linux «base64»
1. base64encode использует первый аргумент в качестве имени файла
(file.txt). Кодирует содержимое файла и выводит результат в файл
out.txt.
2.Встроенная в Linux утилита base64 кодирует содержимое файла и выводит результат в файл out2.txt.
3.Контрольные суммы MD5 файлов out.txt и out2.txt совпадают.
4.base64decode использует первый аргумент в качестве имени файла
(out.txt). Декодирует содержимое файла и выводит результат в файл
decoded.txt.
5.Встроенная в Linux утилита base64 декодирует содержимое файла и выводит результат в файл decoded2.txt.
6.Контрольные суммы MD5 файлов file.txt, decoded.txt и
decoded2.txt совпадают.
7.Флаг -t после имени файла позволяет узнать, сколько времени потребовалось программе на кодирование/декодирование данных.
6
Ответы на контрольные вопросы приведены в таблице 5.
Таблица 5. Ответы на контрольные вопросы
Декодированный текст |
Кодированный текст |
|
Xz25 |
WHoyNQ== |
|
01010111 01001000 01101111 01111001 |
||
01011000 01111010 00110010 00110101 |
||
01001110 01010001 00111101 00111101 |
||
|
||
Fh7k |
Rmg3aw== |
|
01010010 01101101 01100111 00110011 |
||
01000110 01101000 00110111 01101011 |
||
01100001 01110111 00111101 00111101 |
||
|
||
gRn4 |
Z1JuNA== |
|
01011010 00110001 01001010 01110101 |
||
01100111 01010010 01101110 00110100 |
||
01001110 01000001 00111101 00111101 |
||
|
||
>Юx |
Pt54 |
|
00111110 11011110 01111000 |
01010000 01110100 00110101 00110100 |
|
F=/ |
Rj0v |
|
01000110 00111101 00101111 |
01010010 01101010 00110000 01110110 |
|
ʹ? |
zbTM |
|
11001101 10110100 11001100 |
01111010 01100010 01010100 01001101 |
ЗАКЛЮЧЕНИЕ
В результате выполнения лабораторной работы мы разработали два приложения для кодирования/декодирования алгоритмом base64 содержимого произвольного файла.
7