C# Encoder crashing (Array Issue) [duplicate] - c#

This question already has answers here:
What is an IndexOutOfRangeException / ArgumentOutOfRangeException and how do I fix it?
(5 answers)
Closed 3 months ago.
Hi just needed a little bit of help with a crash im having, so im putting together a little program that will work similar to a Caesar cipher by shifting the letters by a set value, however each subsequent letter is shifted to a different letter, rather then all letters being shifted by the same value (which is easy to crack with frequency analysis).
Anyway the program works fine if the word being encoded is shorter or the same length as the code (like a 7 letter word being encoded by a 7 digit code), but as soon as you try to encode a word longer than it, it crashes with an array out of bounds error or something.
What needs to happen is it needs to loop back around to the start of the code until no more letters need encoding.
```
```
static char ZipherEncode(char input, int shift)
{
char letter = (char)(input + shift);
if (letter > 'z')
{
letter = (char)(letter - 26);
}
else if (letter < 'a')
{
letter = (char)(letter + 26);
}
return letter;
}
static char ZipherDecode(char input, int shift)
{
char letter = (char)(input - shift);
if (letter > 'z')
{
letter = (char)(letter - 26);
}
else if (letter < 'a')
{
letter = (char)(letter + 26);
}
return letter;
}
int[] codeShift = new int[] { 4, 2, 6, 3, 0, 5, 1 };
Console.WriteLine("Please enter a word to encode: ");
string input = Console.ReadLine();
char[] zipherEncoded = new char[input.Length];
zipherEncoded = input.ToCharArray();
Console.WriteLine("\n\nENCODED: ");
int currCode = codeShift[0];
int counter = 0;
for (int i = 0; i < zipherEncoded.Length; i++)
{
if (zipherEncoded[i] != '\0')
{
//DEBUG
Console.WriteLine("WE ARE HERE!");
Console.Write("{0}", ZipherEncode(zipherEncoded[i], codeShift[i]));
}
else
{
//DEBUG
Console.WriteLine("WE ARE HERE (ELSE)!");
Console.Write("{0}", ZipherEncode(zipherEncoded[i], codeShift[currCode]));
//Console.Write("{0}", ZipherEncode(zipherEncoded[i], codeShift[counter]));
//Console.Write("{0}", ZipherEncode(zipherEncoded[i], Array.IndexOf(codeShift, currCode)));
counter++;
}
}
Console.ReadLine();
```
```
The output when I encode the word "volcano" reads -
ENCODED:
zqrfasp
This is correct, however when I try and encode "volcanocat", because the word is longer than the 7 digits in the code it crashes, the output should read -
ENCODED:
zqrfaspgcz
the "cat" part of the phrase should be encoded with a shift of 426 <<< loops back to the start of the codeShift array storing our shifts.
C (Shift of 4) > G
A (Shift of 2) > C
T (Shift of 6) > Z
As from the parts that are commented out you can see im trying to find a way to have it loop back around to the start of the shift code to finish off the final letters.
Im really new to C# so there may be a function built in to do what I want, but I cant get my head around it!
I think I cant see the forrest through the trees!

The problem is here:
ZipherEncode(zipherEncoded[i], codeShift[i])
In this code, i represents an index in the input string, but you're also using it as an index in the codeShift array. Since the input string may be longer than the array, you'll get an out of bounds error.
One way to resolve this would be to use a modulus operation (the% operator), which returns the remainder of a division. This can be used to always constrain the index to a value within the size of the codeShift array.
For example, let's pretend that the word has 6 characters but we only have 3 items in the codeShift array. We're fine for indexes 0, 1, and 2, but when we get to 3 we need to access the value at 0 again in the array. To do this, we can say:
int codeShiftIndex = i % codeShift.Length;
As we stated, codeShift has a length of 3, so when i is 3, i % 3 == 0 (the remainder of three divided by three), and when i is 4, i % 3 == 1, etc.
So your code change would look like:
ZipherEncode(zipherEncoded[i], codeShift[i % codeShift.Length])

Related

Caesar Cipher (C#)

I would like to preface this by saying it is for an assessment, so I don't want you to directly give me the answer, I would like you to point me in the right direction, slightly tweak what I have done or just tell me what I should look into doing.
I am trying to create a Caesar Cipher to decipher a text document we have been given. It needs to print all the possible shifts in the console and output the final one to a text document. I'm thinking of trying to add frequency analysis to this later to find the correct one, but I have some problems now that I need to sort out before I can do that.
This is what I have made so far:
using System;
using System.IO;
class cipher
{
public static void Main(string[] args)
{
string output = "";
int shift;
bool userright = false;
string cipher = File.ReadAllText("decryptme.txt");
char[] decr = cipher.ToCharArray();
do {
Console.WriteLine("How many times would you like to shift? (Between 0 and 26)");
shift = Convert.ToInt32(Console.ReadLine());
if (shift > 26) {
Console.WriteLine("Over the limit");
userright = false;
}
if (shift < 0) {
Console.WriteLine("Under the limit");
userright = false;
}
if (shift <= 26 && shift >= 0)
{
userright = true;
}
} while (userright == false);
for (int i = 0; i < decr.Length; i++)
{
{
char character = decr[i];
character = (char)(character + shift);
if (character == '\'' || character == ' ')
continue;
if (character > 'Z')
character = (char)(character - 26);
else if (character < 'A')
character = (char)(character + 26);
output = output + character;
}
Console.WriteLine("\nShift {0} \n {1}", i + 1, output);
}
StreamWriter file = new StreamWriter("decryptedtext.txt");
file.WriteLine(output);
file.Close();
}
}
Right now it compiles and read the document but when it runs in the console it prints shift one as 1 letter from the encoded text, shift 2 as 2 letters from it, etc.
I have no idea what I have done wrong and any help would be greatly appreciated. I have also started thinking about ASCII values for letters but have no idea how to implement this.
Once again, please don't just give me the answer or I will not have learned anything from this - and I have been trying to crack this myself but had no luck.
Thanks.
Break the problem down into smaller bite-sized chunks. Start by printing a single shifted line, say with a shift of 1.
When you have that part working correctly (and only then) extend your code to print 26 lines with shifts of 0, 1, 2, 3, ... 26. I am not sure if your instructor wants either or both of shift 0 at the start and shift 26 at the end. You will need to ask.
Again, get that working correctly, and write new code to analyse one line only, and give it some sort of score. Get that working properly.
Now calculate the scores for all the lines and pick out the line with the best score. That should be the right answer. If it isn't then you will need to check your scoring method.
Writing small incremental changes to a very simple starting program is usually a lot easier than trying to go straight from a blank screen to the full, complex, program. Add the complexity gradually, one piece at a time, testing as you go.

Generate a Unique 6 digit number from set of 0-49 such that no two numbers repeat?

I'm given a set {0,1,2,....49}.
I have to generate a set of numbers(1 or 2 digit) such that no two numbers in it repeat
ex
1 2 3 4 5 6
or
1 23 32 8
Here 2 is repeating but 2 digit num '23' or '32' is not.
This is where im getting stuck.
I tried This. But could not find solution.
Please help.
Edit: Sorry for the unclear explaination before.
this is 1 time generation and i want to generate all such possible numbers.
Here is a working example for the question you asked. hope that it will help you. i think there is no need for line by line explanation for code since the code speak for itself.
Random rnd = new Random();
string output="";
List<int> numbersTaken = new List<int>();
int curNumber;
while (output.Length < 6)
{
Label: curNumber = rnd.Next(0, 49);
if (!numbersTaken.Contains(curNumber) && output.Length + curNumber.ToString().Length <= 6)
{
output += curNumber;
numbersTaken.Add(curNumber);
}
else
{
if (output.Length <= 6) { return; }
goto Label;
}
}
Updates:
In particular point of execution, Let the output be output="12451" and the curNumber=33, so it is clear that 33 is not yet taken but if we add 33 to the output then the length of output exceed the limit6 so we have to look for another random number(goto redirect to the next random number) which is not present in the string.

Reading an int from a file returns ASCII

I'm currently making a game but I seem to have problems reading values from a text file. For some reason, when I read the value, it gives me the ASCII code of the value rather than the actual value itself when I wrote it to the file. I've tried about every ASCII conversion function and string conversion function, but I just can't seem to figure it out.
I use a 2D array of integers. I use a nested for loop to write each element into the file. I've looked at the file and the values are correct, but I don't understand why it's returning the ASCII code. Here's the code I'm using to write and read to file:
Writing to file:
for (int i = 0; i < level.MaxRows(); i++)
{
for (int j = 0; j < level.MaxCols(); j++)
{
fileWrite.Write(level.GetValueAtIndex(i, j) + " ");
//Console.WriteLine(level.GetValueAtIndex(i, j));
}
//add new line
fileWrite.WriteLine();
}
And here's the code where I read the values from the file:
string str = "";
int iter = 0; //used to iterate in each column of array
for (int i = 0; i < level.MaxRows(); i++)
{
iter = 0;
//TODO: For some reason, the file is returning ASCII code, convert to int
//keep reading characters until a space is reached.
str = fileRead.ReadLine();
//take the above string and extract the values from it.
//Place each value in the level.
foreach (char id in str)
{
if (id != ' ')
{
//convert id to an int
num = (int)id;
level.ChangeTile(i, iter, num);
iter++;
}
}
This is the latest version of the loop that I use to read the values. Reading other values is fine; it's just when I get to the array, things go wrong. I guess my question is, why did the conversion to ASCII happen? If I can figure that out, then I might be able to solve the issue. I'm using XNA 4 to make my game.
This is where the convertion to ascii is happening:
fileWrite.Write(level.GetValueAtIndex(i, j) + " ");
The + operator implicitly converts the integer returned by GetValueAtIndex into a string, because you are adding it to a string (really, what did you expect to happen?)
Furthermore, the ReadLine method returns a String, so I am not sure why you'd expect a numeric value to magically come back here. If you want to write binary data, look into BinaryWriter
This is where you are converting the characters to character codes:
num = (int)id;
The id variable is a char, and casting that to int gives you the character code, not the numeric value.
Also, this converts a single character, not a whole number. If you for example have "12 34 56 " in your text file, it will get the codes for 1, 2, 3, 4, 5 and 6, not 12, 34 and 56.
You would want to split the line on spaces, and parse each substring:
foreach (string id in str.Split(' ')) {
if (id.Length > 0) {
num = Int32.Parse(id);
level.ChangeTile(i, iter, num);
iter++;
}
}
Update: I've kept the old code (below) with the assumption that one record was on each line, but I've also added a different way of doing it that should work with multiple integers on a line, separated by a space.
Multiple records on one line
str = fileRead.ReadLine();
string[] values = str.Split(new Char[] {' '});
foreach (string value in values)
{
int testNum;
if (Int32.TryParse(str, out testnum))
{
// again, not sure how you're using iter here
level.ChangeTile(i, iter, num);
}
}
One record per line
str = fileRead.ReadLine();
int testNum;
if (Int32.TryParse(str, out testnum))
{
// however, I'm not sure how you're using iter here; if it's related to
// parsing the string, you'll probably need to do something else
level.ChangeTile(i, iter, num);
}
Please note that the above should work if you write out each integer line-by-line (i.e. how you were doing it via the WriteLine which you remarked out in your code above). If you switch back to using a WriteLine, this should work.
You have:
foreach (char id in str)
{
//convert id to an int
num = (int)id;
A char is an ASCII code (or can be considered as such; technically it is a unicode code-point, but that is broadly comparable assuming you are writing ANSI or low-value UTF-8).
What you want is:
num = (int)(id - '0');
This:
fileWrite.Write(level.GetValueAtIndex(i, j) + " ");
converts the int returned from level.GetValueAtIndex(i, j) into a string. Assuming the function returns the value 5 for a particular i and j then you write "5 " into the file.
When you then read it is being read as a string which consists of chars and you get the ASCII code of 5 when you cast it simply to an int. What you need is:
foreach (char id in str)
{
if (id != ' ')
{
//convert id to an int
num = (int)(id - '0'); // subtract the ASCII value for 0 from your current id
level.ChangeTile(i, iter, num);
iter++;
}
}
However this only works if you only ever are going to have single digit integers (only 0 - 9). This might be better:
foreach (var cell in fileRead.ReadLine().Split(' '))
{
num = Int.Parse(cell);
level.ChangeTile(i, iter, num);
iter++;
}

How to compare 2 strings with int out of errors?

I've searched online for a diff algorithm but none of them do what I am looking for. It is for a texting contest (as in cell phone) and I need the entry text compared to the master text recording the errors along the way. I am semi-new to C# and I get most of the string functions and didn't think this was going to be that hard of a problem, but alas I just can't wrap my head around it.
I have a form with 2 rich-text-boxes (one on top of the other) and 2 buttons. The top box is the master text (string) and the bottom box is the entry text (string). Every contestant is sending a text to an email account, from the email we copy and paste the text into the Entry RTB and compare to the Master RTB. For each single word and single space counts as a thing to check. A word, no matter how many errors it has, is still 1 error. And for every error add 1 sec. to their time.
Examples:
Hello there! <= 3 checks (2 words and 1 space)
Helothere! <= 2 errors (Helo and space)
Hello there!! <= 1 error (extra ! at end of there!)
Hello there! How are you? <= 9 checks (5 words and 4 spaces)
Helothere!! How a re you? <= still 9 checks, 4 errors(helo, no space, extra !, and a space in are)
Hello there!# Ho are yu?? <= 3 errors (# at end of there!, no w, no o and extra ? (all errors are still under the 1 word)
What I have so far:
I've created 6 arrays (3 for master, 3 for entry) and they are
CharArray of all chars
StringArray of all strings(words) including the spaces
IntArray with length of the string in each StringArray
My biggest trouble is if the entry text is wrong and it's shorter or longer than the master. I keep getting IndexOutOfRange exceptions (understandably) but can't fathom how to go about checking and writing the code to compensate.
I hope I have made myself clear enough as to what I need help with. If anyone could give some code examples or something to shoot me in the right path would be very helpful.
Have you looked into the Levenshtein distance algorithm? It returns the number of differences between two strings, which, in your case would be texting errors. Implementing the algorithm based off the pseudo-code found on the wikipedia page passes the first 3 of your 4 use cases:
Assert.AreEqual(2, LevenshteinDistance("Hello there!", "Helothere!");
Assert.AreEqual(1, LevenshteinDistance("Hello there!", "Hello there!!"));
Assert.AreEqual(4, LevenshteinDistance("Hello there! How are you?", "Helothere!! How a re you?"));
Assert.AreEqual(3, LevenshteinDistance("Hello there! How are you?", "Hello there!# Ho are yu??")); //fails, returns 4 errors
So while not perfect out of the box, it is probably a good starting point for you. Also, if you have too much trouble implementing your scoring rules, it might be worth revisiting them.
hth
Update:
Here is the result of the string you requested in the comments:
Assert.AreEqual(7, LevenshteinDistance("Hello there! How are you?", "Hlothere!! Hw a reYou?"); //fails, returns 8 errors
And here is my implementation of the Levenshtein Distance algorithm:
int LevenshteinDistance(string left, string right)
{
if (left == null || right == null)
{
return -1;
}
if (left.Length == 0)
{
return right.Length;
}
if (right.Length == 0)
{
return left.Length;
}
int[,] distance = new int[left.Length + 1, right.Length + 1];
for (int i = 0; i <= left.Length; i++)
{
distance[i, 0] = i;
}
for (int j = 0; j <= right.Length; j++)
{
distance[0, j] = j;
}
for (int i = 1; i <= left.Length; i++)
{
for (int j = 1; j <= right.Length; j++)
{
if (right[j - 1] == left[i - 1])
{
distance[i, j] = distance[i - 1, j - 1];
}
else
{
distance[i, j] = Min(distance[i - 1, j] + 1, //deletion
distance[i, j - 1] + 1, //insertion
distance[i - 1, j - 1] + 1); //substitution
}
}
}
return distance[left.Length, right.Length];
}
int Min(int val1, int val2, int val3)
{
return Math.Min(val1, Math.Min(val2, val3));
}
You need to come up with a scoring systems that works for you're situation.
I would make a word array after each space.
If a word is found on the same index +5.
If a word is found on the same index +-1 index location +3 (keep a counter how much words differ to increase the +- correction
If a needed word is found as part of another word +2
etc.etc. Matching words is hard, getting up with a rules engine that works is 'easier'
I once implemented an algorithm (which I can't find at the moment, I'll post code when I find it) which looked at the total number of PAIRS in the target string. i.e. "Hello, World!" would have 11 pairs, { "He", "el", "ll",...,"ld", "d!" }.
You then do the same thing on an input string such as "Helo World" so you have { "He",...,"ld" }.
You can then calculate accuracy as a function of correct pairs (i.e. input pairs that are in the list of target pairs), incorrect pairs (i.e. input pairs that do not exists in the list of target pairs), compared to the total list of target pairs. Over long enough sentences, this measure would be very accurate fair.
A simple algorithm would be to check letter by letter. If the letters differ increment the num of errors. If the next pairing of letters match, its a switched letter so just continue. If the messup matches the next letter, it is an omission and treat it accordingly. If the next letter matches the messed up one, its an insertion and treat it accordingly. Else the person really messed up and continue.
This doesn't get everything but with a few modifications this could become comprehensive.
a weak attempt at pseudocode:
edit: new idea. look at comments. I don't know the string functions off the top of my head so you'll have to figure that part out. The algorithm kinda fails for words that repeat a lot though...
string entry; //we'll pretend that this has stuff inside
string master; // this too...
string tempentry = entry; //stuff will be deleted so I need a copy to mess up
int e =0; //word index for entry
int m = 0; //word index for master
int errors = 0;
while(there are words in tempentry) //!tempentry.empty() ?
string mword = the next word in master;
m++;
int eplace = find mword in tempentry; //eplace is the index of where the mword starts in tempentry
if(eplace == -1) //word not there...
continue;
else
errors += m - e;
errors += find number of spaces before eplace
e = m // there is an error
tempentry = stripoff everything between the beginning and the next word// substring?
all words and spaces left in master are considered errors.
There are a couple of bounds checking errors that need to be fixed here but its a good start.

toString() of int e = 0000007 omits all zeros. How can I preserve them?

I'm trying to write a program in C# that takes in an int x and decides if it has exactly 7 digits. Right now I'm using x.toString().Length == 7 to check, but I noticed that if the number starts with 0, it automatically gets omitted and I get an incorrect answer (ie the program thinks the input length is less than 7)
Is there a way to fix this? Thanks in advance.
Edit: Sorry I should have mentioned, this was a program to collect and validate the format of ID numbers (so I didn't want something like 0000001 to default to 1) Thanks for the string input suggestion, I think I'm going to try that.
If you want to preserve the input formatting, you must not convert the input to an int. You must store it in a String.
You say your program takes an int. At that point you have already lost. You need to change that interface to accept String inputs.
If you don't care about leading zeros, you're really looking for 7 digits or less. You can check for:
x.toString().Length <= 7
or better:
x < 10000000
Maybe I'm wrong, but to me, 0000001 == 1, and 1 has one digit, not seven. So that's mathematically correct behaviour.
I think you could format it as a string:
int myInt=1;
myInt.ToString("0000000");
prints:
0000001.
so you could do:
if (myInt.ToString("0000000").Length==7)
You can simply write:
int input = 5;
if(input.ToString("0000000").Length == 7)
{
//do your stuff
}
No. It is perfectly valid for a numeric literal to have leading 0s, but a) many languages consider this to be an octal literal, and b) the leading 0s don't actually exist as part of the number. If you need a string then start with a string literal.
You should use string to check length count including 0.
Then I would like to ask "Why do you want to show 0000007? For What?"
You said you're asking for a int, but I suppose you're receiving it as string:
int i = 0;
string number = Console.ReadLine();
if (Int32.TryParse(number, out i))
{
//if (i.ToString().Length == 7) // you can try this too
if (i > 999999 && i < 10000000)
{
Console.WriteLine("Have exactly 7 digits");
}
else
{
Console.WriteLine("Doesn't have exactly 7 digits");
}
}
else
{
Console.WriteLine("Not an Int32 number");
}
This way you try to cast that received number as Int32 and, so, compare its length.
You can let the number be saved as an int with the omitted zeros. but then if you want the number displayed with the zeros then you can use an if statement and a while loop. for example,
Let's assume the values are stored in a numbers array and you need them to be stored as int so you can sort them but displayed as string so you can display with the leading zeros.
int[] numbers = new int[3];
numbers[0] = 001;
numbers[1] = 002;
numbers[2] = 123;
String displayed_Number;
for (int i = 0; i < numbers.Length; i++)
{
displayed_Number = numbers[i].ToString();
if (displayed_Number.Length == 3)
{
listBox.Items.Add(displayed_Number);
}
else if (displayed_Number.Length < 3)
{
while (displayed_Number.Length < 3)
{
displayed_Number = "0" + displayed_Number;
}
listBox.Items.Add(displayed_Number);
}
}
The output is 001 002 123
That way you can maintain the zeros in the numbers when displayed. and they can be stored as int in case you have to store them as int.

Categories

Resources