Zmiana podstawy systemu liczbowego kodu znaku. Prosty szyfr

Artur Protasewicz

Wstęp

W artykule pokazano, jak zmienić podstawę systemu liczbowego kodu znaku i użyć powstałego zapisu kodów znaków, jako szyfru. Jest to szyfr podstawieniowy o zmiennej długości ciągu zastępującego pojedynczy znak. Można w ten sposób kodować znaki kontrolne, takie jak tabulacja (znak o kodzie 9), para CR+LF (znaki o kodzie 13 i 10), pozostałe znaki kodu ASCII oraz znaki o kodach od 128 do 65535, a więc także znaki narodowe różnych języków.


Etapy kodowania na przykładzie słowa ŻUK

Kodowanie słowa - przykład
04 EtapyKodowania.png


Przykłady tekstów zaszyfrowanych (podstawa systemu liczbowego 18)

Program HelloWorld ze znakami tabulacji i przejścia do następnego wiersza
00 HelloWorld.png

Niektóre znaki greckie i cyrylica
01 GreekCyrylic.png

Cyfry 0..9 w języku arabskim
02 ArabicDigits.png

Tekst z polskimi znakami diakrytycznymi
03 PLDiacritic.png


Algorytmy szyfrujący i deszyfrujący - kod C#

namespace NSEncoder
{
    class Constants
    {
        // Używana w programie podstawa systemu liczbowego
        // Uwaga: Program będzie działał również po wybraniu
        // innej podstawy ze zbioru [2..17]
        // Im mniejsza podstawa tym dłuższy zaszyfrowany tekst
        public static int Base = 18;

        // Cyfry używane przy zmianie podstawy (analogicznie jak w systemie
        // szesnastkowym '0'..'9', 'A', 'B',...)
        public static string BaseDigits = "0123456789ABCDEFGH";

        // Znaki używane do zamiany ostatnich cyfr po których występuje '#'
        // Uwaga: musi być ich tyle samo ile cyfr w BaseDigits
        // Tu: 18
        // Liczba cyfr 18 pozwala na wykorzystanie całego alfabetu
        // łacińskiego (w połączeniu z cyframi 0..9)
        public static string EndDigitReplacers = "IJKLMNOPQRSTUVWXYZ";
    }
}

namespace NSEncoder
{
    // Szyfrator
    class Encoder
    {
        // Zamiana liczby dziesiętnej na liczbę o zadanej podstawie
        // Tu: podstawa może być od 2 do 18, bo Constants.BaseDigits
        // to zbiór cyfr ['0'..'9', 'A'..'H']
        static string DecToBase(int decNum, int base_)
        {
            string s = "";

            if (decNum == 0)
            {
                s = Constants.EndDigitReplacers[0].ToString();
            }
            else
            {
                while (decNum > 0)
                {
                    s = Constants.BaseDigits[decNum % base_] + s;
                    decNum = decNum / base_;
                }
            }

            return s;
        }

        // Zamiana kodów znaków na liczby o podstawie Constants.Base
        // Liczby w zmienionym systemie liczbowym odzielane są znakami '#'
        // i łączone w jeden łańcuch znaków
        // Tu: Podstawa to 18
        static string EncodeCharCodes(string text)
        {
            string s = "";

            for (int i = 0; i < text.Length; i++)
            {
                s += DecToBase((int)(text[i]), Constants.Base) + '#';
            }

            return s;
        }

        // Zamiana par ostania_cyfra-znak_krzyżyk na znaki ['I'..'Z']
        // pochodzące z Constants.EndDigitReplacers
        // Przykład: kod znaku 'A' to dziesiętnie 65
        // po zamianie na system 18-kowy to '3B'
        // po złączeniu z '#' to '3B#', a po zamianie 'B#' na 'T'
        // zapis kodu znaku to '3T'
        static string EncodeEndDigits(string text)
        {
            for (int i = 0; i < Constants.Base; i++)
            {
                text = text.Replace(Constants.BaseDigits[i].ToString() + '#', 
                    Constants.EndDigitReplacers[i].ToString());
            }

            return text;
        }

        // Szyfrowanie teksu
        public static string Encode(string text)
        {
            text = EncodeCharCodes(text);
            text = EncodeEndDigits(text);

            return text;
        }
    }
}

using System.Collections.Generic;

namespace NSEncoder
{
    // Deszyfrator
    class Decoder
    {
        // Zamiana liczby o zadanej podstawie na liczbę dziesiętną
        static int BaseToDec(string baseNum, int base_)
        {
            int n = 0;

            for (int i = 0; i < baseNum.Length; i++)
            {
                n = base_ * n + Constants.BaseDigits.IndexOf(baseNum[i]);
            }

            return n;
        }

        // Zamiana ostatnich cyfr na pary cyfra-znak_krzyżyk
        static string DecodeEndDigits(string text)
        {
            for (int i = 0; i < Constants.Base; i++)
            {
                text = text.Replace(Constants.EndDigitReplacers[i].ToString(),
                    Constants.BaseDigits[i].ToString() + '#');
            }
            return text;
        }

        // Zamiana kodów znaków (liczb o podstawie Constants.Base) oddzielonych
        // znakami '#' na znaki odszyfrowanego tekstu
        static string DecodeCharCodes(string text)
        {
            List<string> strings = StringHelper.SplitString(text, '#');
            string Text = "";

            for (int i = 0; i < strings.Count - 1; i++)
            {
                Text = Text + (char)(BaseToDec(strings[i], Constants.Base));
            }

            return Text;
        }

        // Odszyfrowywanie tekstu
        public static string Decode(string text)
        {
            text = DecodeEndDigits(text);
            text = DecodeCharCodes(text);

            return text;
        }
    }
}

Funkcja pomocnicza SplitString

Funkcja dzieli tekst z wyróżnionym separatorem (tu: '#') na odrębne ciągi i zapisuje je do listy.

using System.Collections.Generic;

namespace NSEncoder
{
    class StringHelper
    {
        // Zamiana ciągu znaków rozdzielanego znakami splitterChar
        // na listę fragmentów tekstu bez separatora
        public static List<string> SplitString(string str, char splitterChar)
        {
            List<string> strings = new List<string>();

            while (str.IndexOf(splitterChar) >= 0)
            {
                int position = str.IndexOf(splitterChar);
                strings.Add(str.Substring(0, position));
                str = str.Substring(position + 1);
            }

            strings.Add(str);

            return strings;
        }
    }
}

0 komentarzy