Reading and parsing integers from a text file - c#

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

Related

Calculate number of zeros in a string

I have a string as follow 51200000000000000000000000000000
This string is not fixed. It will be appended depends on the number of boards. If there are two boards, the string will be as follow 5120000000000000000000000000000052200000000000000000000000000000
I would like to know how to calculate the number of zeros in the string.
I'm using the following code but it is not flexible if there are more than two boards.
string str = "51200000000000000000000000000000";
string zeros = "00000000000000000000000000000";
if (str.Contains(zeros))
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
You can use the following piece of code to do this, which will give you the number of zeros(Example).
char matchChar='0';
string strInput = "51200000000000000000000000000000";
int zeroCount = strInput.Count(x => x == matchChar); // will be 29
You can do the same by iterating through each characters and check whether it is the required character(say 0) then take its count.
Use a simple foreach loop to traverse the string and count:
int CountZeroes(string str)
{
// TODO: error checking, etc.
int count = 0;
foreach (var character in str)
{
if (character == '0') count++;
}
return count;
}
a little advanced (or so) technique would be to convert the string to char array then to list of chars then using LINQ
string str = "51200000000000000000000000000000";
List<char> nums = str.ToCharArray().ToList();
Console.WriteLine(nums.Where(x => x.Equals('0')).Select(x => x.ToString()).Count());
i just placed this here in case you want to learn not just a single approach :)
It can also do with a for loop and Substring.
Code
string str = "51200000000000000000000000000000";
int n = 0;
for (int i = 0; i < str.Length; i++)
{
if (str.Substring(i, 1) == "0")
n += 1;
}
Console.WriteLine("Count : " + n.ToString());
Working fiddle demo
Code:
string st;
st = textBox1.Text;
int countch = 0, i;
for (i = 0; i < st.Length; i++)
if (st[i]=='0') countch++;
MessageBox.Show(countch.ToString());
using System.Linq
int count0s = str.Count(z => z == '0');
will return how many 0's in your str string

validating string with wildcards or placeholders for up to 2 missing chars

I'm working on a business layer query to validate an entry against a list of valid entries in a database. I am trying to validate with up to 2 wild card placeholders. For example, XXXX_XX, with _ being the wildcard. To validate the instance where there is only a single _ I'm using the following code:
if (DiagCode.Contains("_"))
{
int count=0;
foreach (char c in DiagCode)
{
if(c.ToString() =="_")
count += 1;
}
if (count == 1)
{
int loc = DiagCode.IndexOf("_");
int len = DiagCode.Length;
int end = len - loc;
string diagCodeSubBegin = DiagCode.Substring(0, loc);
string diagCodeSubEnd = DiagCode.Substring(loc, end);
NHCSLINQ.MODEL.DIAGS_ICD10 tblDIAG_ICD102 = (from c in NHCSDB.DIAGS_ICD10s
where c.Code.StartsWith(diagCodeSubBegin)
&& c.Code.EndsWith(diagCodeSubEnd)
select c).SingleOrDefault();
if (tblDIAG_ICD102 != null)
{
return true;
}
}
The problem arises when I have an entry as such, XX_XX_X. I've thought about using Regex to split the code an validate each piece individually as seen below.
if (count ==2)
{
string[] lines = Regex.Split(DiagCode, "_");
foreach (string line in lines)
{
int loc = DiagCode.IndexOf("_");
int len = DiagCode.Length;
int end = len - loc;
string diagCodeSubBegin = DiagCode.Substring(0, loc);
string diagCodeSubEnd = DiagCode.Substring(loc, end);
NHCSLINQ.MODEL.DIAGS_ICD10 tblDIAG_ICD102 = (from c in NHCSDB.DIAGS_ICD10s
where c.Code.StartsWith(diagCodeSubBegin)
&& c.Code.EndsWith(diagCodeSubEnd)
select c).SingleOrDefault();
if (tblDIAG_ICD102 != null)
{
return true;
}
}
}
But then I'm at a loss to reconnect the individual strings as a whole with the _ in place. There must be an easier way to accomplish this?
Why are you trying to reinvent the wheel?
TSQL Like has that and ironically they use _.

Negate and increment value in binary system

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);

Format string with dashes

I have a compressed string value I'm extracting from an import file. I need to format this into a parcel number, which is formatted as follows: ##-##-##-###-###. So therefore, the string "410151000640" should become "41-01-51-000-640". I can do this with the following code:
String.Format("{0:##-##-##-###-###}", Convert.ToInt64("410151000640"));
However, The string may not be all numbers; it could have a letter or two in there, and thus the conversion to the int will fail. Is there a way to do this on a string so every character, regardless of if it is a number or letter, will fit into the format correctly?
Regex.Replace("410151000640", #"^(.{2})(.{2})(.{2})(.{3})(.{3})$", "$1-$2-$3-$4-$5");
Or the slightly shorter version
Regex.Replace("410151000640", #"^(..)(..)(..)(...)(...)$", "$1-$2-$3-$4-$5");
I would approach this by having your own formatting method, as long as you know that the "Parcel Number" always conforms to a specific rule.
public static string FormatParcelNumber(string input)
{
if(input.length != 12)
throw new FormatException("Invalid parcel number. Must be 12 characters");
return String.Format("{0}-{1}-{2}-{3}-{4}",
input.Substring(0,2),
input.Substring(2,2),
input.Substring(4,2),
input.Substring(6,3),
input.Substring(9,3));
}
This should work in your case:
string value = "410151000640";
for( int i = 2; i < value.Length; i+=3){
value = value.Insert( i, "-");
}
Now value contains the string with dashes inserted.
EDIT
I just now saw that you didn't have dashes between every second number all the way, to this will require a small tweak (and makes it a bit more clumsy also I'm afraid)
string value = "410151000640";
for( int i = 2; i < value.Length-1; i+=3){
if( value.Count( c => c == '-') >= 3) i++;
value = value.Insert( i, "-");
}
If its part of UI you can use MaskedTextProvider in System.ComponentModel
MaskedTextProvider prov = new MaskedTextProvider("aa-aa-aa-aaa-aaa");
prov.Set("41x151000a40");
string result = prov.ToDisplayString();
Here is a simple extension method with some utility:
public static string WithMask(this string s, string mask)
{
var slen = Math.Min(s.Length, mask.Length);
var charArray = new char[mask.Length];
var sPos = s.Length - 1;
for (var i = mask.Length - 1; i >= 0 && sPos >= 0;)
if (mask[i] == '#') charArray[i--] = s[sPos--];
else
charArray[i] = mask[i--];
return new string(charArray);
}
Use it as follows:
var s = "276000017812008";
var mask = "###-##-##-##-###-###";
var dashedS = s.WithMask(mask);
You can use it with any string and any character other than # in the mask will be inserted. The mask will work from right to left. You can tweak it to go the other way if you want.
Have fun.
If i understodd you correctly youre looking for a function that removes all letters from a string, aren't you?
I have created this on the fly, maybe you can convert it into c# if it's what you're looking for:
Dim str As String = "410151000vb640"
str = String.Format("{0:##-##-##-###-###}", Convert.ToInt64(MakeNumber(str)))
Public Function MakeNumber(ByVal stringInt As String) As String
Dim sb As New System.Text.StringBuilder
For i As Int32 = 0 To stringInt.Length - 1
If Char.IsDigit(stringInt(i)) Then
sb.Append(stringInt(i))
End If
Next
Return sb.ToString
End Function

How to make next step of a string. C#

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

Categories

Resources