I've got binary number and I need to:
1) negate all bytes
2) add 1 to negate number
So, I wrote this:
public string u2_number_plus = "1001";
public string u2_number_minus = "";
public string binToU2()
{
int length = u2_number_plus.Length;
int temp = 1;
//negate all bytes
for (int a = 0; a < length; a++)
{
if (u2_number_plus[a] == '1')
u2_number_minus += '0';
else
u2_number_minus += '1';
}
//add 1 to my new (because negate) number
for (int b = length - 1; b >= 0; b--)
{
if (u2_number_minus[b] == 0 && temp == 1)
{
u2_number_minus = u2_number_minus.Replace(u2_number_minus[b], '1');
temp = 0;
}
else if (u2_number_minus[b] == 1 && temp == 1)
{
u2_number_minus = u2_number_minus.Replace(u2_number_minus[b], '0');
temp = 1;
}
else
break;
}
return u2_number_minus;
}
My function binToU2() returns negate but not increment value.
If input data is 1001 I should get 0111, but function returns just 0110. Where I made a mistake?
When you are doing the checking of u2_number_minus[b] you need to compare it against '0' and '1' not the number 0 and 1.
if (u2_number_minus[b] == '0' && temp == 1)
There is also another error, the use of Replace changes all occurrences of the specified character in the string, but we only want to change the one at the specified position. C# does not have replaceAt, but a helper function can be created to do this. See Replacing a char at a given index in string?. I used Jon Skeet's code here:
public static class ReplaceHelper
{
public static string ReplaceAt(this string input, int index, char newChar)
{
if (input == null)
{
throw new ArgumentNullException("input");
}
char[] chars = input.ToCharArray();
chars[index] = newChar;
return new string(chars);
}
}
and change the Replace lines to use ReplaceAt eg
u2_number_minus = u2_number_minus.ReplaceAt(b, '1');
don't really get what you want to do or where you need this for, but anyways, maybe you want to use a BitArray instead of struggling with string manipulation.
BitArray is actually storing bits and gives you basic functionality to negate the array or use other operations...
Let me give you an example:
// define a bit array with length=4 and false as default value for each bit.
var bits = new BitArray(4, false);
bits.Not(); // negate --> all 4 bits are now true.
// your example:
bits = new BitArray(new bool[] { true, false, false, true });
// to inverst/negate it
bits.Not();
// convert to string:
string bitString = string.Empty;
foreach (var bit in bits)
{
bitString += (bool)bit ? "1" : "0";
}
Console.WriteLine(bitString);
// from string:
string longBitString = "01000101001001010100010010010";
var longIntArray = longBitString.ToCharArray().Select(p => p.Equals('0') ? false : true).ToArray();
var longBitArray = new BitArray(longIntArray);
Related
I have a method which gets two string. These strings can contain numbers, ASCII chars or both at the same time.
The algorithm works like this:
Split both strings into char Arrays A and B.
Try to parse element Ai and Bi to an int
Compare element Ai with element Bi, in case of integers use direct comparison, in case of chars use ordinal string comparison.
Do work based on the result
Now, I'm wondering: Do I really need to parse the elements to int? I simply could compare each element in an ordinal string comparison and would get the same result, right?
What are the performance implications here? Is parsing and normal comparison faster than ordinal string comparison? Is it slower?
Is my assumption (using ordinal string comparison instead of parsing and comparing) correct?
Here is the method in question:
internal static int CompareComponentString(this string componentString, string other)
{
bool componentEmpty = string.IsNullOrWhiteSpace(componentString);
bool otherEmtpy = string.IsNullOrWhiteSpace(other);
if (componentEmpty && otherEmtpy)
{
return 0;
}
if (componentEmpty)
{
return -1;
}
if (otherEmtpy)
{
return 1;
}
string[] componentParts = componentString.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
string[] otherParts = other.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < Math.Min(componentParts.Length, otherParts.Length); i++)
{
string componentChar = componentParts[i];
string otherChar = otherParts[i];
int componentNumVal, otherNumVal;
bool componentIsNum = int.TryParse(componentChar, out componentNumVal);
bool otherIsNum = int.TryParse(otherChar, out otherNumVal);
if (componentIsNum && otherIsNum)
{
if (componentNumVal.CompareTo(otherNumVal) == 0)
{
continue;
}
return componentNumVal.CompareTo(otherNumVal);
}
else
{
if (componentIsNum)
{
return -1;
}
if (otherIsNum)
{
return 1;
}
int comp = string.Compare(componentChar, otherChar, StringComparison.OrdinalIgnoreCase);
if (comp != 0)
{
return comp;
}
}
}
return componentParts.Length.CompareTo(otherParts.Length);
}
This are strings that might be used. I might add only the part after the minus sign is used.
1.0.0-alpha
1.0.0-alpha.1
1.0.0-alpha.beta
1.0.0-beta.2
With this method you can create a compare string for each of your string. These strings are comparable by simple alphanumeric comparison.
Assumptions:
There is a minus in the string separating the common part and the indiv part
before the minus is always a substring of three integer values divided by a dot
These integer values are not higher than 999 (look at variable "MaxWidth1")
behind the minus is another substring consisting of several parts, also divided by a dot
The second substring's parts may be numeric or alphanumeric with a max. width of 7 (look at "MaxWidth2")
The second substring consists of max. 5 parts (MaxIndivParts)
Put this method wherever you want:
public string VersionNumberCompareString(string versionNumber, int MaxWidth1=3, int MaxWidth2=7,int MaxIndivParts=5){
string result = null;
int posMinus = versionNumber.IndexOf('-');
string part1 = versionNumber.Substring(0, posMinus);
string part2 = versionNumber.Substring(posMinus+1);
var integerValues=part1.Split('.');
result = integerValues[0].PadLeft(MaxWidth1, '0');
result += integerValues[1].PadLeft(MaxWidth1, '0');
result += integerValues[2].PadLeft(MaxWidth1, '0');
var alphaValues = part2.Split('.');
for (int i = 0; i < MaxIndivParts;i++ ) {
if (i <= alphaValues.GetUpperBound(0)) {
var s = alphaValues[i];
int casted;
if (int.TryParse(s, out casted)) //if int: treat as number
result += casted.ToString().PadLeft(MaxWidth2, '0');
else //treat as string
result += s.PadRight(MaxWidth2, ' ');
}
else
result += new string(' ', MaxWidth2);
}
return result; }
You call it like this:
var s1 = VersionNumberCompareString("1.3.0-alpha.1.12");
//"001003000alpha 00000010000012 "
var s2 = VersionNumberCompareString("0.11.4-beta");
//"000011004beta "
var s3 = VersionNumberCompareString("2.10.11-beta.2");
//"002010011beta 0000002 "
Be aware of the final " sign. All strings are of the same length!
Hope this helps...
that's .net comparison logic for ascii strings -
private unsafe static int CompareOrdinalIgnoreCaseHelper(String strA, String strB)
{
Contract.Requires(strA != null);
Contract.Requires(strB != null);
Contract.EndContractBlock();
int length = Math.Min(strA.Length, strB.Length);
fixed (char* ap = &strA.m_firstChar) fixed (char* bp = &strB.m_firstChar)
{
char* a = ap;
char* b = bp;
while (length != 0)
{
int charA = *a;
int charB = *b;
Contract.Assert((charA | charB) <= 0x7F, "strings have to be ASCII");
// uppercase both chars - notice that we need just one compare per char
if ((uint)(charA - 'a') <= (uint)('z' - 'a')) charA -= 0x20;
if ((uint)(charB - 'a') <= (uint)('z' - 'a')) charB -= 0x20;
//Return the (case-insensitive) difference between them.
if (charA != charB)
return charA - charB;
// Next char
a++; b++;
length--;
}
return strA.Length - strB.Length;
}
}
having said that, Unless you have a strict performance constaint, i would say if you get the same result from an already implemented & tested function, its better to reuse it and not to reinvent the wheel.
It saves so much time in implementation, unit testing, debugging & bug fixing time. & helps keep the software simple.
How can I convert a string to correct GSM encoded value to be sent to a mobile operator?
Below is a port of gnibbler's answer, slightly modified and with a detailed explantation.
Example:
string output = GSMConverter.StringToGSMHexString("Hello World");
// output = "48-65-6C-6C-6F-20-57-6F-72-6C-64"
Implementation:
// Data/info taken from http://en.wikipedia.org/wiki/GSM_03.38
public static class GSMConverter
{
// The index of the character in the string represents the index
// of the character in the respective character set
// Basic Character Set
private const string BASIC_SET =
"#£$¥èéùìòÇ\nØø\rÅåΔ_ΦΓΛΩΠΨΣΘΞ\x1bÆæßÉ !\"#¤%&'()*+,-./0123456789:;<=>?" +
"¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà";
// Basic Character Set Extension
private const string EXTENSION_SET =
"````````````````````^```````````````````{}`````\\````````````[~]`" +
"|````````````````````````````````````€``````````````````````````";
// If the character is in the extension set, it must be preceded
// with an 'ESC' character whose index is '27' in the Basic Character Set
private const int ESC_INDEX = 27;
public static string StringToGSMHexString(string text, bool delimitWithDash = true)
{
// Replace \r\n with \r to reduce character count
text = text.Replace(Environment.NewLine, "\r");
// Use this list to store the index of the character in
// the basic/extension character sets
var indicies = new List<int>();
foreach (var c in text)
{
int index = BASIC_SET.IndexOf(c);
if(index != -1) {
indicies.Add(index);
continue;
}
index = EXTENSION_SET.IndexOf(c);
if(index != -1) {
// Add the 'ESC' character index before adding
// the extension character index
indicies.Add(ESC_INDEX);
indicies.Add(index);
continue;
}
}
// Convert indicies to 2-digit hex
var hex = indicies.Select(i => i.ToString("X2")).ToArray();
string delimiter = delimitWithDash ? "-" : "";
// Delimit output
string delimited = string.Join(delimiter, hex);
return delimited;
}
}
The code of Omar Didn't work for me. But I found The actual code that works:
public static string Encode7bit(string s)
{
string empty = string.Empty;
for (int index = s.Length - 1; index >= 0; --index)
empty += Convert.ToString((byte)s[index], 2).PadLeft(8, '0').Substring(1);
string str1 = empty.PadLeft((int)Math.Ceiling((Decimal)empty.Length / new Decimal(8)) * 8, '0');
List<byte> byteList = new List<byte>();
while (str1 != string.Empty)
{
string str2 = str1.Substring(0, str1.Length > 7 ? 8 : str1.Length).PadRight(8, '0');
str1 = str1.Length > 7 ? str1.Substring(8) : string.Empty;
byteList.Add(Convert.ToByte(str2, 2));
}
byteList.Reverse();
var messageBytes = byteList.ToArray();
var encodedData = "";
foreach (byte b in messageBytes)
{
encodedData += Convert.ToString(b, 16).PadLeft(2, '0');
}
return encodedData.ToUpper();
}
I'm working through an algorithm problem set which poses the following question:
"Determine if a string has all unique characters. Assume you can only use arrays".
I have a working solution, but I would like to see if there is anything better optimized in terms of time complexity. I do not want to use LINQ. Appreciate any help you can provide!
static void Main(string[] args)
{
FindDupes("crocodile");
}
static string FindDupes(string text)
{
if (text.Length == 0 || text.Length > 256)
{
Console.WriteLine("String is either empty or too long");
}
char[] str = new char[text.Length];
char[] output = new char[text.Length];
int strLength = 0;
int outputLength = 0;
foreach (char value in text)
{
bool dupe = false;
for (int i = 0; i < strLength; i++)
{
if (value == str[i])
{
dupe = true;
break;
}
}
if (!dupe)
{
str[strLength] = value;
strLength++;
output[outputLength] = value;
outputLength++;
}
}
return new string(output, 0, outputLength);
}
If time complexity is all you care about you could map the characters to int values, then have an array of bool values which remember if you've seen a particular character value previously.
Something like ... [not tested]
bool[] array = new bool[256]; // or larger for Unicode
foreach (char value in text)
if (array[(int)value])
return false;
else
array[(int)value] = true;
return true;
try this,
string RemoveDuplicateChars(string key)
{
string table = string.Empty;
string result = string.Empty;
foreach (char value in key)
{
if (table.IndexOf(value) == -1)
{
table += value;
result += value;
}
}
return result;
}
usage
Console.WriteLine(RemoveDuplicateChars("hello"));
Console.WriteLine(RemoveDuplicateChars("helo"));
Console.WriteLine(RemoveDuplicateChars("Crocodile"));
output
helo
helo
Crocdile
public boolean ifUnique(String toCheck){
String str="";
for(int i=0;i<toCheck.length();i++)
{
if(str.contains(""+toCheck.charAt(i)))
return false;
str+=toCheck.charAt(i);
}
return true;
}
EDIT:
You may also consider to omit the boundary case where toCheck is an empty string.
The following code works:
static void Main(string[] args)
{
isUniqueChart("text");
Console.ReadKey();
}
static Boolean isUniqueChart(string text)
{
if (text.Length == 0 || text.Length > 256)
{
Console.WriteLine(" The text is empty or too larg");
return false;
}
Boolean[] char_set = new Boolean[256];
for (int i = 0; i < text.Length; i++)
{
int val = text[i];//already found this char in the string
if (char_set[val])
{
Console.WriteLine(" The text is not unique");
return false;
}
char_set[val] = true;
}
Console.WriteLine(" The text is unique");
return true;
}
If the string has only lower case letters (a-z) or only upper case letters (A-Z) you can use a very optimized O(1) solution.Also O(1) space.
c++ code :
bool checkUnique(string s){
if(s.size() >26)
return false;
int unique=0;
for (int i = 0; i < s.size(); ++i) {
int j= s[i]-'a';
if(unique & (1<<j)>0)
return false;
unique=unique|(1<<j);
}
return true;
}
Remove Duplicates in entire Unicode Range
Not all characters can be represented by a single C# char. If you need to take into account combining characters and extended unicode characters, you need to:
parse the characters using StringInfo
normalize the characters
find duplicates amongst the normalized strings
Code to remove duplicate characters:
We keep track of the entropy, storing the normalized characters (each character is a string, because many characters require more than 1 C# char). In case a character (normalized) is not yet stored in the entropy, we append the character (in specified form) to the output.
public static class StringExtension
{
public static string RemoveDuplicateChars(this string text)
{
var output = new StringBuilder();
var entropy = new HashSet<string>();
var iterator = StringInfo.GetTextElementEnumerator(text);
while (iterator.MoveNext())
{
var character = iterator.GetTextElement();
if (entropy.Add(character.Normalize()))
{
output.Append(character);
}
}
return output.ToString();
}
}
Unit Test:
Let's test a string that contains variations on the letter A, including the Angstrom sign Å. The Angstrom sign has unicode codepoint u212B, but can also be constructed as the letter A with the diacritic u030A. Both represent the same character.
// ÅÅAaA
var input = "\u212BA\u030AAaA";
// ÅAa
var output = input.RemoveDuplicateChars();
Further extensions could allow for a selector function that determines how to normalize characters. For instance the selector (x) => x.ToUpperInvariant().Normalize() would allow for case-insensitive duplicate removal.
public static bool CheckUnique(string str)
{
int accumulator = 0;
foreach (int asciiCode in str)
{
int shiftedBit = 1 << (asciiCode - ' ');
if ((accumulator & shiftedBit) > 0)
return false;
accumulator |= shiftedBit;
}
return true;
}
The question is complicated but I will explain it in details.
The goal is to make a function which will return next "step" of the given string.
For example
String.Step("a"); // = "b"
String.Step("b"); // = "c"
String.Step("g"); // = "h"
String.Step("z"); // = "A"
String.Step("A"); // = "B"
String.Step("B"); // = "C"
String.Step("G"); // = "H"
Until here its quite easy, But taking in mind that input IS string it can contain more than 1 characters and the function must behave like this.
String.Step("Z"); // = "aa";
String.Step("aa"); // = "ab";
String.Step("ag"); // = "ah";
String.Step("az"); // = "aA";
String.Step("aA"); // = "aB";
String.Step("aZ"); // = "ba";
String.Step("ZZ"); // = "aaa";
and so on...
This doesn't exactly need to extend the base String class.
I tried to work it out by each characters ASCII values but got stuck with strings containing 2 characters.
I would really appreciate if someone can provide full code of the function.
Thanks in advance.
EDIT
*I'm sorry I forgot to mention earlier that the function "reparse" the self generated string when its length reaches n.
continuation of this function will be smth like this. for example n = 3
String.Step("aaa"); // = "aab";
String.Step("aaZ"); // = "aba";
String.Step("aba"); // = "abb";
String.Step("abb"); // = "abc";
String.Step("abZ"); // = "aca";
.....
String.Step("zzZ"); // = "zAa";
String.Step("zAa"); // = "zAb";
........
I'm sorry I didn't mention it earlier, after reading some answers I realised that the problem was in question.
Without this the function will always produce character "a" n times after the end of the step.
NOTE: This answer is incorrect, as "aa" should follow after "Z"... (see comments below)
Here is an algorithm that might work:
each "string" represents a number to a given base (here: twice the count of letters in the alphabet).
The next step can thus be computed by parsing the "number"-string back into a int, adding 1 and then formatting it back to the base.
Example:
"a" == 1 -> step("a") == step(1) == 1 + 1 == 2 == "b"
Now your problem is reduced to parsing the string as a number to a given base and reformatting it. A quick googling suggests this page: http://everything2.com/title/convert+any+number+to+decimal
How to implement this?
a lookup table for letters to their corresponding number: a=1, b=2, c=3, ... Y = ?, Z = 0
to parse a string to number, read the characters in reverse order, looking up the numbers and adding them up:
"ab" -> 2*BASE^0 + 1*BASE^1
with BASE being the number of "digits" (2 count of letters in alphabet, is that 48?)
EDIT: This link looks even more promising: http://www.citidel.org/bitstream/10117/20/12/convexp.html
Quite collection of approaches, here is mine:-
The Function:
private static string IncrementString(string s)
{
byte[] vals = System.Text.Encoding.ASCII.GetBytes(s);
for (var i = vals.Length - 1; i >= 0; i--)
{
if (vals[i] < 90)
{
vals[i] += 1;
break;
}
if (vals[i] == 90)
{
if (i != 0)
{
vals[i] = 97;
continue;
}
else
{
return new String('a', vals.Length + 1);
}
}
if (vals[i] < 122)
{
vals[i] += 1;
break;
}
vals[i] = 65;
break;
}
return System.Text.Encoding.ASCII.GetString(vals);
}
The Tests
Console.WriteLine(IncrementString("a") == "b");
Console.WriteLine(IncrementString("z") == "A");
Console.WriteLine(IncrementString("Z") == "aa");
Console.WriteLine(IncrementString("aa") == "ab");
Console.WriteLine(IncrementString("az") == "aA");
Console.WriteLine(IncrementString("aZ") == "ba");
Console.WriteLine(IncrementString("zZ") == "Aa");
Console.WriteLine(IncrementString("Za") == "Zb");
Console.WriteLine(IncrementString("ZZ") == "aaa");
public static class StringStep
{
public static string Next(string str)
{
string result = String.Empty;
int index = str.Length - 1;
bool carry;
do
{
result = Increment(str[index--], out carry) + result;
}
while (carry && index >= 0);
if (index >= 0) result = str.Substring(0, index+1) + result;
if (carry) result = "a" + result;
return result;
}
private static char Increment(char value, out bool carry)
{
carry = false;
if (value >= 'a' && value < 'z' || value >= 'A' && value < 'Z')
{
return (char)((int)value + 1);
}
if (value == 'z') return 'A';
if (value == 'Z')
{
carry = true;
return 'a';
}
throw new Exception(String.Format("Invalid character value: {0}", value));
}
}
Split the input string into columns and process each, right-to-left, like you would if it was basic arithmetic. Apply whatever code you've got that works with a single column to each column. When you get a Z, you 'increment' the next-left column using the same algorithm. If there's no next-left column, stick in an 'a'.
I'm sorry the question is stated partly.
I edited the question so that it meets the requirements, without the edit the function would end up with a n times by step by step increasing each word from lowercase a to uppercase z without "re-parsing" it.
Please consider re-reading the question, including the edited part
This is what I came up with. I'm not relying on ASCII int conversion, and am rather using an array of characters. This should do precisely what you're looking for.
public static string Step(this string s)
{
char[] stepChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
char[] str = s.ToCharArray();
int idx = s.Length - 1;
char lastChar = str[idx];
for (int i=0; i<stepChars.Length; i++)
{
if (stepChars[i] == lastChar)
{
if (i == stepChars.Length - 1)
{
str[idx] = stepChars[0];
if (str.Length > 1)
{
string tmp = Step(new string(str.Take(str.Length - 1).ToArray()));
str = (tmp + str[idx]).ToCharArray();
}
else
str = new char[] { stepChars[0], str[idx] };
}
else
str[idx] = stepChars[i + 1];
break;
}
}
return new string(str);
}
This is a special case of a numeral system. It has the base of 52. If you write some parser and output logic you can do any kind of arithmetics an obviously the +1 (++) here.
The digits are "a"-"z" and "A" to "Z" where "a" is zero and "Z" is 51
So you have to write a parser who takes the string and builds an int or long from it. This function is called StringToInt() and is implemented straight forward (transform char to number (0..51) multiply with 52 and take the next char)
And you need the reverse function IntToString which is also implementet straight forward (modulo the int with 52 and transform result to digit, divide the int by 52 and repeat this until int is null)
With this functions you can do stuff like this:
IntToString( StringToInt("ZZ") +1 ) // Will be "aaa"
You need to account for A) the fact that capital letters have a lower decimal value in the Ascii table than lower case ones. B) The table is not continuous A-Z-a-z - there are characters inbetween Z and a.
public static string stepChar(string str)
{
return stepChar(str, str.Length - 1);
}
public static string stepChar(string str, int charPos)
{
return stepChar(Encoding.ASCII.GetBytes(str), charPos);
}
public static string stepChar(byte[] strBytes, int charPos)
{
//Escape case
if (charPos < 0)
{
//just prepend with a and return
return "a" + Encoding.ASCII.GetString(strBytes);
}
else
{
strBytes[charPos]++;
if (strBytes[charPos] == 91)
{
//Z -> a plus increment previous char
strBytes[charPos] = 97;
return stepChar(strBytes, charPos - 1); }
else
{
if (strBytes[charPos] == 123)
{
//z -> A
strBytes[charPos] = 65;
}
return Encoding.ASCII.GetString(strBytes);
}
}
}
You'll probably want some checking in place to ensure that the input string only contains chars A-Za-z
Edit Tidied up code and added new overload to remove redundant byte[] -> string -> byte[] conversion
Proof http://geekcubed.org/random/strIncr.png
This is a lot like how Excel columns would work if they were unbounded. You could change 52 to reference chars.Length for easier modification.
static class AlphaInt {
private static string chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static string StepNext(string input) {
return IntToAlpha(AlphaToInt(input) + 1);
}
public static string IntToAlpha(int num) {
if(num-- <= 0) return "a";
if(num % 52 == num) return chars.Substring(num, 1);
return IntToAlpha(num / 52) + IntToAlpha(num % 52 + 1);
}
public static int AlphaToInt(string str) {
int num = 0;
for(int i = 0; i < str.Length; i++) {
num += (chars.IndexOf(str.Substring(i, 1)) + 1)
* (int)Math.Pow(52, str.Length - i - 1);
}
return num;
}
}
LetterToNum should be be a Function that maps "a" to 0 and "Z" to 51.
NumToLetter the inverse.
long x = "aazeiZa".Aggregate((x,y) => (x*52) + LetterToNum(y)) + 1;
string s = "";
do { // assertion: x > 0
var c = x % 52;
s = NumToLetter() + s;
x = (x - c) / 52;
} while (x > 0)
// s now should contain the result
I'm trying to get a line of integers from a text file and parse them into separate variables. The text file is set up like this:
ID:HP:MP:STR:WIS:SPD:GOLD:XP
0:100:50:10:5:12:5:10
I want to split them with the : symbol in between each. One of the problems I'm having with this is being able to read the file line by line as strings, parsing them, and then storing the parsed strings as ints. Here is the code I'm attempting to use so far:
class monster
{
string line;
string[] mstats;
string[] mname;
char[] delimeterChars = {':'};
int id;
int i = -1;
int j = 0;
int hp;
int mp;
int str;
int wis;
int spd;
int gold;
int xp;
public monster(int id)
{
StreamReader stats = new StreamReader("monsterStats.txt");
while(i != id)
{
i++;
line = stats.ReadLine();
mstats = line.Split(delimeterChars);
j = 0;
foreach(string s in mstats)
{
if (j == 0) id = int.Parse(s);
else if (j == 1) hp = int.Parse(s);
else if (j == 2) mp = int.Parse(s);
else if (j == 3) str = int.Parse(s);
else if (j == 4) wis = int.Parse(s);
else if (j == 5) spd = int.Parse(s);
else if (j == 6) gold = int.Parse(s);
else if (j == 7) xp = int.Parse(s);
j++;
}
}
curHp = hp;
curMp = mp;
curSpd = spd;
curStr = str;
curWis = wis;
}
}
I get the following error when this code runs:
Input string was not in a correct format.
It references this part of the code:
if (j == 0) id = int.Parse(s);
Well, the first thing is to find out what the bad input was.
If you're expecting bad input data, use int.TryParse instead of just int.Parse. If you're not expecting bad input data, the fact that it's throwing an exception is probably appropriate - but you should examine your data to find out what's wrong.
I'd also recommend putting the parsing call once rather than in every case. It's not like you're doing a different kind of parsing for each field.
Why the foreach? How about:
id = int.Parse(mstats[0]);
hp = int.Parse(mstats[1]);
and so on. With a check beforehand that mstats is long enough.
A bit of Linq would let you get an array of integers in one shot:
int[] fields = line.Split(delimeterChars).Select(s => int.Parse(s)).ToArray();
id = field[0];
hp = field[2];
As for getting the code working, try printing out the line of text, and each piece of text just before you pass it to Parse. If it's not an integer, that's your problem.
A very good way for parsing text input are always regular expressions.
Regex r = new Regex(#"(?<id>\d+):(?<hp>\d+):(?<mp>\d+):(?<str>\d+):(?<wis>\d+):(?<spd>\d+):(?<gold>\d+):(?<xp>\d+)");
// loop over lines
Monster m = new Monster();
Match mc = r.Match(input);
m.hp = GetValue(mc.Groups["hp"], m.hp);
m.mp = GetValue(mc.Groups["mp"], m.mp);
m.str = GetValue(mc.Groups["str"], m.str);
...
// method to handle extracted value
private static int GetValue(Group g, int fallback)
{
if (g == null) throw new ArgumentNullException("g");
return g.Success ? Convert.ToInt32(g.Value) : fallback;
}
The method GetValue checks the extracted value. If the match failed (perhaps "" or "AB" instead of a number - g.Success is false) you can handle it the way you want. In my way i simply use an fallback value.
http://msdn.microsoft.com/en-us/library/hs600312.aspx