Caesar Cypher problem

Hey guys, me again

I’m trying to write a Caesar cypher program to help me learn and understand arrays, so I have to shift to the next letter from the letter in the message to create the cypher.

Here’s what I have so far

public class CaesarCypher 
{
	static String encryptTheMessage(String message)
	{
		String newString = "";

		char alphabet [] = {'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'};

		for(int i = 0; i < message.length(); i++)
		{
			char oldChar = message.charAt(i);
				
			newString = newString + alphabet[oldChar + 1]; 		  
		}

		return newString;
	}

	public static void main(String [] args) 
	{
		System.out.print("Enter a message to encrypt: ");

		String userMessage = Console.readString();

		String encryptedMessage = encryptTheMessage(userMessage);
		
		System.out.print("The encrypted message is: " + encryptedMessage);
	}
}

If you compile it, it gives you an out of bounds exception, but I don’t know where I’m going wrong. I’ve written the problem out on paper and attempted the solution and just can’t get it. So any ideas??

Thx guys

Hauk

Hmmm…this looks like homework…however…

The key to your problem is here:

char oldChar = message.charAt(i);

Remember that arrays access starts at 0 - check the value of oldChar and then walk through the rest of your program.

there’s your bug. when oldChar is 26, you need to do alphabet[ 0 ].

your algorithm is wrong on paper - it should have said (although this isnt necessairly the most efficient way of implementing it)

newString += alphabet[ (oldChar+1) % 26 ];

because mod is the standard mathematical way of making it roll-around the ends.

ALSO, more useuflly algorithmically, this can accomodate a user-supplied ROT - e.g. ROT13 - with no code changes.

Although watch out for negative ROT from the user - I have vague memories that java happily returns negative numbers from % of negative numbers :(.

…but even that won’t work because char converted to a number isn’t simply 0 == ‘a’ etc, IIRC

Correct, its the ASCII value.

yup.

And again this sounds like homework. And we generally don’t do people’s hoemwork here, but since you at elast started it.

You have an aditional problem that charAt(0 returns a char. And a char isn’t ASCII, its unicode.

There are various solutions to this. The slowest but simplest would be (In psuedo code)

FOR EACH CHARACTER IN CHARACTERARRAY
INDEX = 0
DONE = FALSE
WHILE NOT DONE
IF ARRAY[INDEX] == CHARACTER
DONE=TRUE
ELSE
INDEX = INDEX +1
IF INDEX >= ARRAYLENGTH
PRINT “ERROR CHARACTER “+CHARACTER+” NOT FOUND”
QUIT
ENDIF
ENDIF
END WHILE
INDEX = (INDEX+1)%ARRAYLENGTH
OUTPUT = OUTPUT + CHARACTERARRAY[INDEX]
END FOR

This is psuedo code. Its not Java. You will have to actually write the program yourself.

ooh ooh ooh! I have a suggestion too!

The String “+” concatenation operation is converted behind the scenes to use the StringBuilder class, so


String s = "I like to con" + "catenate strings";

becomes something like


StringBuilder sb = new StringBuilder( "I like to con" );
sb.append( "catenate strings" );
String s = sb.toString();

For a once-off concatenation, you don’t need to worry about it, but using string = string + string in a loop will be a lot slower than it could be.

Anyhoo, the upshot is:
You should use a StringBuilder, and it’s append() method, to build a string.

True but probably not significant in this particular case ;D

The looping search for the charcteris goign to be the primary CPU hog.

This could be improved with everything from hash table to a precomputed translation array but, given his apprent level, I wanted to keep it basic 8)

(Actually you couls do it all in Unicode space IF you knew that the ordinal order of Unicode was the same as your translation order,or had a fixed translation from and to Unicdoe order, but he said he wanted to do it with an array so I didn’t mention that.)

Huh? Isn’t that optimized away as it should be, by the compiler, as of about java 1.3?

Instead of looking for the oldChar index in the array, since the alphabet is ordered, you can do:


int  oldChar = message.charAt(i) - 'a';  //This give you the index for the alphabet array
newString = newString + alphabet[(oldChar + 1)%26]; //The case 26 + 1 -> 0 

Rafael…

I seem to remember making huge speed ups switching to StringBuilder (or StringBuffers as there was then) on 1.4, but it may be because the way I was building the string was sufficiently obscured that the compiler didn’t catch it.
In any case, if you’re building a string, it seems sensible to use a StringBuilder ;D

Nope. The compiler optimizes simple cases, but not across loops. As an example the following code

private static String append1() {
    String a = "a" + "b" + "c" + "d";

    return a;
}

private static String append2() {
    String[] subStrings = new String[] {
        "a",
        "b",
        "c",
        "d"
    };

    String a = "";
    for (int i = 0; i < subStrings.length; i++) {
        a += subStrings[i];
    }

    return a;
}

Results in the following byte code (using the 1.4.2_03 compiler)

private static java.lang.String append1();
  Code:
   0:   ldc     #4; //String abcd
   2:   astore_0
   3:   aload_0
   4:   areturn

private static java.lang.String append2();
  Code:
   0:   iconst_4
   1:   anewarray       #5; //class java/lang/String
   4:   dup
   5:   iconst_0
   6:   ldc     #6; //String a
   8:   aastore
   9:   dup
   10:  iconst_1
   11:  ldc     #7; //String b
   13:  aastore
   14:  dup
   15:  iconst_2
   16:  ldc     #8; //String c
   18:  aastore
   19:  dup
   20:  iconst_3
   21:  ldc     #9; //String d
   23:  aastore
   24:  astore_0
   25:  ldc     #10; //String
   27:  astore_1
   28:  iconst_0
   29:  istore_2
   30:  iload_2
   31:  aload_0
   32:  arraylength
   33:  if_icmpge       63
   36:  new     #11; //class java/lang/StringBuffer
   39:  dup
   40:  invokespecial   #12; //Method java/lang/StringBuffer."<init>":()V
   43:  aload_1
   44:  invokevirtual   #13; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   47:  aload_0
   48:  iload_2
   49:  aaload
   50:  invokevirtual   #13; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   53:  invokevirtual   #14; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
   56:  astore_1
   57:  iinc    2, 1
   60:  goto    30
   63:  aload_1
   64:  areturn
}

As you can see the StringBuffer optimization is done inside the loop, but not across the entire loop.

Yup I know, but the decimal value is identical and the back of my Java 2 book doesnt have a Unicode table, it has an ASCII table. So I figured his might be the same way.

My initial impulse was to explain it all, then I held back with just a small but incomplete comment :slight_smile:

Fair enough, thopugh he’ll get REALLY out of range numerbs if he encounters any non-US characters ;D