/    Sign up×
Community /Pin to ProfileBookmark

[RESOLVED] Caeser cipher help – excluding non-alphabetic characters

Hey everyone,

I wrote a Caesar cipher (a script where, given a message, the letters in the message are shifter x number of places; e.g. A => D, B => E, C => F, X => A, Y => B, Z => C, etc.). It works but it shifts [I]all[/I] ASCII characters. I want to exclude characters that are not A-Z or a-z (outside the ASCII values of 65 – 90 and/or 97 – 122) so that spaces, punctuation, numbers all remain the same instead of shifting as well.

The script is here:

[code=php]<?php
for ($i=0; $i < strlen($string); $i++) {
$ascii = ord($string[$i]);
for ($j=0; $j < $sp; $j++) {
if ($ascii == 90) { //uppercase bound
$ascii = 65; //reset back to ‘A’
}
else if ($ascii == 122) { //lowercase bound
$ascii = 97; //reset back to ‘a’
}
else {
$ascii++;
}
}
$newstring[$i] = chr($ascii);
$encstring = $newstring;
}
?>[/code]

Any help would be appreciated as I can’t figure it out. If you want to see it in action it’s [URL=”http://avondaleriverside.com”]here[/URL].

to post a comment
PHP

19 Comments(s)

Copy linkTweet thisAlerts:
@NogDogJan 30.2014 — Sounds like you just need to change the else to an elseif, and put in that range as the condition?
Copy linkTweet thisAlerts:
@winthropiteauthorFeb 01.2014 — I replaced the last else with else if as follows:

[code=php]
I changed the last else to else if as follows:

else if ($ascii < 65 || ($ascii > 90 || $ascii < 97) || $ascii > 122 ) { //uppercase bound

$ascii++;
}
[/code]


but it still isn't working. I can't seem to wrap my head around it. Any ideas?
Copy linkTweet thisAlerts:
@NogDogFeb 01.2014 — [code=php]
esleif ( ($ascii >= 65 and $ascii <= 90) or ($ascii >= 97 or $ascii <= 122 ) ) {
[/code]


PS: Feel free to change "and" to "&&" and "or" to "||", I just use the word forms out of habit, as I find them a bit easier both to read and type. ?
Copy linkTweet thisAlerts:
@winthropiteauthorFeb 01.2014 — As soon as I saw this I got excited, but I tried it and it still is shifting non-alphabetic characters such as space, period, exclamation point, etc.

Here's what I have:

[code=php]
for ($i=0; $i < strlen($string); $i++) {
$ascii = ord($string[$i]);
for ($j=0; $j < $sp; $j++) {
if ($ascii == 90) { //uppercase bound
$ascii = 65; //reset back to 'A'
}
else if ($ascii == 122) { //lowercase bound
$ascii = 97; //reset back to 'a'
}
else if ( ($ascii >= 65 && $ascii <= 90) || ($ascii >= 97 || $ascii <= 122 ) ) {
$ascii++;
}
}
$newstring[$i] = chr($ascii);
$encstring = $newstring;
}
[/code]

I can't figure out where the problem lies; it seems like this should work. I seriously appreciate all your help on this, by the way! =)
Copy linkTweet thisAlerts:
@NogDogFeb 01.2014 — Here's my revamped stab at it:
[code=php]
<?php

$sp = 5;
$string = "Testing 1, 2, 3, and X, y, Z. All done!";
$newstring = array();
for ($i=0; $i < strlen($string); $i++) {
$ascii = ord($string[$i]);
if ($ascii < 65 || ($ascii > 90 && $ascii < 97) || $ascii > 122) {
$newstring[$i] = $string[$i];
}
else {
for ($j=0; $j < $sp; $j++) {
$ascii += 1;
if ($ascii == 91 or $ascii == 123) {
$ascii -= 26;
}
}
$newstring[$i] = chr($ascii);
}
}
$encstring = implode('', $newstring);
echo $encstring;
[/code]

You do realize this would be extremely easy to decipher, right? ?
Copy linkTweet thisAlerts:
@winthropiteauthorFeb 01.2014 — So very close! I changed $sp = $_POST['offset']; and $string to $_POST['msg']; as that's where the user-entered info comes from.

I entered "hi bill" into the form and on Encode it printed 3 different values: "lm fmpplm fmpp" into the textarea box, "Array" into the Encoded message output at the bottom, and "lm fmpp" where nothing should be printing. It looks like somehow it printed the decoded message twice (i.e. lm fmpp once and lm fmpp again[lm fmpplm fmpp]).

Firebug shows the proper string as follows:

&lt;form class="form-inline" method="post" role="form"&gt;
lm fmpp
&lt;p&gt;Original message:&lt;/p&gt;


So it's somehow printing in between the form and 'Original message' paragraph. Any clue what's going on?
Copy linkTweet thisAlerts:
@winthropiteauthorFeb 01.2014 — Sorry; it's printing here:

[code=html]</form>
lm fmpp
<p>Original message:</p>[/code]
Copy linkTweet thisAlerts:
@NogDogFeb 01.2014 — Without seeing the code, no way to tell why it's outputting what it does where it does. ?
Copy linkTweet thisAlerts:
@winthropiteauthorFeb 01.2014 — I thought this was going to be a one-reply answer; I always end up with weird things going on.. haha.

From index.php

<i>
</i>&lt;form class="form-inline" role="form" method="post"&gt;
&lt;div class="table-responsive"&gt;
&lt;table&gt;
&lt;tr class="tr_top"&gt;
&lt;td class="td_top"&gt;&lt;textarea class="form-control" rows="4" name="msg" placeholder="Your message here." onfocus='this.select()'&gt;&lt;?php
require ('encode.php');
require ('decode.php');

<i> </i> if (isset($_POST['encode'])) {
<i> </i> echo $encstring;
<i> </i> } elseif (isset($_POST['decode'])) {
<i> </i> echo $decstring;
<i> </i> }
<i> </i> ?&gt;&lt;/textarea&gt;&lt;/td&gt;
<i> </i> &lt;/tr&gt;
<i> </i> &lt;tr class="tr_mid"&gt;
<i> </i> &lt;td class="td_mid"&gt;&lt;input type=text class="form-control input_mid" name="offset" value="&lt;?php if (isset($_POST['encode']) || isset($_POST['decode'])) { echo htmlspecialchars($_POST['offset']);} ?&gt;" placeholder="Enter a number." pattern="[0-9]{0,3}" oninvalid="setCustomValidity('Please enter a number between 1 and 999.')" oninput="setCustomValidity('')"&gt;&lt;/td&gt;
<i> </i> &lt;/tr&gt;
<i> </i> &lt;tr class="tr_bottom"&gt;
<i> </i> &lt;td class="td_bottom"&gt;
<i> </i> &lt;input class="input_bottom btn btn-default submit" type="submit" name="encode" value="Encode"&gt;
<i> </i> &lt;input class="input_bottom btn btn-default submit" type="submit" name="decode" value="Decode"&gt;
<i> </i> &lt;input class="input_bottom btn btn-default" type="button" value="Clear"&lt;/td&gt;
<i> </i> &lt;/tr&gt;
<i> </i> &lt;/table&gt;
<i> </i> &lt;/div&gt;&lt;!-- close table-responsive --&gt;
<i> </i> &lt;/form&gt;

<i> </i> &lt;?php
<i> </i> //encode
<i> </i> require ('encode.php');
<i> </i> if (isset($_POST['encode'])) {
<i> </i> echo "&lt;p&gt;Original message:&lt;/p&gt;";
<i> </i> echo "&lt;p class='string ital'&gt;" . $string . "&lt;/p&gt;";
<i> </i> echo "&lt;p&gt;Encoded message:&lt;/p&gt;";
<i> </i> echo "&lt;p class='string ital'&gt;" . $newstring . "&lt;/p&gt;";
<i> </i> }

<i> </i> //decode
<i> </i> require ('decode.php');
<i> </i> if (isset($_POST['decode'])) {
<i> </i> echo "&lt;p&gt;Encoded message:&lt;/p&gt;";
<i> </i> echo "&lt;p class='string ital'&gt;" . $string . "&lt;/p&gt;";
<i> </i> echo "&lt;p&gt;Decoded message:&lt;/p&gt;";
<i> </i> echo "&lt;p class='string ital'&gt;" . $newstring . "&lt;/p&gt;";
<i> </i> }
<i> </i> ?&gt;



---
From encode.php

[code=php]<?php
$sp = $_POST['offset'];
$string = $_POST['msg'];
$newstring = array();
for ($i=0; $i < strlen($string); $i++) {
$ascii = ord($string[$i]);
if ($ascii < 65 || ($ascii > 90 && $ascii < 97) || $ascii > 122) {
$newstring[$i] = $string[$i];
}
else {
for ($j=0; $j < $sp; $j++) {
$ascii += 1;
if ($ascii == 91 || $ascii == 123) {
$ascii -= 26;
}
}
$newstring[$i] = chr($ascii);
}
}
$encstring = implode('', $newstring);
echo $encstring;
?>[/code]
Copy linkTweet thisAlerts:
@NogDogFeb 02.2014 — Remove the echo command at the end of the encode file, and in the main file, use $encstring, not $newstring. ?

In fact, the way you are modularizing things (which is good) could be taken a step further, and putting the encode/decode stuff into functions that return the resulting strings, which you then would call in the main file; or even (gasp!) a class definition with encode() and decode() methods. ?
Copy linkTweet thisAlerts:
@NogDogFeb 02.2014 — In fact, here's how you might put it all into a single function, so that we can enforce the DRY (don't repeat yourself) principle:
[code=php]
<?php
/**
* pseudo encrypt/decrypt a string
* @return string
* @param string $string text to be encoded/decoded
* @param int $offset number of positions to offset letters (only)
*/
function caesar_cipher($string, $offset)
{
if(!ctype_digit($offset)) {
throw new Exception("offset must be an integer");
}
$newstring = array();
for ($i=0; $i < strlen($string); $i++) {
$ascii = ord($string[$i]);
if ($ascii < 65 || ($ascii > 90 && $ascii < 97) || $ascii > 122) {
$newstring[$i] = $string[$i];
}
else {
for ($j=0; $j < abs($offset); $j++) {
$ascii += 1;
if($offset > 0 and ($ascii == 91 or $ascii == 123)) {
$ascii -= 26;
}
elseif($ascii == 65 or $ascii == 97) { // negative offset
$ascii += 26;
}
}
$newstring[$i] = chr($ascii);
}
}
$encstring = implode('', $newstring);
return $encstring;
}
[/code]

To use it, just require() the file with that function definition, and then:
[code=php]
$string = "whatever...";
$sp = 5;
$encrypted = caesar_encrypt($string, $sp);
$decrypted = caesar_encrypt($string, $sp * -1); // just negate the original offset
[/code]
Copy linkTweet thisAlerts:
@NogDogFeb 02.2014 — There's a bug in the above function, which is corrected in the following, where I decided to get all object-oriented on it:
[code=php]
<?php

/**
* Provide a simple pseudo encryption of text only affecting [A-Za-z]
*/
class CaesarCipher
{
/**
* Encrypt it
* @return string
* @param string $string
* @param int $offset
*/
public function encrypt($string, $offset)
{
return $this->execute($string, $offset);
}

/**
* Decrypt it
* @return string
* @param string $string
* @param int $offset
*/
public function decrypt($string, $offset)
{
return $this->execute($string, $offset * -1);
}

/**
* pseudo encrypt/decrypt a string
* @return string
* @param string $string text to be encoded/decoded
* @param int $offset number of positions to offset letters (only)
*/
private function execute($string, $offset)
{
if(!is_int($offset)) {
throw new Exception("offset must be an integer");
}
// need to use this to fix bug:
$inc = 1;
if($offset < 0) {
$inc = -1;
}
$newstring = array();
for ($i=0; $i < strlen($string); $i++) {
$ascii = ord($string[$i]);
if ($ascii < 65 || ($ascii > 90 && $ascii < 97) || $ascii > 122) {
$newstring[$i] = $string[$i];
}
else {
for ($j=0; $j < abs($offset); $j++) {
$ascii += $inc;
if($offset > 0 and ($ascii == 91 or $ascii == 123)) {
$ascii -= 26;
}
elseif($ascii == 64 or $ascii == 96) { // negative offset
$ascii += 26;
}
}
$newstring[$i] = chr($ascii);
}
}
return implode('', $newstring);
}
}
[/code]

sample usage:
[code=php]
$text = "ABCDE, abcde, UVWXYZ, uvwxyz, 123.";
$sp = 4;
$crypt = new CaesarCipher();
echo "<p>Start:<br />$text</p>n";
$text = $crypt->encrypt($text, $sp);
echo "<p>Encrypted:<br />$text</p>n";
$text = $crypt->decrypt($text, $sp);
echo "<p>Decrypted:<br />$text</p>n";
[/code]

Test output:

Start:

ABCDE, abcde, UVWXYZ, uvwxyz, 123.

Encrypted:

EFGHI, efghi, YZABCD, yzabcd, 123.

Decrypted:

ABCDE, abcde, UVWXYZ, uvwxyz, 123.
[/quote]
Copy linkTweet thisAlerts:
@winthropiteauthorFeb 02.2014 — NogDog, this is way above and beyond man. Thanks! I'm going to try to make it all work together with the Object Oriented version.

One last question. As-is, everything works except for decode (decode.php). Here's what I have:

[code=php]<?php
$sp = $_POST['offset'];
$string = $_POST['msg'];
$newstring = array();
for ($i=0; $i < strlen($string); $i++) {
$ascii = ord($string[$i]);
if ($ascii < 65 || ($ascii > 90 && $ascii < 97) || $ascii > 122) {
$newstring[$i] = $string[$i];
}
else {
for ($j=0; $j < $sp; $j++) {
$ascii += 1;
if ($ascii == 91 || $ascii == 123) {
$ascii -= 26;
}
}
$newstring[$i] = chr($ascii);
}
}
$decstring = implode('', $newstring);
?>[/code]


How do I get it to work the other way around? I tried $i--, $ascii -= 1, and $ascii += 26 in various combinations. $ascii -= 1 seemed to work the closest, but for my test case, I typed "Hi, bob!" with an offset of 4, and the encode function worked perfectly. The first time I use Decode it works; it decodes "Lm, fsf!" back to "Hi, bob!". But if I hit Decode again, it prints "De, ^k^!".

When all this is said and done, I owe you a drink of your choice! ?
Copy linkTweet thisAlerts:
@NogDogFeb 02.2014 — You want to do [B]$ascii -= 1[/B] to decrement $ascii, but then you need to use [B]+= 26[/B] if it goes below the minimum value. (You could use $ascii-- or even --$ascii, but for readability/maintainability reasons, I prefer to avoid those.)
Copy linkTweet thisAlerts:
@winthropiteauthorFeb 02.2014 — That outputs "De, ^k^!" when I use 4 as the offset, encode once, decode once (which the first time works fine), then decode again ($ascii-- produces the same result). It should output "De, xkx!".
Copy linkTweet thisAlerts:
@NogDogFeb 02.2014 — When decoding (decrementing the ascii value) the turnover points are 64 and 96 (at the other end of the valid ranges). See my "negative offset" comment in the OOP example.
Copy linkTweet thisAlerts:
@rootFeb 02.2014 — How about putting in to an array all the letters you want A-Z a-z and then creating an array of values that can be used as keys and merged with the array_combine function.

Something like this (not checked if it would produce desired results but...)[code=php]
$asciiArray = array();
$keysArray = array();
// make ascii array
for($ascii=65; $ascii<=122; $ascii++)
if($ascii<=90 or $ascii >=97){
// we're in range
$asciiArray[] = chr( $ascii );
// make keys
$keysArray[] = chr( $ascii);
}
shuffle( &$keysArray);
$code = array_combine($keysArray,$asciiArray);
[/code]


then if you need to send the sequence of the array with the message as the cipher key, you can serialize the array and base64 or AES encode it.
Copy linkTweet thisAlerts:
@winthropiteauthorFeb 02.2014 — A million thanks, seriously!!
Copy linkTweet thisAlerts:
@rootFeb 02.2014 — OK, slight error with

shuffle( &$keysArray);

it should be

shuffle( $keysArray);

I tried it, serialized the array for the keys and code, base64_encoded it then tagged on the code, the breaking apart is easy if you add in a delimiter value like a line return

[CODE]YTo1Mjp7czoxOiJ3IjtzOjE6IkEiO3M6MToiVyI7czoxOiJCIjtzOjE6ImoiO3M6MToiQyI7czoxOiJCIjtzOjE6IkQiO3M6MToiZCI7czoxOiJFIjtzOjE6ImkiO3M6MToiRiI7czoxOiJYIjtzOjE6IkciO3M6MToiSCI7czoxOiJIIjtzOjE6IlYiO3M6MToiSSI7czoxOiJFIjtzOjE6IkoiO3M6MToidiI7czoxOiJLIjtzOjE6IkMiO3M6MToiTCI7czoxOiJJIjtzOjE6Ik0iO3M6MToicCI7czoxOiJOIjtzOjE6IlQiO3M6MToiTyI7czoxOiJaIjtzOjE6IlAiO3M6MToiTSI7czoxOiJRIjtzOjE6Im4iO3M6MToiUiI7czoxOiJKIjtzOjE6IlMiO3M6MToiTyI7czoxOiJUIjtzOjE6InUiO3M6MToiVSI7czoxOiJzIjtzOjE6IlYiO3M6MToidCI7czoxOiJXIjtzOjE6IkwiO3M6MToiWCI7czoxOiJOIjtzOjE6IlkiO3M6MToiUiI7czoxOiJaIjtzOjE6ImYiO3M6MToiYSI7czoxOiJ6IjtzOjE6ImIiO3M6MToiZSI7czoxOiJjIjtzOjE6ImciO3M6MToiZCI7czoxOiJvIjtzOjE6ImUiO3M6MToiSyI7czoxOiJmIjtzOjE6ImwiO3M6MToiZyI7czoxOiJiIjtzOjE6ImgiO3M6MToiWSI7czoxOiJpIjtzOjE6ImsiO3M6MToiaiI7czoxOiJoIjtzOjE6ImsiO3M6MToiciI7czoxOiJsIjtzOjE6IkQiO3M6MToibSI7czoxOiJVIjtzOjE6Im4iO3M6MToibSI7czoxOiJvIjtzOjE6InkiO3M6MToicCI7czoxOiJjIjtzOjE6InEiO3M6MToiRyI7czoxOiJyIjtzOjE6IngiO3M6MToicyI7czoxOiJxIjtzOjE6InQiO3M6MToiUSI7czoxOiJ1IjtzOjE6IlAiO3M6MToidiI7czoxOiJBIjtzOjE6InciO3M6MToiUyI7czoxOiJ4IjtzOjE6ImEiO3M6MToieSI7czoxOiJGIjtzOjE6InoiO30=
Hcgge AelgE, WkFV VkeUgE hc cRqeEcE AkFgc 123456789 FV ReW...[/CODE]


it is a bit heavy on the code length for short messages but each time you use the coder, the cipher changes randomly
×

Success!

Help @winthropite spread the word by sharing this article on Twitter...

Tweet This
Sign in
Forgot password?
Sign in with TwitchSign in with GithubCreate Account
about: ({
version: 0.1.9 BETA 5.19,
whats_new: community page,
up_next: more Davinci•003 tasks,
coming_soon: events calendar,
social: @webDeveloperHQ
});

legal: ({
terms: of use,
privacy: policy
});
changelog: (
version: 0.1.9,
notes: added community page

version: 0.1.8,
notes: added Davinci•003

version: 0.1.7,
notes: upvote answers to bounties

version: 0.1.6,
notes: article editor refresh
)...
recent_tips: (
tipper: @AriseFacilitySolutions09,
tipped: article
amount: 1000 SATS,

tipper: @Yussuf4331,
tipped: article
amount: 1000 SATS,

tipper: @darkwebsites540,
tipped: article
amount: 10 SATS,
)...