2

Имеется следующий код на си (шифрование по шифру Вижнера):

#include "stdafx.h"
#include <iostream>

#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable: 4996)

int main()
{
    char secret_key[255];
    int c, s, i = 0;    
    int secret_key_len;
    int select_menu = 0;

    FILE * normal_text = fopen("normal.txt", "r");
    FILE * crypt_text = fopen("crypt.txt", "w");
    FILE * decrypt_text = fopen("decrypt.txt", "w");

    setlocale(LC_ALL, "Russian");

    printf("Введите ключ: ");
    scanf("%s", secret_key);

    printf("Зашифровать normal.txt (0), или расшифровать crypt.txt (1)");
    scanf_s("%d", &select_menu);

    secret_key_len = strlen(secret_key);

    if (select_menu)
    {
        if (crypt_text == NULL)
        {
            printf("Файл с зашифрованным текстом пустой.");
        }

        while ((c = fgetc(crypt_text)) != EOF)
        {
            s = secret_key[i++ % secret_key_len];
            fputc( (c - s + 26) % 26, decrypt_text);
        }
    }
    else
    {
        if (normal_text == NULL)
        {
            printf("Нормальный текст отсутствует");
        }

        while ((c = fgetc(normal_text)) != EOF)
        {
            s = secret_key[i++ % secret_key_len];
            fputc( (c + s) % 26, crypt_text);
        }
    }


    fclose(normal_text);
    fclose(crypt_text);
    fclose(decrypt_text);

    system("pause");
    return 0;
}

Почему fputc сохраняет кракозябры вместо символов? Понять не могу.

Nicolas Chabanovsky
  • 51,426
  • 87
  • 267
  • 507
Sadykh
  • 95

1 Answers1

3

С функцией fputc, разумеется, всё в порядке. Проблема в кодах символов.

Вы почему-то предполагаете, что коды символов a..z есть числа 0..25. А это не так. Код символа 'a' — 97, например.

Если ваш текст на входе — строчные латинские буквы, вместо

fputc( (c - s + 26) % 26, decrypt_text);

вам нужно вот что:

int letternum = c - 'a';
int encodednum = (letternum - s + 26) % 26;
char encodedletter = 'a' + encodednum;
fputc(encodedletter, decrypt_text);

Ну или просто

fputc('a' + (c - 'a' - s + 26) % 26, decrypt_text);

Аналогичная проблема у вас с расшифровкой, надеюсь, тут вы разберётесь сами по аналогии.

Подумайте, как вы хотите кодировать текст, в котором есть пробелы, заглавные буквы, цифры, знаки препинания.


Дополнение: если вы хотите работать со всеми символами, возможно, имеет смысл работать не по модулю 26, а по модулю 256:

fputc(c - s, decrypt_text);

и соответственно

fputc(c + s, crypt_text);

При этом байты ключа должны быть не в промежутке 0..25, а 0..255. Это автоматически решает проблему с пробелами, заглавными буквами, цифрами, знаками препинания и т. п.

VladD
  • 206,799
  • Правильно понимаю, мы вычитаем код символа, чтобы получить число в нужном диапазоне? Но почему именно 'A', а не 'a', например?

    Да, вы пожалуй правы. Знаки пунктуации, цифры и пробелы лучше не кодировать, вас понял.

    – Sadykh Nov 26 '15 at 06:29
  • @Sadykh: И правда, нужно вычитать 'a'. Сейчас исправлю. – VladD Nov 26 '15 at 10:16
  • @Sadykh: Именно так, мы вычитаем код символа, чтобы привести значение в диапазон 0..25. – VladD Nov 26 '15 at 10:18
  • А где можно подробнее прочитать по данной теме? Ну, в плане вычитания из кода символа 'a'. Просто про этот момент нигде не натыкался, любопытно. – Sadykh Nov 26 '15 at 10:35
  • @Sadykh: Даже не знаю, где, давайте попробую объяснить. Откройте таблицу ASCII, там есть коды английских букв. Символ (char) в C++ есть не что иное, как число с его кодом (просто при выводе получается не число, а символ, это практически единственное различие). Далее, коды букв идут подряд, сначала 'a', потом 'b' и т. д. Поэтому чтобы получить номер буквы, нужно вычесть из буквы (то есть, её кода!) код первой буквы. Вот. – VladD Nov 26 '15 at 10:49
  • @Sadykh: Дополнил ещё ответ. – VladD Nov 26 '15 at 10:53
  • спасибо огромное! В дополненном варианте, без вычитания кода символа 'a' - кракозябры. Если же использовать вычитание символа - большая часть нормально шифруется, а часть с кракозябрами, но расшифровывает нормально. Не поможете? – Sadykh Nov 26 '15 at 18:07
  • @Sadykh: На самом деле, вас не должен заботить внешний вид зашифрованного текста. Среди символов таки-да есть крякозябры, и поскольку при зашифровке любой символ может превратиться в любой, то крякозябр стоит ожидать. Главное — чтобы расшифровывалось правильно! – VladD Nov 26 '15 at 18:15
  • @Sadykh: А вариант из дополнения расшифровывает правильно? – VladD Nov 26 '15 at 18:15
  • в дополненном варианте расшифровывается правильно, но вот с шифруется с кракозябрами. Но с кириллицей не расшифровывает или иногда обрезает. – Sadykh Nov 26 '15 at 18:29
  • @Sadykh: Ну, то, что шифруется с крякозябрами, это нормально, зашифрованный текст всё равно не предназначен для чтения. А вот то, что не расшифровывается с кириллицей, это странно. Можете дать пример данных на которых воспроизводится проблема с расшифровкой? – VladD Nov 26 '15 at 19:28
  • Весьма странно. Начал паковать exe, решил проверить - шифрует же и расшифровывает нормально. Но при запуске с Visual Studio 2015 - проблемы с кодировкой (я так думаю). И ещё один момент так и остался: если в программе сразу шифровать/расшифровывать, то в расшифровке мусор (будто буфер/кэш не чистится, не знаю, предположение). Есть у вас будет время, посмотрите, пожалуйста - https://yadi.sk/d/S2lwiz-EkmRWT – Sadykh Nov 26 '15 at 20:05
  • @Sadykh: Угу, сейчас гляну. – VladD Nov 26 '15 at 20:56
  • @Sadykh: Вы просто забыли сбросить i в 0. Добавьте это в начало цикла перед printf("Зашифровать normal.txt... – VladD Nov 26 '15 at 22:01
  • По поводу проблем с кодировкой, смотрите сюда: http://ru.stackoverflow.com/q/459154/10105 – VladD Nov 26 '15 at 22:02