Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Основы разработки программного обеспечения на примере языка С. Учебное пособие для СПО
.pdf
* Purpose : reads string from keyboard, and tests
* whether it is a correct string
* Input : str (empty string)
* Output : str (inputted string)
* Return :
* -1 bad string
* 0 good string
******************************************************/
int readString(char *str)
{
/* reads only 11 chars from keyboard */
fgets(str,12,stdin);
replaceLineEnd(str);
return testString(str);
}
/*******************************************************
* Name : encodeAtbash
* Purpose : encodes string str using Atbash code
* Input : str (string)
* Output : str (encoded string)
* Return : none
******************************************************/
void encodeAtbash(char *str)
{
int tmp;
while (*str)
{
if ( (int)*str >= STARTD && (int)*str <= ENDD )
{
tmp = ENDD + STARTD - (int)*str ;
*str = (char)tmp;
}
str++;
}
}
int main()
{
char str[12]; /* max length 11 + 1 for ending symbol */
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
101

printf("Enter string to encode: ");
if (readString(str) < 0)
{
printf("Incorrect string, exiting");
return 0;
}
encodeAtbash(str);
printf("Encoded string: %s",str);
return 0;
}
В данном примере строка хранится в переменной str, для которой
выделено 12 байт статической памяти. Выделение происходит при
компиляции программы, так как переменная определена как массив
char. Можно было бы задать str как указатель на char, в этом
случае необходимо выделить память самостоятельно:
str = (char*) malloc(12);
и строка располагалась бы в динамической памяти.
Из выделенных 12-ти байт один используется для хранения символа
конца строки '\0' , для хранения символов самой строки остается 11
байт.
Для ввода строки сконструирована функция readSnring,
использующая в свою очередь библиотечную функцию fgets. Первым
параметром fgets указывается область ввода данных (str), вторым –
допустимый размер в байтах порции ввода, а третьим задается
потоковый файл, откуда производится ввод данных. Таким образом,
размер массива str превышен не будет, даже если пользователь введет
более 11 символов.
Дополнительная функция replaceLineEnd обеспечивает очистку
буфера входной строки и подготовку переменной str для дальнейшей
работы. Она проверяет, чем закончилась процедура считывания
входного потока. Если в строку был перенесен символ '\n' – признак
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
102

конца строки потокового файла, то он заменяется на '\0' – признак
конца символьной строки в Си.
Кроме того, в процедуре replaceLineEnd происходит дочитывание
строки потокового файла до '\n', если пользователь набрал слишком
длинную строку:
if ( *(str+i) == '\0' )
{
/* empty input buffer */
while (temp[0] != '\n')
{
fgets(temp,2,stdin); /* reads 1 symbol + 1 end */
}
}
В приведенном примере для этого происходит последовательное
считывание из буфера по одному символу, пока не будет встречен
символ перевода строки, которым пользователь закончил ввод.
Для кодирования строки выбран алгоритм "Атбаш". Упоминание об его
использовании встречается в Библии. Книга пророка Иеремии, глава
25, стих 26 содержит текст: "И всех царей севера, близких друг к другу и
дальних, и все царства земные, которые - на лице земли, а царь Сесаха
выпьет после них". Слово "Сесах" не является ни ошибкой, ни
искажением библейского текста, хотя такого царя или царства не
существовало. Священные тексты древних иудеев шифровались шифром
простой замены "Атбаш". Алгоритм этого шифра прост: первая буква
алфавита заменялась на последнюю, вторая - на предпоследнюю в
алфавите и т.д. После дешифрации на языке оригинала (для успешной
дешифрации необходимо знать язык сообщения) слова "Сесах"
получается "Вавилон". По смыслу алгоритма функция, реализующая
шифровку и зашифровку, одна и та же.
Алгоритм подразумевает кодирование строки, состоящей из символов
конкретного алфавита, в примере реализации выбран английский
алфавит, точнее его прописные символы. Перед началом кодирования
введенная строка проверяется на допустимость функцией
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
103

testString. Так как прописные символы английского алфавита
расположены последовательно в таблице кодировки символов ASCII,
то достаточно проверить каждый символ введенной строки на
принадлежность определенному диапазону кодов по таблице ASCII.
В функции кодирования encodeAtbash для доступа к символам строки
используется синтаксис работы с указателем. Чтобы получить текущий
символ, используется выражение *str. Для перехода к следующему
символу выполняется str++. Можно реализовать эту функцию иначе с
использованием механизма работы с массивом:
void encodeAtbash(char *str)
{
int tmp;
int i;
i = 0;
while (str[i])
{
if ( (int)str[i] >= STARTD && (int)str[i] <= ENDD )
{
tmp = ENDD + STARTD - (int)str[i];
str[i] = (char)tmp;
}
i++;
}
}
Более того, даже параметр функции можно указать не как ссылку на
char, а как "открытый" массив:
void encodeAtbash(char str[ ] )
В этом случае, несмотря на то, что в Си нет динамических массивов и
их размер должен указываться при инициализации, при передаче
параметров допускается указывать "открытые" массивы. Но тогда
разработчик должен самостоятельно позаботиться о том, чтобы
сообщить функции размер конкретного массива, который передается в
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
104

ходе работы программы. В приведенном примере передаваемая строка
всегда заканчивается символом конца строки '\0', поэтому можно
последовательно перебирать элементы массива, пока не встретится
символ '\0', и не опасаться выйти за границы массива. Если же
конечного символа не предусматривается (в большинстве случаев в
массиве хранятся не строки, а единообразные данные, не
заканчивающиеся никаким специальным элементом), то размер массива
следует передать дополнительно:
void encodeAtbash(char str[], int arrayLength)
{
int i;
for (i = 0; i < arrayLength; i++)
{
. . .
}
. . .
}
А теперь вернемся к задаче, рассмотренной ранее в разделе 2, и
посмотрим, как будет выглядеть реализация программы,
соответствующая сформулированным требованиям.
Чтение строки может быть выполнено аналогично приведенному
примеру кодирования строки алгоритмом "Атбаш", но считывать
необходимо не 11, а 81 символ. Последний 81-й символ нужен, чтобы
обработать ситуацию, когда пользователь ввел более 80-ти символов.
/*******************************************************
Date: 10 January 2013
Description: string reading and encoding sample 2
*****************************************************/
#include <stdio.h>
/*******************************************************
* Name : isEnglishLetter
*
* Purpose : сhecks whether the symbol belongs to
* English alphabet
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
105

* Input : c - symbol
* Output : none
* Return : 1 – belongs, 0 – do not belongs
*
*****************************************************/
int isEnglishLetter(char c)
{
if( (c >= 'a') && ( c <= 'z' ) )
{
return 1;
}
if ( (c >= 'A') && (c <= 'Z') )
{
return 1;
}
return 0;
}
/*******************************************************
* Name : isRussianLetter
*
* Purpose : checks whether the symbol belongs to
* Russian alphabet
* Input : c - symbol
* Output : none
* Return : 1 – belongs, 0 – do not belongs
******************************************************/
int isRussianLetter(char c)
{
if( (c >= 'а') && ( c <= 'я' ) )
{
return 1;
}
if ( (c >= 'А') && (c <= 'Я') )
{
return 1;
}
return 0;
}
/*******************************************************
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
106

* Name : isNumber
*
* Purpose : checks whether the symbol is number
* Input : c - symbol
* Output : none
* Return : 1 – number, 0 – not a number
******************************************************/
int isNumber(char c)
{
if( ((int)c >= (int)'0') && ( (int)c <= (int)'9' ) )
{
return 1;
}
return 0;
}
/*******************************************************
* Name : isDelimeter
*
* Purpose : checks whether the symbol is a delimiter
* Input : c - symbol
* Output : none
* Return : 1 – delimiter, 0 – not a delimiter
******************************************************/
int isDelimeter(char c)
{
if( (c == ',') || (c == '.') || (c == ' ') || (c == '\0') )
{
return 1;
} else
{
return 0;
}
}
/*******************************************************
* Name : testString
*
* Purpose : test string for correctness
* (according requirements)
* Input : str (string to check)
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
107

* Output : none
* Return :
* -1 bad string
* 0 good string (only acceptable characters)
******************************************************/
int testString(char *str)
{
int start;
int goodSymb;
int count;
count = 0;
start = 0;
while(*str)
{
goodSymb = 0;
if (isEnglishLetter(*str))
{
start = 1;
goodSymb = 1;
}
if (isRussianLetter(*str))
{
start = 1;
goodSymb = 1;
}
if (isNumber(*str))
{
start = 1;
goodSymb = 1;
}
if ( (*str) == ' ' )
{
goodSymb = 1;
}
if ( (isDelimeter(*str)) && start )
{
goodSymb = 1;
}
if (goodSymb == 0)
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
108

{
return 0;
} else
{
str++;
count++;
}
}
if (count > 10)
{
return 0;
} else
{
return 1;
}
}
/*******************************************************
* Name : replaceLineEnd
* Purpose : replaces ending '\n' (if exists)
* symbol by '\0' symbol
* except English small characters
* Input : str (string)
* Output : str (string)
* Return : none
******************************************************/
void replaceLineEnd(char *str)
{
int i;
char temp[2];
for(i=0; ( (*(str+i)!='\n') && (*(str+i)!='\0') ); i++);
if ( *(str+i) == '\0' )
{
/* empty input buffer */
while (temp[0] != '\n') \
{
fgets(temp,2,stdin);
}
}
if ( *(str+i) == '\n' )
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
109

{
*(str+i) = '\0';
}
}
/*******************************************************
* Name : readString
*
* Purpose : reads string from keyboard
* Input : str (empty string)
* Output : str (inputted string)
* Return : none
******************************************************/
void readString(char *str)
{
/* reads only 81 chars from keyboard, 82 sets to '\0' */
fgets(str,82,stdin);
replaceLineEnd(str);
}
/*******************************************************
* Name : change231
* Purpose : ncodes word in string str using 231 exchange
* Input : str (string),
* startPos – position in string
* where the word starts from
* Output : str (encoded string)
* Return : encoded word end position in str
******************************************************/
int change231(char *str, int startPos)
{
int i;
char temp;
i = startPos + 1;
while (!isDelimeter(str[i]))
{
temp = str[i];
str[i] = str[i-1];
str[i-1] = temp;
i++;
if (((i-startPos)%3 == 0) && (!isDelimeter(str[i])))
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
110
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
