Реализация ROT47

  

ROT13 («вращать на 13 мест», иногда дефискованный ROT-13) - это простой буквенный шифр замещения, который заменяет букву буквой 13 букв после нее в алфавите. ROT13 является частным случаем шифра Цезаря, разработанного в Древнем Риме.

     

ROT47 является производным от ROT13, который, помимо скремблирования основных букв, также обрабатывает числа и общие символы. Вместо использования последовательности A-Z в качестве алфавита ROT47 использует больший набор символов из общей кодировки символов, известной как ASCII. В частности, 7-битные печатные символы, исключая пробел, из десятичного числа 33 '!' через 126 '~', всего 94, взятых в порядке численных значений их кодов ASCII, вращаются на 47 позиций без особого рассмотрения случая. Например, символ A сопоставляется с p, а a сопоставляется с 2.

Ниже приведена моя реализация алгоритма ROT47:

#include <cstring>
#include <cstdio>
#include <iostream>

std::string rot47(std::string s)
{
    std::string s1 = "!\"#$%&\'()*+,-./0123456789:;<=>[email protected][\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
    std::string s2 = "PQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~!\"#$%&\'()*+,-./0123456789:;<=>[email protected]";

    std::string ret = "";
    for (unsigned int i = 0; i < s.size(); i++)
    {
        std::size_t pos = s1.find(s[i]);
        ret += s2[pos];
    }

    return ret;
}


int main()
{
    std::string str = "HelloWorld!";

    std::string retFct = rot47(str);
    std::cout << retFct << std::endl;

    std::string retFct2 = rot47(retFct);
    std::cout << retFct2 << std::endl;
}

Он работает так, как ожидалось, но мой вопрос: Есть ли что-то, что я могу улучшить в своей реализации? Я не знаю, есть ли более прямой способ получить результат.

11 голосов | спросил Aleph0 6 +03002015-10-06T10:59:11+03:00312015bEurope/MoscowTue, 06 Oct 2015 10:59:11 +0300 2015, 10:59:11

4 ответа


15
#include <cstring>
#include <cstdio>

Вы не используете ни одного из них, поэтому удалите их и добавьте <string>, чтобы получить std::string

std::string rot47(std::string s) { /* ... */ }

Зачем копировать строку звонящего? Вы только читаете его, поэтому возьмите его с помощью const&.

std::string s1 = "!\"#$%&\...";
std::string s2 = "PQRSTUVW...";

Эти два являются постоянными, не выделяют их для каждого вызова, делают их static (и const):

static std::string const s1 = "...";
std::string ret = "";

Эта инициализация не требуется, строка, построенная по умолчанию, пуста. Возможно, вы захотите reserve соответствующий размер, так как вы его знаете, для оптимальной производительности.

for (unsigned int i = 0; i < s.size(); i++) { /* ... */ }

Вы можете использовать цикл, основанный на диапазоне, со строками:

for (auto c: s) { /* ... */ }

И вы должны, вероятно, проверить, что s1.find действительно что-то нашел, иначе строка после этого - неопределенное поведение.

И s, s1 и s2 - довольно плохой выбор для этих имен переменных. Что-то вроде «источника», «оригинала», «открытого текста» для ввода было бы лучше. map_from /map_to может работать для другого два.

ответил Mat 6 +03002015-10-06T11:33:27+03:00312015bEurope/MoscowTue, 06 Oct 2015 11:33:27 +0300 2015, 11:33:27
5

EBCDIC - это миф, созданный для того, чтобы напугать детей во время праздников, поэтому здесь есть версия, зависящая от ASCII, как это предлагается в комментарии JPhi1618.

Основное внедрение - эффективность, избегая std :: string :: find, который, вероятно, реализован как линейный поиск.

Unicode - это не миф, к сожалению, поэтому никогда не пытайтесь шифровать строку «Zurück».

    #include <string>
    #include <iostream>

    std::string rot47(const std::string& s) {
        std::string ret;
        for(const char plain : s) {
            const char crypt = '!' + (plain - '!' + 47) % 94;
            ret += crypt;
        }
        return ret;
    }

    int main() {
        std::string str = "HelloWorld!";
        std::string retFct = rot47(str);
        std::cout << retFct << std::endl;
        std::string retFct2 = rot47(retFct);
        std::cout << retFct2 << std::endl;
    }
ответил Christopher Oicles 7 +03002015-10-07T03:07:44+03:00312015bEurope/MoscowWed, 07 Oct 2015 03:07:44 +0300 2015, 03:07:44
4

Всегда полезно избегать явных для циклов. В самом деле, вы можете сделать это, чтобы добиться этого, используя std::transform следующим образом.

#include <string>
#include <iostream>
#include <algorithm>

void inplace_rot47(std::string& s)
{
   std::transform(s.begin(), s.end(), s.begin(), 
     [&](char plain) { return '!' + (plain - '!' + 47) % 94; });
}

int main() 
{
   std::string s = "HelloWorld!";

   inplace_rot47(s);
   std::cout << s << std::endl;

   inplace_rot47(s);
   std::cout << s << std::endl;
}

Конечно, если бы вы захотели, вы могли бы вернуть новую строку, а не писать поверх строки. Для этого вам нужно использовать итератор, нажав на новую строку (третий аргумент std::transform).

ответил Juho 17 +03002015-10-17T18:48:52+03:00312015bEurope/MoscowSat, 17 Oct 2015 18:48:52 +0300 2015, 18:48:52
2

Как я вижу, ваш код будет вести себя странно, когда будет передаваться не-ASCII-символы. Он выполняет итерацию по всем байтам в строке и, таким образом, делает странные вещи с многобайтными символами, например, в кодировке UTF-8.

Вы можете попробовать сами, переведя «Zurück» с кодом.

Это не так легко исправить неудачно. Вам нужно либо явно поддерживать UTF-8, либо делать хакерские вещи, такие как прерывание /ошибка, когда вводится символ, превышающий 0x7F (поскольку вы не можете знать длину следующих байтов).

ответил Tobias Mädel 6 +03002015-10-06T17:18:20+03:00312015bEurope/MoscowTue, 06 Oct 2015 17:18:20 +0300 2015, 17:18:20

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132