Опубликован: 05.11.2013 | Доступ: свободный | Студентов: 543 / 46 | Длительность: 11:51:00
Лекция 5:

Препроцессор, оформление программы и средства ввода/вывода

< Лекция 4 || Лекция 5: 12345 || Лекция 6 >

В качестве еще одного примера рассмотрим чтение с клавиатуры и последующее кодирование символьной строки.

/*******************************************************
Date: 10 January 2013
Description: string reading and encoding sample
*******************************************************/
#include <stdio.h>
const int STARTD = 97; /* 'a' code */
const int ENDD = 122; /* 'z' code */
/******************************************************
* Name : testString
*
* Purpose : checks whether string contains anything
* except English small characters
* Input : str (string to check)
* Output : none
* Return :
* -1 bad string
* 0 good string (only small English characters)
*
******************************************************/
int testString(char *str)
{
  int i = 0;
  for(; *(str+i)!='\0'; i++)
  {
    if ( (*(str+i) < STARTD) || (*(str+i) > ENDD) )
    {
      return -1;
    }
  }
  return 0;
}
/*******************************************************
* 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); /* reads 1 symbol + 1 end */
    }
  }
  if ( *(str+i) == '\n' )
  {
    *(str+i) = '\0';
  }
}
/*******************************************************
* Name : readString
*
* 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 */
    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' – признак конца строки потокового файла, то он заменяется на '\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 содержит текст: "И всех царей севера, близких друг к другу и дальних, и все царства земные, которые - на лице земли, а царь Сесаха выпьет после них". Слово "Сесах" не является ни ошибкой, ни искажением библейского текста, хотя такого царя или царства не существовало. Священные тексты древних иудеев шифровались шифром простой замены "Атбаш". Алгоритм этого шифра прост: первая буква алфавита заменялась на последнюю, вторая - на предпоследнюю в алфавите и т.д. После дешифрации на языке оригинала (для успешной дешифрации необходимо знать язык сообщения) слова "Сесах" получается "Вавилон". По смыслу алгоритма функция, реализующая шифровку и зашифровку, одна и та же.

Алгоритм подразумевает кодирование строки, состоящей из символов конкретного алфавита, в примере реализации выбран английский алфавит, точнее его прописные символы. Перед началом кодирования введенная строка проверяется на допустимость функцией 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[ ] )  
    
< Лекция 4 || Лекция 5: 12345 || Лекция 6 >