For Programmers: Free Programming Magazines  


Home > Archive > PERL Beginners > March 2004 > Logical problem with small script??









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author Logical problem with small script??
Gordon Low

2004-03-18, 6:21 pm

Wondered if anyone can throw some light on why this script won't act
properly.

Wrote it to convert a string using a ceaser cipher for a course I am
doing. Basically get the string and replace each character with the
character offset by 3 or 4 up to 26. Have to try up to 26 times so it
was crying out for a script, trouble is it doesn't work the way I
intended and I cannot see how.

No real attempts at error catching have been done as it was only
supposed to be used by myself.
Supposed to do this
Enter
>cipher.pl phhw dw plgqljkw

....and get all 26 different combinations to crack the message.
Trouble is I get this.....
qiix ex qmhrmklx -------> offset = 1 <-------
qiix ex qmhrmklx -------> offset = 1 <-------
rjjy fy rnisnlmy -------> offset = 2 <-------
qiix ex qmhrmklx -------> offset = 1 <-------
rjjy fy rnisnlmy -------> offset = 2 <-------
skkz gz sojtomnz -------> offset = 3 <-------
qiix ex qmhrmklx -------> offset = 1 <-------
rjjy fy rnisnlmy -------> offset = 2 <-------
skkz gz sojtomnz -------> offset = 3 <-------
tlla ha tpkupnoa -------> offset = 4 <-------

Instead of just going through once it is repeating ie loops to 1 then
loops to 2 then loops to 3 up till loops to 25. Cracks the message ok at
offset 23 but I don't want it looping all the time. Code follows, I am
on Linux 8.0 with Perl 5.8, there is probably better ways to do this but
it is driving me mad trying to see the logical problem, hoping someone
can help.

****************************************
***************************
#!/usr/bin/perl -w

use strict;

my @alpha =
("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z");
my %alphat;
my $count;
my $ceascount;
my $op = "";

foreach (@alpha){ # set up a lookup table for the array
offset
$alphat{$alpha[$count]} = $count;
$count++;
}

for ($ceascount = 1; $ceascount < ($#alpha +1);$ceascount++,print
$op){ # try all ceaser cipher offsets
foreach (@ARGV){ # take in each command line arg
for ($count=0; $count < length($_); $count++){ # go through
each arg and convert with cipher
$op .= $alpha[($alphat{substr($_,$count,1)} + $ceascount) %
($#alpha+1)]; # conversion
}
$op .= " "; # space between args
}
$op .= " -------> offset = $ceascount <-------\n"; # final result
} # use another ceaser cipher offset



****************************************
***************************

Jim Halkyard

2004-03-18, 6:22 pm



-----Original Message-----
From: Gordon Low [mailto:glow2797@bigpond.net.au]
Sent: 09 March 2004 08:29
To: beginners@perl.org
Subject: Logical problem with small script??


Wondered if anyone can throw some light on why this script won't act
properly.

Wrote it to convert a string using a ceaser cipher for a course I am
doing. Basically get the string and replace each character with the
character offset by 3 or 4 up to 26. Have to try up to 26 times so it
was crying out for a script, trouble is it doesn't work the way I
intended and I cannot see how.

No real attempts at error catching have been done as it was only
supposed to be used by myself.
Supposed to do this
Enter
>cipher.pl phhw dw plgqljkw

....and get all 26 different combinations to crack the message.
Trouble is I get this.....
qiix ex qmhrmklx -------> offset = 1 <-------
qiix ex qmhrmklx -------> offset = 1 <-------
rjjy fy rnisnlmy -------> offset = 2 <-------
qiix ex qmhrmklx -------> offset = 1 <-------
rjjy fy rnisnlmy -------> offset = 2 <-------
skkz gz sojtomnz -------> offset = 3 <-------
qiix ex qmhrmklx -------> offset = 1 <-------
rjjy fy rnisnlmy -------> offset = 2 <-------
skkz gz sojtomnz -------> offset = 3 <-------
tlla ha tpkupnoa -------> offset = 4 <-------

Instead of just going through once it is repeating ie loops to 1 then
loops to 2 then loops to 3 up till loops to 25. Cracks the message ok at
offset 23 but I don't want it looping all the time. Code follows, I am
on Linux 8.0 with Perl 5.8, there is probably better ways to do this but
it is driving me mad trying to see the logical problem, hoping someone
can help.

****************************************
***************************
#!/usr/bin/perl -w

use strict;

my @alpha =
("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s"
,"t","u","v","w","x","y","z");
my %alphat;
my $count;
my $ceascount;
my $op = "";

foreach (@alpha){ # set up a lookup table for the array
offset
$alphat{$alpha[$count]} = $count;
$count++;
}

for ($ceascount = 1; $ceascount < ($#alpha +1);$ceascount++,print
$op){ # try all ceaser cipher offsets
foreach (@ARGV){ # take in each command line arg
for ($count=0; $count < length($_); $count++){ # go through
each arg and convert with cipher
$op .= $alpha[($alphat{substr($_,$count,1)} + $ceascount) %
($#alpha+1)]; # conversion
}
$op .= " "; # space between args
}
$op .= " -------> offset = $ceascount <-------\n"; # final result
} # use another ceaser cipher offset



****************************************
***************************



Hi Gordon,

The problem here not really the logic of the script. Each time you loop
through on the outer for loop $op is being appended to, not over written.
Each time though the loop you are processing a different offset but printing
that and all previous ones.

To fix this simply move the line my $op = ""; to be the first line inside
the outer for loop and move the print to the end of that loop.

for ($ceascount = 1; $ceascount < ($#alpha +1);$ceascount++)
{
my $op = ""; # This means $op is empty each time through
foreach (@ARGV)
{
for ($count=0; $count < length($_); $count++)
{
$op .= $alpha[($alphat{substr($_,$count,1)} +
$ceascount) %($#alpha+1)];
}
$op .= " ";
}
$op .= " -------> offset = $ceascount <-------\n"; # final result
print $op,"\n";
}

Alternatively move the print to outside the loops all together. ie only
print once.

for ($ceascount = 1; $ceascount < ($#alpha +1);$ceascount++)
{
foreach (@ARGV)
{
for ($count=0; $count < length($_); $count++)
{
$op .= $alpha[($alphat{substr($_,$count,1)} +
$ceascount) %($#alpha+1)];
}
$op .= " ";
}
$op .= " -------> offset = $ceascount <-------\n";
}
print $op,"\n";

Hope this helps,

Jim

--
To unsubscribe, e-mail: beginners-unsubscribe@perl.org
For additional commands, e-mail: beginners-help@perl.org
<http://learn.perl.org/> <http://learn.perl.org/first-response>

Eurospace Szarindar

2004-03-18, 6:22 pm

Hi Gordon,

why don't you use the power of pack and unpack

>print unpack ("C",a) =09=09=09# returns 97
>print pack ("i", unpack ("C",A)+3) # returns D
>print pack ("i*", map ( $_+=3D3 , (unpack ("C*",ABCD))) ) # returns =

DEFG

hope it helps

Michel

-----Message d'origine-----
De: Gordon Low [mailto:glow2797@bigpond.net.au]
Date: mardi 9 mars 2004 09:29
=C0: beginners@perl.org
Objet: Logical problem with small script??


Wondered if anyone can throw some light on why this script won't act
properly.

Wrote it to convert a string using a ceaser cipher for a course I am
doing. Basically get the string and replace each character with the
character offset by 3 or 4 up to 26. Have to try up to 26 times so it
was crying out for a script, trouble is it doesn't work the way I
intended and I cannot see how.=20

No real attempts at error catching have been done as it was only
supposed to be used by myself.
Supposed to do this=20
Enter=20
>cipher.pl phhw dw plgqljkw

=2E..and get all 26 different combinations to crack the message.
Trouble is I get this.....
qiix ex qmhrmklx -------> offset =3D 1 <-------
qiix ex qmhrmklx -------> offset =3D 1 <-------
rjjy fy rnisnlmy -------> offset =3D 2 <-------
qiix ex qmhrmklx -------> offset =3D 1 <-------
rjjy fy rnisnlmy -------> offset =3D 2 <-------
skkz gz sojtomnz -------> offset =3D 3 <-------
qiix ex qmhrmklx -------> offset =3D 1 <-------
rjjy fy rnisnlmy -------> offset =3D 2 <-------
skkz gz sojtomnz -------> offset =3D 3 <-------
tlla ha tpkupnoa -------> offset =3D 4 <-------

Instead of just going through once it is repeating ie loops to 1 then
loops to 2 then loops to 3 up till loops to 25. Cracks the message ok=
at
offset 23 but I don't want it looping all the time. Code follows, I a=
m
on Linux 8.0 with Perl 5.8, there is probably better ways to do this =
but
it is driving me mad trying to see the logical problem, hoping someon=
e
can help.

****************************************
***************************
#!/usr/bin/perl -w

use strict;

my @alpha =3D
("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q",=
"r","s"
,"t","u","v","w","x","y","z");
my %alphat;
my $count;
my $ceascount;
my $op =3D "";

foreach (@alpha){ # set up a lookup table for the ar=
ray
offset
$alphat{$alpha[$count]} =3D $count;
$count++;
}

for ($ceascount =3D 1; $ceascount < ($#alpha +1);$ceascount++,print
$op){ # try all ceaser cipher offsets
foreach (@ARGV){ # take in each command line arg
for ($count=3D0; $count < length($_); $count++){ # go throu=
gh
each arg and convert with cipher
$op .=3D $alpha[($alphat{substr($_,$count,1)} + $ceascount) %
($#alpha+1)]; # conversion
}
$op .=3D " "; # space between args
}
$op .=3D " -------> offset =3D $ceascount <-------\n"; # final res=
ult
} # use another ceaser cipher offset



****************************************
***************************


--=20
To unsubscribe, e-mail: beginners-unsubscribe@perl.org
For additional commands, e-mail: beginners-help@perl.org
<http://learn.perl.org/> <http://learn.perl.org/first-response>

James Edward Gray II

2004-03-18, 6:22 pm

On Mar 9, 2004, at 2:28 AM, Gordon Low wrote:

> Wondered if anyone can throw some light on why this script won't act
> properly.


Looks like you've already been helped with the print problem, but I
thought I would add two things.

[snip]

> #!/usr/bin/perl -w
>
> use strict;
>
> my @alpha =
> ("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r
> ","s","t","u","v","w","x","y","z");
> my %alphat;
> my $count;
> my $ceascount;
> my $op = "";
>
> foreach (@alpha){ # set up a lookup table for the
> array
> offset
> $alphat{$alpha[$count]} = $count;
> $count++;
> }
>
> for ($ceascount = 1; $ceascount < ($#alpha +1);$ceascount++,print
> $op){ # try all ceaser cipher offsets
> foreach (@ARGV){ # take in each command line arg
> for ($count=0; $count < length($_); $count++){ # go through
> each arg and convert with cipher


First, I believe there is a logic bug in the line above. I believe you
meant (length($_) - 1). The warnings the program was issuing helped me
find it.

> $op .= $alpha[($alphat{substr($_,$count,1)} + $ceascount) %
> ($#alpha+1)]; # conversion
> }
> $op .= " "; # space between args
> }
> $op .= " -------> offset = $ceascount <-------\n"; # final result
> } # use another ceaser cipher offset


And two, here's a more Perlish example of your script:

#!/usr/bin/perl

use strict;
use warnings;

my @alpha = ('a'..'z');
my %alphat;
foreach (0..$#alpha){
$alphat{$alpha[$_]} = $_;
}

my $op = '';
for my $ceascount (1..scalar @alpha){
foreach (@ARGV) {
for my $count (0..(length($_) - 1)){
$op .= $alpha[($alphat{substr $_, $count, 1} + $ceascount) %
@alpha];
}
$op .= ' '; # space between args
}
$op .= " -------> offset = $ceascount <-------\n";
}
print $op, "\n";

__END__

I'm not trying to say you need to be writing like this. I just thought
you might find a shortcut or two in there you like.

Good luck.

James

John W. Krahn

2004-03-18, 6:22 pm

Gordon Low wrote:
>
> Wondered if anyone can throw some light on why this script won't act
> properly.


Certainly.

> Wrote it to convert a string using a ceaser cipher for a course I am
> doing. Basically get the string and replace each character with the
> character offset by 3 or 4 up to 26. Have to try up to 26 times so it
> was crying out for a script, trouble is it doesn't work the way I
> intended and I cannot see how.
>
> No real attempts at error catching have been done as it was only
> supposed to be used by myself.
> Supposed to do this
> Enter
> ...and get all 26 different combinations to crack the message.
> Trouble is I get this.....
> qiix ex qmhrmklx -------> offset = 1 <-------
> qiix ex qmhrmklx -------> offset = 1 <-------
> rjjy fy rnisnlmy -------> offset = 2 <-------
> qiix ex qmhrmklx -------> offset = 1 <-------
> rjjy fy rnisnlmy -------> offset = 2 <-------
> skkz gz sojtomnz -------> offset = 3 <-------
> qiix ex qmhrmklx -------> offset = 1 <-------
> rjjy fy rnisnlmy -------> offset = 2 <-------
> skkz gz sojtomnz -------> offset = 3 <-------
> tlla ha tpkupnoa -------> offset = 4 <-------
>
> Instead of just going through once it is repeating ie loops to 1 then
> loops to 2 then loops to 3 up till loops to 25. Cracks the message ok at
> offset 23 but I don't want it looping all the time. Code follows, I am
> on Linux 8.0 with Perl 5.8, there is probably better ways to do this but
> it is driving me mad trying to see the logical problem, hoping someone
> can help.
>
> ****************************************
***************************
> #!/usr/bin/perl -w
> use strict;
>
> my @alpha =
> ("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z");


Ouch. Writing out each letter is error prone. It is better to use the
range operator to create the list.

my @alpha = 'a' .. 'z';

> my %alphat;
> my $count;
> my $ceascount;
> my $op = "";
>
> foreach (@alpha){ # set up a lookup table for the array
> offset
> $alphat{$alpha[$count]} = $count;
> $count++;
> }


When this loop runs I get the warning:

Use of uninitialized value in array element at ./cipher.pl line 92.

Where line 92 is "$alphat{$alpha[$count]} = $count;" because the initial
value in $count is undef. You can explicitly set $count:

my $count = 0;
foreach ( @alpha ){ # set up a lookup table for the array offset
$alphat{ $alpha[ $count ] } = $count;
$count++;
}

However, I was wondering why you are looping through the values in @alpha
but not using them?

foreach ( @alpha ){ # set up a lookup table for the array offset
$alphat{ $_ } = $count++;
}

That eliminates the warnings message and makes the code shorter and easier
to read but you can do the same thing without the foreach loop and the $count
variable by using a hash slice:

# set up a lookup table for the array offset
@alphat{ @alpha } = 0 .. $#alpha;


> for ($ceascount = 1; $ceascount < ($#alpha +1);$ceascount++,print
> $op){ # try all ceaser cipher offsets


That looks more like C then Perl. :-) A more perl-ish way to write that is:

for my $ceascount ( 1 .. $#alpha ) { # try all ceaser cipher offsets
...
}
continue {
print $op;
}

Your problem is that after printing $op it retains the contents from the
previous iteration of the loop. You need to clear out the contents of $op
after you print it:

for my $ceascount ( 1 .. $#alpha ) { # try all ceaser cipher offsets
...
}
continue {
print $op;
$op = '';
}


> foreach (@ARGV){ # take in each command line arg
> for ($count=0; $count < length($_); $count++){ # go through
> each arg and convert with cipher


A more perl-ish for loop would be:

for my $count ( 0 .. length() - 1 ){ # go through each arg and convert with cipher


> $op .= $alpha[($alphat{substr($_,$count,1)} + $ceascount) %
> ($#alpha+1)]; # conversion
> }
> $op .= " "; # space between args
> }
> $op .= " -------> offset = $ceascount <-------\n"; # final result
> } # use another ceaser cipher offset


A simpler way to write that:

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

my @alpha = 'a' .. 'z';
my @ceaser = @alpha[ 1 .. $#alpha, 0 ];

my %alphat;
@alphat{ @alpha } = @ceaser;

my $code = "@ARGV";

for my $ceascount ( 1 .. $#alpha ) {
( my $message = $code ) =~ s/([@alpha])/ exists $alphat{ $1 } ? $alphat{ $1 } : $1 /eg;
print "$message -------> offset = $ceascount <-------\n";
@alphat{ @alpha } = @ceaser = @ceaser[ 1 .. $#ceaser, 0 ];
}

__END__



John
--
use Perl;
program
fulfillment
Gordon Low

2004-03-18, 6:22 pm


Thanks to all for replying, biggest mistake I made was not clearing the
string but have been comparing how others would have done the same
script and finding it very useful. Am not a great scripter so all hints
welcome, will be checking scripts for techniques.




On Wed, 2004-03-10 at 09:09, John W. Krahn wrote:
> Gordon Low wrote:
>
> Certainly.
>
>
> Ouch. Writing out each letter is error prone. It is better to use the
> range operator to create the list.
>
> my @alpha = 'a' .. 'z';
>
>
> When this loop runs I get the warning:
>
> Use of uninitialized value in array element at ./cipher.pl line 92.
>
> Where line 92 is "$alphat{$alpha[$count]} = $count;" because the initial
> value in $count is undef. You can explicitly set $count:
>
> my $count = 0;
> foreach ( @alpha ){ # set up a lookup table for the array offset
> $alphat{ $alpha[ $count ] } = $count;
> $count++;
> }
>
> However, I was wondering why you are looping through the values in @alpha
> but not using them?
>
> foreach ( @alpha ){ # set up a lookup table for the array offset
> $alphat{ $_ } = $count++;
> }
>
> That eliminates the warnings message and makes the code shorter and easier
> to read but you can do the same thing without the foreach loop and the $count
> variable by using a hash slice:
>
> # set up a lookup table for the array offset
> @alphat{ @alpha } = 0 .. $#alpha;
>
>
>
> That looks more like C then Perl. :-) A more perl-ish way to write that is:
>
> for my $ceascount ( 1 .. $#alpha ) { # try all ceaser cipher offsets
> ...
> }
> continue {
> print $op;
> }
>
> Your problem is that after printing $op it retains the contents from the
> previous iteration of the loop. You need to clear out the contents of $op
> after you print it:
>
> for my $ceascount ( 1 .. $#alpha ) { # try all ceaser cipher offsets
> ...
> }
> continue {
> print $op;
> $op = '';
> }
>
>
>
> A more perl-ish for loop would be:
>
> for my $count ( 0 .. length() - 1 ){ # go through each arg and convert with cipher
>
>
>
> A simpler way to write that:
>
> #!/usr/bin/perl
> use warnings;
> use strict;
>
> my @alpha = 'a' .. 'z';
> my @ceaser = @alpha[ 1 .. $#alpha, 0 ];
>
> my %alphat;
> @alphat{ @alpha } = @ceaser;
>
> my $code = "@ARGV";
>
> for my $ceascount ( 1 .. $#alpha ) {
> ( my $message = $code ) =~ s/([@alpha])/ exists $alphat{ $1 } ? $alphat{ $1 } : $1 /eg;
> print "$message -------> offset = $ceascount <-------\n";
> @alphat{ @alpha } = @ceaser = @ceaser[ 1 .. $#ceaser, 0 ];
> }
>
> __END__
>
>
>
> John
> --
> use Perl;
> program
> fulfillment
>
> --
> To unsubscribe, e-mail: beginners-unsubscribe@perl.org
> For additional commands, e-mail: beginners-help@perl.org
> <http://learn.perl.org/> <http://learn.perl.org/first-response>
>
>



Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com