jcr has asked for the wisdom of the Perl Monks concerning the following question:

Hello, I am trying to encrypt a string using a predetermined encryption map. I know that this a very very weak form of encryption, yet that is not the real problem. I think that this working code is too long and ugly to be efficient. Can anyone suggest a way to reduce the lines of code and also make the code more efficient? Thanks jcr =========================================================== sub encrypt { my ($self, $STRING) = @_; my $ENCRYPTED_STRING = ''; my $LENGTH = length($STRING); my @STRING = split(//,$STRING); my $ENCRYPTION_MAP = "abcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyz0123456789.&@="; my @ENCRYPTION_ARRAY = split(//,$ENCRYPTION_MAP); my $MAP_LENGTH = length($ENCRYPTION_MAP); my $MAP_HALF = length($ENCRYPTION_MAP)/2; for ($i=0; $i < $LENGTH; $i++) { my $CHARACTER = $STRING$i; my $POSITION = index($ENCRYPTION_MAP, "$CHARACTER"); if ($POSITION < 0) { $ENCRYPTED_STRING .= &EscapeCharacter("$CHARACTER"); } else { $ENCRYPTED_STRING .= &EscapeCharacter("$ENCRYPTION_ARRAY($POSITION + $MAP_HALF) % $MAP_LENGTH"); } } return $ENCRYPTED_STRING; } ################################## # CHARACTER ESCAPING SUBROUTINE: # ################################## sub EscapeCharacter { my $CHARACTER_TO_ESCAPE = shift; if (($CHARACTER_TO_ESCAPE eq '&') || ($CHARACTER_TO_ESCAPE eq '\\') || ($CHARACTER_TO_ESCAPE eq ' ') || ($CHARACTER_TO_ESCAPE eq '%') || ($CHARACTER_TO_ESCAPE eq '@') || ($CHARACTER_TO_ESCAPE eq '/') || ($CHARACTER_TO_ESCAPE eq '#') || ($CHARACTER_TO_ESCAPE eq '=') || ($CHARACTER_TO_ESCAPE eq '"') || ($CHARACTER_TO_ESCAPE eq "'") || ($CHARACTER_TO_ESCAPE eq '+') || ($CHARACTER_TO_ESCAPE eq '|') || ($CHARACTER_TO_ESCAPE eq '?')){ $CHARACTER_TO_ESCAPE = ord("$CHARACTER_TO_ESCAPE"); $CHARACTER_TO_ESCAPE = sprintf "%x", $CHARACTER_TO_ESCAPE; return ".$CHARACTER_TO_ESCAPE"; } else {return $CHARACTER_TO_ESCAPE;} }

Replies are listed 'Best First'.
Re: More Efficient Subroutine
by chromatic (Archbishop) on Sep 30, 2000 at 00:03 UTC
    I didn't bother to deal with the punctuation characters, as once you understand the following technique, you'll be able to write your own:

    $string =~ tr/0-9A-Za-z/a-mN-ZA-Mn-z0-9/;

    That's certainly not an exact match for your substitution cipher, but it's a solution of the tr/// type that tilly mentioned.

Re (tilly) 1: More Efficient Subroutine
by tilly (Archbishop) on Sep 29, 2000 at 23:46 UTC
    Breaking this kind of encryption is downright trivial. People who know what they are doing can do it by hand in a matter of minutes based on character frequency. If you want encryption, I strongly recommend looking for an appropriate module.

    But it looks like you knew that. Even without formatting (the <code> tag exists for a reason) it looks like you are trying to do ROT13. The problem you have is that you have a lot of this or this or this or... logic which has to be scanned on each character. It is far faster to arrange to have a hash that contains the answer and do a lookup. So the key logic would be:

    my $encrypted = join '', map {exists $trans{$_} ? $trans{$_} : $_} spl +it //, $orig;
    Of course the tr function gives an even more efficient solution in this case.

    But the general performance note is that any time you can convert scanning logic (scanning a string, array, file, whatever) to a hash lookup, you moved to a more efficient algorithm and if the thing scanned was long should see significant performance improvments.

Re: More Efficient Subroutine
by jcr (Initiate) on Sep 30, 2000 at 02:43 UTC
    Thank You jptxs for fixing up my post. Thank You tilly and chromatic for your very insightful suggestions. I have to say that i had blindly copied (almost line by line) that encryption subroutine from Java. I completely disregarded the power of perl (i guess i was in the Java mindset and did not look at the problem thinking in Perl). I am almost ashamed of the previous code now that it reads like this in Perl:
    sub encrypt { my $string = shift; $string =~ tr/a-mN-ZA-Mn-z0-9.&@=/H-Mn-z0-9.&@=a-mN-ZA-G/; return $string; }
Re: More Efficient Subroutine
by jptxs (Curate) on Sep 29, 2000 at 23:32 UTC

    use code tags...see the help about what they are.

    that will get you more efficent perlmonk advice, but that's all i know :)

    =========================================================== sub encrypt { my ($self, $STRING) = @_; my $ENCRYPTED_STRING = ''; my $LENGTH = length($STRING); my @STRING = split(//,$STRING); my $ENCRYPTION_MAP = "abcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLMnopq +rstuvwxyz0123456789.&@="; my @ENCRYPTION_ARRAY = split(//,$ENCRYPTION_MAP); my $MAP_LENGTH = length($ENCRYPTION_MAP); my $MAP_HALF = length($ENCRYPTION_MAP)/2; for ($i=0; $i < $LENGTH; $i++) { my $CHARACTER = $STRING$i; my $POSITION = index($ENCRYPTION_MAP, "$CHARACTER"); if ($POSITION ($POSITION + $MAP_HALF) % $MAP_LENGTH"); } } return $ENCRYPTED_STRING; } ################################## # CHARACTER ESCAPING SUBROUTINE: # ################################## sub EscapeCharacter { my $CHARACTER_TO_ESCAPE = shift; if(($CHARACTER_TO_ESCAPE eq '&') || ($CHARACTER_TO_ESCAPE eq '\\') + || ($CHARACTER_TO_ESCAPE eq ' ') || ($CHARACTER_TO_ESCAPE eq '%') || ($CHARACTER_TO_ESCAPE eq '@') | +| ($CHARACTER_TO_ESCAPE eq '/') || ($CHARACTER_TO_ESCAPE eq '#') || ($CHARACTER_TO_ESCAPE eq '=') | +| ($CHARACTER_TO_ESCAPE eq '"') || ($CHARACTER_TO_ESCAPE eq "'") || ($CHARACTER_TO_ESCAPE eq '+') | +| ($CHARACTER_TO_ESCAPE eq '|') || ($CHARACTER_TO_ESCAPE eq '?')) { $CHARACTER_TO_ESCAPE = ord("$CHARACTER_TO_ESCAPE"); $CHARACTER_TO_ESCAPE = sprintf "%x", $CHARACTER_TO_ESCAPE; return ".$CHARACTER_TO_ESCAPE"; } else { return $CHARACTER_TO_ESCAPE;} }

    -- I'm a solipsist, and so is everyone else. (think about it)