Perl: как кодировать и декодировать символы только в верхнем регистре букв алфавита - PullRequest
0 голосов
/ 17 августа 2011

Я занимаюсь этим довольно давно. У меня есть символы, скажем, латинского алфавита, и я хочу, чтобы они кодировались только в верхнем регистре букв. Есть ли модуль, который мог бы сделать это? Или любую кодировку BaseX, которую я могу изменить, чтобы использовать только буквенные символы uc?

В настоящее время я реализовал его части, используя подстановки регулярных выражений, но он охватывает только несколько символов и определенно не эффективен :)

в любом случае, если нет способа справиться с этим через модуль или функцию, Есть ли способ сделать это эффективно с помощью регулярных выражений?

я думал о tr/[\+,\-,...]/[PLUS,MINUS,...]/cds;

но похоже, что tr заменяет char только на char, а не char на последовательность символов: (

есть идеи?

Ахим

Ответы [ 6 ]

4 голосов
/ 17 августа 2011

Чтобы ответить на вопрос tr:

%subs = ( '+' => 'PLUS' );
my $pat = join '|', map quotemeta, keys %subs;
s/($pat)/$subs{$1}/g;

Базу 26 можно сделать, но реализовать ее немного сложно и неэффективно, поскольку 26 не является степенью 2. Но это определенно то, что вы хотите. Я посмотрю о кодировании.

А пока вот базовое решение 16:

sub bytes_to_base16 {
   my $e = unpack('H*', $_);
   $e =~ tr/0123456789ABCDEFabcdef/ABCDEFGHIJKLMNOPKLMNOP/;
   return $e;
}

sub base16_to_bytes {
   my $e = $_[0];
   $e =~ tr/ABCDEFGHIJKLMNOP/0123456789ABCDEF/;
   return pack('H*', $_);
}

Посмотрим, насколько эффективна база 26 по сравнению с базой 16:

$ perl -MMath::BigInt -MMath::BigFloat -E'
   my $n = Math::BigInt->new(1);
   my $bs = 0;
   for (1..10) {
      $n <<= 8;
      ++$bs;
      my $bd16 = 2*$bs;
      my $bd26 = Math::BigFloat->new($n)->blog(26, 5)->bceil->numify;
      say sprintf "%2d bytes takes %2d base16 digits or %2d base26 digits.".
                  " base26 is %3.0f%% of the size of base16.",
         $bs, $bd16, $bd26, $bd26/$bd16*100;
      }
'
 1 bytes takes  2 base16 digits or  2 base26 digits. base26 is 100% of the size of base16.
 2 bytes takes  4 base16 digits or  4 base26 digits. base26 is 100% of the size of base16.
 3 bytes takes  6 base16 digits or  6 base26 digits. base26 is 100% of the size of base16.
 4 bytes takes  8 base16 digits or  7 base26 digits. base26 is  88% of the size of base16.
 5 bytes takes 10 base16 digits or  9 base26 digits. base26 is  90% of the size of base16.
 6 bytes takes 12 base16 digits or 11 base26 digits. base26 is  92% of the size of base16.
 7 bytes takes 14 base16 digits or 12 base26 digits. base26 is  86% of the size of base16.
 8 bytes takes 16 base16 digits or 14 base26 digits. base26 is  88% of the size of base16.
 9 bytes takes 18 base16 digits or 16 base26 digits. base26 is  89% of the size of base16.
10 bytes takes 20 base16 digits or 18 base26 digits. base26 is  90% of the size of base16.

Эффективная реализация выдаст чуть менее эффективный результат.

$ perl -MMath::BigInt -MMath::BigFloat -E'
   my $bs = 0;
   for (1..10) {
      ++$bs;
      my $bd16 = 2*$bs;
      my $bd26 = int($bs/4)*7 + ($bs%4)*2;
      say sprintf "%2d bytes takes %2d base16 digits or %2d base26 digits.".
                  " base26 is %3.0f%% of the size of base16.",
         $bs, $bd16, $bd26, $bd26/$bd16*100;
      }
'
 1 bytes takes  2 base16 digits or  2 base26 digits. base26 is 100% of the size of base16.
 2 bytes takes  4 base16 digits or  4 base26 digits. base26 is 100% of the size of base16.
 3 bytes takes  6 base16 digits or  6 base26 digits. base26 is 100% of the size of base16.
 4 bytes takes  8 base16 digits or  7 base26 digits. base26 is  88% of the size of base16.
 5 bytes takes 10 base16 digits or  9 base26 digits. base26 is  90% of the size of base16.
 6 bytes takes 12 base16 digits or 11 base26 digits. base26 is  92% of the size of base16.
 7 bytes takes 14 base16 digits or 13 base26 digits. base26 is  93% of the size of base16.
 8 bytes takes 16 base16 digits or 14 base26 digits. base26 is  88% of the size of base16.
 9 bytes takes 18 base16 digits or 16 base26 digits. base26 is  89% of the size of base16.
10 bytes takes 20 base16 digits or 18 base26 digits. base26 is  90% of the size of base16.

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

Так стоит ли использовать base26 вместо base16? Вероятно, нет, если каждый байт действительно драгоценен.


И, наконец, вот реализация базы 26.

my @syms = ('A'..'Z');
my %syms = map { $syms[$_] => $_ } 0..$#syms;

sub bytes_to_base26 {
   my $e = '';

   my $full_blocks = int(length($_[0]) / 4);
   for (0..$full_blocks-1) {
      my $block = unpack('N', substr($_[0], $_*4, 4));
      $e .= join '', @syms[
         $block / 26**6 % 26,
         $block / 26**5 % 26,
         $block / 26**4 % 26,
         $block / 26**3 % 26,
         $block / 26**2 % 26,
         $block / 26**1 % 26,
         $block / 26**0 % 26,
      ];
   }

   my $extra = substr($_[0], $full_blocks*4);
   for my $block (unpack('C*', $extra)) {
      $e .= join '', @syms[
         $block / 26**1 % 26,
         $block / 26**0 % 26,
      ];
   }

   return $e;
}

sub base26_to_bytes {
   my $d = '';

   my $full_blocks = int(length($_[0]) / 7);
   for (0..$full_blocks-1) {
      my $block = 0;
      $block = $block*26 + $syms{$_} for unpack '(a)*', substr($_[0], $_*7, 7);
      $d .= pack('N', $block);
   }

   my $extra = substr($_[0], $full_blocks*7);
   my @extra = unpack('(a)*', $extra);
   while (@extra) {
      my $block = 0;
      $block = $block*26 + $syms{ shift(@extra) };
      $block = $block*26 + $syms{ shift(@extra) };
      $d .= pack('C', $block);
   }

   return $d;
}
3 голосов
/ 17 августа 2011

Самый простой подход - это использовать кодировку base16, как предлагали другие, и переназначать цифры на буквы - но тогда вы используете только 16 из 26 символов, что бесполезно.

Наиболее эффективным из возможных кодирований будет base26, но это будет очень сложно - фактически вы будете рассматривать весь ввод как большое двоичное число и преобразовывать его из базы 2 в базу 26.

log2 (26) - чуть больше 4,7, поэтому в лучшем случае (при отсутствии сжатия) вы можете кодировать 4,7 бита на букву. Менее расточительное кодирование может кодировать 4 байта (32 бита) в 7 букв. 7 букв дают вам около 32,9 бит информации, так что вы не теряете столько информации. И все это можно сделать в 32-битной арифметике. Затем вам нужно будет решить, что делать, если ввод не кратен 4 байтам.

(Фактическая реализация оставлена ​​в качестве упражнения - по крайней мере, на данный момент.)

0 голосов
/ 18 августа 2011

Для забавы, вот мой симулятор Enigma. Нет простого способа добиться того, что вы хотите сделать, поскольку у колес нет escape-символов, и любые последовательности, которые вы придумываете для представления escape-последовательности, значительно уменьшат силу шифра.

Однако 8-битный латинский вход может быть отображен от 0-255 до [AP] [AP] с использованием 65 + ($ Char & 15) .65 + ($ Char >> 4) и инвертирован на выходе, но RZ будет впустую и будет много дыр на входе, хотя это можно решить, сначала запустив gzip.

Немцы обычно использовали X для обозначения пробелов и, если это действительно необходимо, прописывали знаки препинания, стараясь не повторять одно и то же дважды. Я знаю, это раздражает, но так оно и есть. Если мы увеличим количество букв на колесах, то он перестанет быть машиной Enigma!

#!/usr/bin/perl
#Tinigma 2010 Usage:tinigma.pl 123 rng ini "GHWVYYDVPQGEWQWVT"
($n,$o,$p)=map(ord()-65,split//,uc$ARGV[1]);($z,$y,$x)=map(ord
()-65,split//,uc$ARGV[2]);($l,$m,$r)=map$_-1,split//,$ARGV[0];
$t=uc$ARGV[3];$t=~s/[^A-Z]//g;$b=26;$j=0;@N=qw(7 25 11 6 1);@R
=('EKMFLGDQVZNTOWYHXUSPAIBRCJ'x3,'AJDKSIRUXBLHWTMCQGZNPYFVOE'x
3,'BDFHJLCPRTXVZNYEIWGAKMUSQO'x3,'ESOVPZJAYQUIRHXLNFTGKDCMWB'x
3,'VZBRGITYUPSDNHLXAWMJQOFECK'x3,'YRUHQSLDPXNGOKMIEBFZCWVJAT'x
3);@t=split//,$t;for$v(@R){$i=0;for(split//,$v){$c=ord($_)-65;
$F[$j][$i]=$c;$R[$j][$c+$b*(int($i/$b))]=$i;$i++}$j++}@S=@{$F[
5]};$f=$y==$F[$m][$N[$m]]?1:0;$i=0;for(@t){if($f){$y++;$y%=$b;
$z++;$z%=$b;$f=0}if($x==$F[$r][$N[$r]]){$y++;$y%=$b;if($y==$F[
$m][$N[$m]]){$f=1}}$x++;$x%=$b;$e.=chr(($R[$r][$R[$m][$R[$l][$
S[$F[$l][$F[$m][$F[$r][ord($_)-39+$x-$n]-$x+$n+$y-$o]-$y+$o+$z
-$p]-$z+$p]+$z-$p]-$z+$p+$y-$o]-$y+$o+$x-$n]-$x+$n)%$b+65)}
print"$e\n"
0 голосов
/ 17 августа 2011

Вы указали перевод с очень потерями ... который может быть неудовлетворительным.

Но:


    #!/usr/bin/perl  
    use strict;  
    use warnings;  
    use 5.012;  

    # 7095527/perl-how-to-encode-and-decode-characters-in-uppercase-alpha-letters-only  

    my $string = "abcDEFghijklMNO1234567890pqr+_)!@#}{?";  
    my @arr = split //, uc($string);  

    my (@intermediate, $char);  
    for my $char(@arr) {  
        if ($char =~ /[A-Z]/) {  
            say "ENIGMA char found (possibly uc'ed): $char";  
        } else {  
            say "WTF? \$char at line17 is !~ /[A-Z]/: $char";  
            next;  
        }  
    }  

    =head OUTPUT:  

    > SO7095527.pl  
    ENIGMA char found (possibly uc'ed): A
    ENIGMA char found (possibly uc'ed): B
    ENIGMA char found (possibly uc'ed): C
    ENIGMA char found (possibly uc'ed): D
    ENIGMA char found (possibly uc'ed): E
    ENIGMA char found (possibly uc'ed): F
    ENIGMA char found (possibly uc'ed): G
    ENIGMA char found (possibly uc'ed): H
    ENIGMA char found (possibly uc'ed): I
    ENIGMA char found (possibly uc'ed): J
    ENIGMA char found (possibly uc'ed): K
    ENIGMA char found (possibly uc'ed): L
    ENIGMA char found (possibly uc'ed): M
    ENIGMA char found (possibly uc'ed): N
    ENIGMA char found (possibly uc'ed): O
    WTF? $char at line17 is !~ /[A-Z]/: 1
    WTF? $char at line17 is !~ /[A-Z]/: 2
    WTF? $char at line17 is !~ /[A-Z]/: 3
    WTF? $char at line17 is !~ /[A-Z]/: 4
    WTF? $char at line17 is !~ /[A-Z]/: 5
    WTF? $char at line17 is !~ /[A-Z]/: 6
    WTF? $char at line17 is !~ /[A-Z]/: 7
    WTF? $char at line17 is !~ /[A-Z]/: 8
    WTF? $char at line17 is !~ /[A-Z]/: 9
    WTF? $char at line17 is !~ /[A-Z]/: 0
    ENIGMA char found (possibly uc'ed): P
    ENIGMA char found (possibly uc'ed): Q
    ENIGMA char found (possibly uc'ed): R
    WTF? $char at line17 is !~ /[A-Z]/: +
    WTF? $char at line17 is !~ /[A-Z]/: _
    WTF? $char at line17 is !~ /[A-Z]/: )
    ...

    =cut

Обратите внимание, что капитан подводной лодки получит бесполезное направление, если в сообщении указывается место дозаправки как "73N 39W" ...

0 голосов
/ 17 августа 2011

Кодировка Base64 генерирует шестнадцатеричный вывод, означающий 16 возможных символов.Поскольку в алфавите 26, вы можете поменять цифры цифрами.Затем вы будете использовать всего 16 символов алфавита, но у вас будет строка, состоящая из букв алфавита, где действительно легко кодировать-декодировать и возвращать исходную строку.Это странный вопрос (и выглядит как домашнее задание), но он поможет.

0 голосов
/ 17 августа 2011

Вы можете использовать кодировку Base32 с 26 заглавными буквами и 6 цифрами:

http://pastebin.com/YPvfrpHW

Просто измените массив $code на любой кодировку, которую вы хотитеиспользовать.

Редактировать: Упс, только что заметил, что вы Perl, а не PHP, извините.Вы должны быть в состоянии найти модуль Base32 на CPAN, который делает то же самое.

Редактировать 2: FWIW, я вижу Convert :: Base32, Encode :: Base32 и MIME :: Base32 на CPAN.

...