This function need to check that a=b=c=0 or only one is <>0
there is a best way to do this check?
void (int p, int a, int s) {
if ((p != 0 && a != 0)
|| (p != 0 && s != 0)
|| (a != 0 && s != 0)
|| (a != 0 && s != 0 && p != 0)) throw new Exception("Error please set A or P or S");
}
A simple solution, without using Linq or any of the overhead that comes with it:
public void Check(int p, int a, int s)
{
var count = 0;
if (p != 0) count++;
if (a != 0) count++;
if (s != 0) count++;
if (count >= 2)
Console.WriteLine("Please set only A or P or S or none");
else
Console.WriteLine("OK");
}
Working Fiddle: https://dotnetfiddle.net/ViMmRV
This function need to check that a=b=c=0 or only one <> 0
If I correctly understand that:
private void CheckValues(int p, int a, int s)
{
var values = new[] { p, a, s };
if (values.Sum() == 0 || values.Count(v => v != 0) == 1)
Console.WriteLine("Error");
else
Console.WriteLine("OK");
}
Or
private void CheckValues(params int[] values)
{
if (values.Sum() == 0 || values.Count(v => v != 0) == 1)
Console.WriteLine("Error");
else
Console.WriteLine("OK");
}
Thus:
CheckValues(0, 0, 0); // <- Error
CheckValues(0, 0, 1); // <- Error
CheckValues(0, 1, 2); // <- OK
CheckValues(1, 2, 3); // <- OK
void Method(int p, int a, int s)
{
if (new [] { p, a, s }.Count(i => i == 0) <= 1)
{
throw new Exception("Error please set A or P or S");
}
}
Related
I am trying to make a string calculator, it works fine with two numbers but I always encounter a problem when evaluating multiple operations:
7*2+4=
Also can you help me with my multiplication and division code. I don't understand why it prints 0 even with just two numbers(7*5)
using System;
using System.Text.RegularExpressions;
namespace Sariling_Calcu
{
class Program
{
private static string exp;
private static int[] i = new int[1000];
private static char[] oper = new char[10];
private static int cntr2;
private static int result;
private static int pluscount;
private static int subcount;
private static int mulcount;
private static int divcount;
static void getNum()
{
string[] strNum = (Regex.Split(exp, #"\D+"));
for (int cntr = 0; cntr < exp.Length; cntr++)
{
foreach (string item in strNum)
{
if (!string.IsNullOrEmpty(item))
{
i[cntr] = int.Parse(item);
cntr += 1;
}
}
}
}
static void counter()
{
for (int cntr = 0; cntr < exp.Length; cntr++)
{
if (exp[cntr] == '+')
{
pluscount++;
}
else if (exp[cntr] == '-')
{
subcount++;
}
else if (exp[cntr] == '*')
{
mulcount++;
}
else if (exp[cntr] == '/')
{
divcount--;
}
}
}
static void oprtr()
{
for (int cntr = 0; cntr < exp.Length; cntr++)
{
if (exp[cntr] != '1'
&& exp[cntr] != '2'
&& exp[cntr] != '3'
&& exp[cntr] != '4'
&& exp[cntr] != '5'
&& exp[cntr] != '6'
&& exp[cntr] != '7'
&& exp[cntr] != '8'
&& exp[cntr] != '9')
{
if (exp[cntr] == '+')
{
result += i[cntr2];
cntr2 += 1;
pluscount--;
if (pluscount == 0 && subcount == 0 && mulcount==0 && divcount==0)
{
cntr2 += 3;
result += i[cntr2];
}
}
else if (exp[cntr] == '-')
{
result -= i[cntr2];
cntr2 += 1;
subcount--;
result = -result;
if (pluscount == 0 && subcount == 0 && mulcount == 0 && divcount == 0)
{
cntr2 += 3;
result -= i[cntr2];
}
}
else if (exp[cntr] == '*')
{
if (result == 0)
{
result += 1;
}
result *= i[cntr2];
cntr2 += 1;
mulcount--;
if (pluscount == 0 && subcount == 0 && mulcount == 0 && divcount == 0)
{
cntr2 += 3;
result *= i[cntr2];
}
}
else if (exp[cntr] == '/')
{
if (result == 0)
{
result += 1;
}
result /= i[cntr2];
cntr2 += 1;
divcount--;
if (pluscount == 0 && subcount == 0 && mulcount == 0 && divcount == 0)
{
cntr2 += 3;
result /= i[cntr2];
}
}
}
}
}
static void Main(string[] args)
{
Console.Write("Expression: ");
exp = Console.ReadLine();
counter();
getNum();
oprtr();
Console.Write("Answer: \n" + result);
Console.ReadLine();
}
}
}
you could use LINQ to reduce your code to a few lines but looks like its a school assignment where you would have to go with Arrays and loops.
I have tried to refine your code a bit and did a few fixes, change getNum(), counter() and oprtr() methods as below and let me know if it works, then I would add some comments in the code to explain changes I made.
static void getNum()
{
string[] strNum = (Regex.Split(exp, #"\D+"));
for (int cntr = 0; cntr < strNum.Length; cntr++)
{
if (!string.IsNullOrEmpty(strNum[cntr]))
{
i[cntr] = int.Parse(strNum[cntr]);
}
}
}
static void counter()
{
cntr2 = 0;
for (int cntr = 0; cntr < exp.Length; cntr++)
{
if (exp[cntr] == '+')
{
oper[cntr2] = '+';
cntr2++;
}
else if (exp[cntr] == '-')
{
oper[cntr2] = '-';
cntr2++;
}
else if (exp[cntr] == '*')
{
oper[cntr2] = '*';
cntr2++;
}
else if (exp[cntr] == '/')
{
oper[cntr2] = '/';
cntr2++;
}
}
}
static void oprtr()
{
result = i[0];
cntr2 = 1;
for (int cntr = 0; cntr < oper.Length; cntr++)
{
if (oper[cntr] == '+')
{
result += i[cntr2];
}
else if (oper[cntr] == '-')
{
result -= i[cntr2];
}
else if (oper[cntr] == '*')
{
result *= i[cntr2];
}
else if (oper[cntr] == '/')
{
if (i[cntr2] == 0)
{
throw new DivideByZeroException();
}
result /= i[cntr2];
}
cntr2 += 1;
}
}
My initial code is 'A0AA' and I need a code/function in C# that will increment it until it goes to 'Z9ZZ'.
for example.
first code is 'D9ZZ'
the next code should be 'E0AA'
sorry maybe my example is quite confusing.. here's another example.. thanks.
first code is 'D9AZ'
the next code should be 'D9BA'
string start = "A9ZZ";
int add = 1;
string next = String.Concat(start.Reverse().Select((x,i) =>
{
char first = i == 2 ? '0' : 'A';
char last = i == 2 ? '9' : 'Z';
if ((x += (char)add) > last)
{
return first;
}
else
{
add = 0;
return x;
}
})
.Reverse());
This should fix it.
private static IEnumerable<string> Increment(string value)
{
if (value.Length != 4)
throw new ArgumentException();
char[] next = value.ToCharArray();
while (new string(next) != "Z9ZZ")
{
next[3]++;
if (next[3] > 'Z')
{
next[3] = 'A';
next[2]++;
}
if (next[2] > 'Z')
{
next[2] = 'A';
next[1]++;
}
if (next[1] > '9')
{
next[1] = '0';
next[0]++;
}
yield return new string(next);
}
}
Example of calling this code:
IList<string> values = Increment("A0AA").Take(100).ToList();
foreach (var value in values)
{
Console.Write(value + " ");
}
Here's a pretty clean solution that checks every character starting at the end:
public SomeMethod()
{
var next = Increment("A2CZ"); // A2DZ
}
public string Increment(string code)
{
var arr = code.ToCharArray();
for (var i = arr.Length - 1; i >= 0; i--)
{
var c = arr[i];
if (c == 90 || c == 57)
continue;
arr[i]++;
return new string(arr);
}
return code;
}
I have method for comparing values like this:
protected bool CompareValues(string a="", int b=0, string c="", int d=0, string e="", int f=0)
{
int counter = 0;
if(int.Parse(a) > b)
{
counter++;
}
if(int.Parse(c) > d)
{
counter++;
}
if(counter > 1)
{
counter = 1;
}
if(int.Parse(e) > f)
{
counter++;
}
if(counter > 1)
{
return true;
}
else
{
return false;
}
}
It works fine for me, but I am can't stand without thinking about some improvement if possible. Any suggestion would be appreciate.
If you need to perform n-many comparisons of the form
(int.Parse(a1) > b1 || int.Parse(a2) > b2 || ... || int.Parse(aK) > bK) && int.Parse(aN) > bN
You can make a method that just accepts a set of pairs of values to compare
protected bool CompareValues(params Tuple<string, int>[] comparisons)
{
if(ReferenceEquals(comparisons, null))
{
throw new ArgumentNullException("comparisons");
}
if(comparisons.Length < 1)
{
throw new ArgumentException("At least one pair to compare must be specified");
}
var atLeastOneComparisonSucceeded = comparisons.Length == 1;
for(var i = 0; !atLeastOneComparisonSucceeded && i < comparisons.Length - 1; ++i)
{
atLeastOneComparisonSucceeded = int.Parse(comparisons[i].Item1) > comparisons[i].Item2;
}
var lastIndex = comparisons.Length - 1;
return atLeastOneComparisonSucceeded && int.Parse(comparisons[lastIndex].Item1) > comparisons[lastIndex].Item2;
}
Usage:
var result = CompareValues(new Tuple<string, int>("5", 2),
new Tuple<string, int>("3", 1),
new Tuple<string, int>("1", 2));
If you only ever need 3 pairs of values (as in your original post), you could provide overloads for the method that provided appropriate default values, like this
protected static bool CompareValues(string a, int b)
{
return CompareValues(a, b, "1", 0);
}
protected static bool CompareValues(string a, int b, string c, int d)
{
return CompareValues(a, b, c, d, "1", 0);
}
protected static bool CompareValues(string a, int b, string c, int d, string e, int f)
{
return ((int.Parse(a) > b || int.Parse(c) > d) && int.Parse(e) > f);
}
Of course, the arguments passed down from the overloads must be chosen such that the semantics are appropriate.
I am not sure in the middle why you reset counter to 1, however this is what I understood
if((int.Parse(a) > b || int.Parse(c) > d) && int.Parse(e) > f)
{
return true;
}
else
{
return false;
}
It looks like you want:
return (int.Parse(a) > b || int.Parse(c) > d) && int.Parse(e) > f;
I'm not sure but is this what you are trying to do?
return ( ( int.Parse(a) > b || int.Parse(c) > d ) && int.Parse(e) > f);
i have code that its working but its not as i would like.
in runtime i create many new textbox/labels so i had to use 10 ifs to check each "future" textbox have textlength = 0 and != null
i would like to use repeat structure like for or while, dont know if its possible.
For example, if i create more textbox/labels will be impossible have really big code.
See my code:
private void cadeiaapagarcampos(TextBox _text, EventArgs e)
{
if (_text.Text == "")
{
Label lblAcessorio2 = (Label)gpbCategoria.Controls.Find("lblAcessorio2", false).FirstOrDefault();
TextBox txtAcessorio2 = (TextBox)gpbCategoria.Controls.Find("txtAcessorio2", false).FirstOrDefault();
Label lblAcessorio3 = (Label)gpbCategoria.Controls.Find("lblAcessorio3", false).FirstOrDefault();
TextBox txtAcessorio3 = (TextBox)gpbCategoria.Controls.Find("txtAcessorio3", false).FirstOrDefault();
Label lblAcessorio4 = (Label)gpbCategoria.Controls.Find("lblAcessorio4", false).FirstOrDefault();
TextBox txtAcessorio4 = (TextBox)gpbCategoria.Controls.Find("txtAcessorio4", false).FirstOrDefault();
Label lblAcessorio5 = (Label)gpbCategoria.Controls.Find("lblAcessorio5", false).FirstOrDefault();
TextBox txtAcessorio5 = (TextBox)gpbCategoria.Controls.Find("txtAcessorio5", false).FirstOrDefault();
Label lblAcessorio6 = (Label)gpbCategoria.Controls.Find("lblAcessorio6", false).FirstOrDefault();
TextBox txtAcessorio6 = (TextBox)gpbCategoria.Controls.Find("txtAcessorio6", false).FirstOrDefault();
Label lblAcessorio7 = (Label)gpbCategoria.Controls.Find("lblAcessorio7", false).FirstOrDefault();
TextBox txtAcessorio7 = (TextBox)gpbCategoria.Controls.Find("txtAcessorio7", false).FirstOrDefault();
Label lblAcessorio8 = (Label)gpbCategoria.Controls.Find("lblAcessorio8", false).FirstOrDefault();
TextBox txtAcessorio8 = (TextBox)gpbCategoria.Controls.Find("txtAcessorio8", false).FirstOrDefault();
Label lblAcessorio9 = (Label)gpbCategoria.Controls.Find("lblAcessorio9", false).FirstOrDefault();
TextBox txtAcessorio9 = (TextBox)gpbCategoria.Controls.Find("txtAcessorio9", false).FirstOrDefault();
Label lblAcessorio10 = (Label)gpbCategoria.Controls.Find("lblAcessorio10", false).FirstOrDefault();
TextBox txtAcessorio10 = (TextBox)gpbCategoria.Controls.Find("txtAcessorio10", false).FirstOrDefault();
if (txtAcessorio2 != null && txtAcessorio2.TextLength == 0)
{
gpbCategoria.Controls.Remove(txtAcessorio2);
gpbCategoria.Controls.Remove(lblAcessorio2);
btnSalvar.Focus();
if (test != 1)
{
n--;
t++;
if (n >= 1 && n <= 10)
{
testeapagar();
test = 1;
}
}
}
if (txtAcessorio3 != null && txtAcessorio3.TextLength == 0)
{
gpbCategoria.Controls.Remove(txtAcessorio3);
gpbCategoria.Controls.Remove(lblAcessorio3);
btnSalvar.Focus();
if (test != 1)
{
n--;
t++;
if (n >= 1 && n <= 10)
{
testeapagar();
test = 1;
}
}
}
if (txtAcessorio4 != null && txtAcessorio4.TextLength == 0)
{
gpbCategoria.Controls.Remove(txtAcessorio4);
gpbCategoria.Controls.Remove(lblAcessorio4);
btnSalvar.Focus();
if (test != 1)
{
n--;
t++;
if (n >= 1 && n <= 10)
{
testeapagar();
test = 1;
}
}
}
if (txtAcessorio5 != null && txtAcessorio5.TextLength == 0)
{
gpbCategoria.Controls.Remove(txtAcessorio5);
gpbCategoria.Controls.Remove(lblAcessorio5);
btnSalvar.Focus();
if (test != 1)
{
n--;
t++;
if (n >= 1 && n <= 10)
{
testeapagar();
test = 1;
}
}
}
if (txtAcessorio6 != null && txtAcessorio6.TextLength == 0)
{
gpbCategoria.Controls.Remove(txtAcessorio6);
gpbCategoria.Controls.Remove(lblAcessorio6);
btnSalvar.Focus();
if (test != 1)
{
n--;
t++;
if (n >= 1 && n <= 10)
{
testeapagar();
test = 1;
}
}
}
if (txtAcessorio7 != null && txtAcessorio7.TextLength == 0)
{
gpbCategoria.Controls.Remove(txtAcessorio7);
gpbCategoria.Controls.Remove(lblAcessorio7);
btnSalvar.Focus();
if (test != 1)
{
n--;
t++;
if (n >= 1 && n <= 10)
{
testeapagar();
test = 1;
}
}
}
if (txtAcessorio8 != null && txtAcessorio8.TextLength == 0)
{
gpbCategoria.Controls.Remove(txtAcessorio8);
gpbCategoria.Controls.Remove(lblAcessorio8);
btnSalvar.Focus();
if (test != 1)
{
n--;
t++;
if (n >= 1 && n <= 10)
{
testeapagar();
test = 1;
}
}
}
if (txtAcessorio9 != null && txtAcessorio9.TextLength == 0)
{
gpbCategoria.Controls.Remove(txtAcessorio9);
gpbCategoria.Controls.Remove(lblAcessorio9);
btnSalvar.Focus();
if (test != 1)
{
n--;
t++;
if (n >= 1 && n <= 10)
{
testeapagar();
test = 1;
}
}
}
}
}
Would something like this be what you are looking for? If you have more labels just update iAcessorioContar and it will automatically check them as well as long as you name the labels incrementally.
private void cadeiaapagarcampos(TextBox _text, EventArgs e)
{
if (_text.Text == "")
{
int iAcessorioContar = 10;
for (int iContador = 2; iContador <= iAcessorioContar; iContador++) {
Label lblAcessorio = (Label)gpbCategoria.Controls.Find("lblAcessorio"+iContador, false).FirstOrDefault();
TextBox txtAcessorio = (TextBox)gpbCategoria.Controls.Find("txtAcessorio"+iContador, false).FirstOrDefault();
if (txtAcessorio != null && txtAcessorio.TextLength == 0)
{
gpbCategoria.Controls.Remove(txtAcessorio);
gpbCategoria.Controls.Remove(lblAcessorio);
btnSalvar.Focus();
if (test != 1) {
n--;
t++;
if (n >= 1 && n <= 10)
{
testeapagar();
test = 1;
}
}
}
}
}
}
You could add the controls in pairs (label and textbox) to a list when you create them so you do not have to search the controls collection which can cost a lot of time.
You can then simply loop the list.
I have written two functions that convert a string of whitespace-separated integers into an int array. The first function uses Substring and then applies System.Int32.Parse to convert the substring into an int value:
let intsOfString (s: string) =
let ints = ResizeArray()
let rec inside i j =
if j = s.Length then
ints.Add(s.Substring(i, j-i) |> System.Int32.Parse)
else
let c = s.[j]
if '0' <= c && c <= '9' then
inside i (j+1)
else
ints.Add(s.Substring(i, j-i) |> System.Int32.Parse)
outside (j+1)
and outside i =
if i < s.Length then
let c = s.[i]
if '0' <= c && c <= '9' then
inside i (i+1)
else
outside (i+1)
outside 0
ints.ToArray()
The second function traverses the characters of the string in-place accumulating the integer without creating a temporary substring:
let intsOfString (s: string) =
let ints = ResizeArray()
let rec inside n i =
if i = s.Length then
ints.Add n
else
let c = s.[i]
if '0' <= c && c <= '9' then
inside (10*n + int c - 48) (i+1)
else
ints.Add n
outside(i+1)
and outside i =
if i < s.Length then
let c = s.[i]
if '0' <= c && c <= '9' then
inside (int c - 48) (i+1)
else
outside (i+1)
outside 0
ints.ToArray()
Benchmarking on space-separated integers 1 to 1,000,000, the first version takes 1.5s whereas the second version takes 0.3s.
Parsing such values can be performance critical so leaving 5x performance on the table by using temporary substrings can be undesirable. Parsing integers is easy but parsing other values such as floating point numbers, decimals and dates is considerably harder.
So, are there built-in functions to parse directly from a substring within a string (i.e. using the given start and length of a string) in order to avoid generating a temporary string? If not, are there any libraries that provide efficient functions to do this?
System.Int32.Parse is slowlest, because it used CultureInfo, FormatInfo and etc; and performance reason is not in the temporary strings.
Code from reflection:
private unsafe static bool ParseNumber(ref char* str, NumberStyles options, ref Number.NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)
{
number.scale = 0;
number.sign = false;
string text = null;
string text2 = null;
string str2 = null;
string str3 = null;
bool flag = false;
string str4;
string str5;
if ((options & NumberStyles.AllowCurrencySymbol) != NumberStyles.None)
{
text = numfmt.CurrencySymbol;
if (numfmt.ansiCurrencySymbol != null)
{
text2 = numfmt.ansiCurrencySymbol;
}
str2 = numfmt.NumberDecimalSeparator;
str3 = numfmt.NumberGroupSeparator;
str4 = numfmt.CurrencyDecimalSeparator;
str5 = numfmt.CurrencyGroupSeparator;
flag = true;
}
else
{
str4 = numfmt.NumberDecimalSeparator;
str5 = numfmt.NumberGroupSeparator;
}
int num = 0;
char* ptr = str;
char c = *ptr;
while (true)
{
if (!Number.IsWhite(c) || (options & NumberStyles.AllowLeadingWhite) == NumberStyles.None || ((num & 1) != 0 && ((num & 1) == 0 || ((num & 32) == 0 && numfmt.numberNegativePattern != 2))))
{
bool flag2;
char* ptr2;
if ((flag2 = (((options & NumberStyles.AllowLeadingSign) == NumberStyles.None) ? false : ((num & 1) == 0))) && (ptr2 = Number.MatchChars(ptr, numfmt.positiveSign)) != null)
{
num |= 1;
ptr = ptr2 - (IntPtr)2 / 2;
}
else
{
if (flag2 && (ptr2 = Number.MatchChars(ptr, numfmt.negativeSign)) != null)
{
num |= 1;
number.sign = true;
ptr = ptr2 - (IntPtr)2 / 2;
}
else
{
if (c == '(' && (options & NumberStyles.AllowParentheses) != NumberStyles.None && (num & 1) == 0)
{
num |= 3;
number.sign = true;
}
else
{
if ((text == null || (ptr2 = Number.MatchChars(ptr, text)) == null) && (text2 == null || (ptr2 = Number.MatchChars(ptr, text2)) == null))
{
break;
}
num |= 32;
text = null;
text2 = null;
ptr = ptr2 - (IntPtr)2 / 2;
}
}
}
}
c = *(ptr += (IntPtr)2 / 2);
}
int num2 = 0;
int num3 = 0;
while (true)
{
if ((c >= '0' && c <= '9') || ((options & NumberStyles.AllowHexSpecifier) != NumberStyles.None && ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))))
{
num |= 4;
if (c != '0' || (num & 8) != 0)
{
if (num2 < 50)
{
number.digits[(IntPtr)(num2++)] = c;
if (c != '0' || parseDecimal)
{
num3 = num2;
}
}
if ((num & 16) == 0)
{
number.scale++;
}
num |= 8;
}
else
{
if ((num & 16) != 0)
{
number.scale--;
}
}
}
else
{
char* ptr2;
if ((options & NumberStyles.AllowDecimalPoint) != NumberStyles.None && (num & 16) == 0 && ((ptr2 = Number.MatchChars(ptr, str4)) != null || (flag && (num & 32) == 0 && (ptr2 = Number.MatchChars(ptr, str2)) != null)))
{
num |= 16;
ptr = ptr2 - (IntPtr)2 / 2;
}
else
{
if ((options & NumberStyles.AllowThousands) == NumberStyles.None || (num & 4) == 0 || (num & 16) != 0 || ((ptr2 = Number.MatchChars(ptr, str5)) == null && (!flag || (num & 32) != 0 || (ptr2 = Number.MatchChars(ptr, str3)) == null)))
{
break;
}
ptr = ptr2 - (IntPtr)2 / 2;
}
}
c = *(ptr += (IntPtr)2 / 2);
}
bool flag3 = false;
number.precision = num3;
number.digits[(IntPtr)num3] = '\0';
if ((num & 4) != 0)
{
if ((c == 'E' || c == 'e') && (options & NumberStyles.AllowExponent) != NumberStyles.None)
{
char* ptr3 = ptr;
c = *(ptr += (IntPtr)2 / 2);
char* ptr2;
if ((ptr2 = Number.MatchChars(ptr, numfmt.positiveSign)) != null)
{
c = *(ptr = ptr2);
}
else
{
if ((ptr2 = Number.MatchChars(ptr, numfmt.negativeSign)) != null)
{
c = *(ptr = ptr2);
flag3 = true;
}
}
if (c >= '0' && c <= '9')
{
int num4 = 0;
do
{
num4 = num4 * 10 + (int)(c - '0');
c = *(ptr += (IntPtr)2 / 2);
if (num4 > 1000)
{
num4 = 9999;
while (c >= '0' && c <= '9')
{
c = *(ptr += (IntPtr)2 / 2);
}
}
}
while (c >= '0' && c <= '9');
if (flag3)
{
num4 = -num4;
}
number.scale += num4;
}
else
{
ptr = ptr3;
c = *ptr;
}
}
while (true)
{
if (!Number.IsWhite(c) || (options & NumberStyles.AllowTrailingWhite) == NumberStyles.None)
{
bool flag2;
char* ptr2;
if ((flag2 = (((options & NumberStyles.AllowTrailingSign) == NumberStyles.None) ? false : ((num & 1) == 0))) && (ptr2 = Number.MatchChars(ptr, numfmt.positiveSign)) != null)
{
num |= 1;
ptr = ptr2 - (IntPtr)2 / 2;
}
else
{
if (flag2 && (ptr2 = Number.MatchChars(ptr, numfmt.negativeSign)) != null)
{
num |= 1;
number.sign = true;
ptr = ptr2 - (IntPtr)2 / 2;
}
else
{
if (c == ')' && (num & 2) != 0)
{
num &= -3;
}
else
{
if ((text == null || (ptr2 = Number.MatchChars(ptr, text)) == null) && (text2 == null || (ptr2 = Number.MatchChars(ptr, text2)) == null))
{
break;
}
text = null;
text2 = null;
ptr = ptr2 - (IntPtr)2 / 2;
}
}
}
}
c = *(ptr += (IntPtr)2 / 2);
}
if ((num & 2) == 0)
{
if ((num & 8) == 0)
{
if (!parseDecimal)
{
number.scale = 0;
}
if ((num & 16) == 0)
{
number.sign = false;
}
}
str = ptr;
return true;
}
}
str = ptr;
return false;
}
public static int Parse(string s)
{
return Number.ParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
internal unsafe static int ParseInt32(string s, NumberStyles style, NumberFormatInfo info)
{
byte* stackBuffer = stackalloc byte[1 * 114 / 1];
Number.NumberBuffer numberBuffer = new Number.NumberBuffer(stackBuffer);
int result = 0;
Number.StringToNumber(s, style, ref numberBuffer, info, false);
if ((style & NumberStyles.AllowHexSpecifier) != NumberStyles.None)
{
if (!Number.HexNumberToInt32(ref numberBuffer, ref result))
{
throw new OverflowException(Environment.GetResourceString("Overflow_Int32"));
}
}
else
{
if (!Number.NumberToInt32(ref numberBuffer, ref result))
{
throw new OverflowException(Environment.GetResourceString("Overflow_Int32"));
}
}
return result;
}
private unsafe static void StringToNumber(string str, NumberStyles options, ref Number.NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
{
if (str == null)
{
throw new ArgumentNullException("String");
}
fixed (char* ptr = str)
{
char* ptr2 = ptr;
if (!Number.ParseNumber(ref ptr2, options, ref number, info, parseDecimal) || ((ptr2 - ptr / 2) / 2 < str.Length && !Number.TrailingZeros(str, (ptr2 - ptr / 2) / 2)))
{
throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
}
}
}
I've written this one for doubles, that doesn't create a temporary substring. It's meant to be used inside a JSON parser so it limits itself to how doubles can be represented in JSON according to http://www.json.org/.
It's not optimal yet because it requires you to know where the number begins and ends (begin and end parameters), so you'll have to traverse the length of the number twice to find out where it ends. It's still around 10-15x faster than double.Parse and it could be fairly easily modified that it finds the end inside the function which is then returned as an out parameter to know where you have to resume parsing the main string.
Used like so:
Parsers.TryParseDoubleFastStream("1", 0, 1, out j);
Parsers.TryParseDoubleFastStream("2.0", 0, 3, out j);
Parsers.TryParseDoubleFastStream("3.5", 0, 3, out j);
Parsers.TryParseDoubleFastStream("-4.5", 0, 4, out j);
Parsers.TryParseDoubleFastStream("50.06", 0, 5, out j);
Parsers.TryParseDoubleFastStream("1000.65", 0, 7, out j);
Parsers.TryParseDoubleFastStream("-10000.8600", 0, 11, out j);
Code can be found here:
https://gist.github.com/3010984 (would be too lengthy to post here).
And StandardFunctions.IgnoreChar is for my purpose as simple as:
public static bool IgnoreChar(char c)
{
return c < 33;
}
Paste all this code into C# and call Test(). This is as close as you can get to operating directly on the string array to parse numbers using C#. It is built for speed, not elegance. The ParseInt and ParseFloat function were created for an OpenGL graphics engine to import vectors from text-based 3d models. Parsing floats is a significant bottleneck in that process. This was as fast as I could make it.
using System.Diagnostics;
private void Test()
{
Stopwatch sw = new Stopwatch();
StringBuilder sb = new StringBuilder();
int iterations = 1000;
// Build a string of 1000000 space separated numbers
for (var n = 0; n < iterations; n++)
{
if (n > 0)
sb.Append(' ');
sb.Append(n.ToString());
}
string numberString = sb.ToString();
// Time the process
sw.Start();
StringToInts(numberString, iterations);
//StringToFloats(numberString, iterations);
sw.Stop();
long proc1 = sw.ElapsedMilliseconds;
Console.WriteLine("iterations: {0} \t {1}ms", iterations, proc1);
}
private unsafe int[] StringToInts(string s, int length)
{
int[] ints = new int[length];
int index = 0;
int startpos = 0;
fixed (char* pStringBuffer = s)
{
fixed (int* pIntBuffer = ints)
{
for (int n = 0; n < s.Length; n++)
{
if (s[n] == ' ' || n == s.Length - 1)
{
if (n == s.Length - 1)
n++;
// pIntBuffer[index++] = int.Parse(new string(pStringBuffer, startpos, n - startpos));
pIntBuffer[index++] = ParseInt((pStringBuffer + startpos), n - startpos);
startpos = n + 1;
}
}
}
}
return ints;
}
private unsafe float[] StringToFloats(string s, int length)
{
float[] floats = new float[length];
int index = 0;
int startpos = 0;
fixed (char* pStringBuffer = s)
{
fixed (float* pFloatBuffer = floats)
{
for (int n = 0; n < s.Length; n++)
{
if (s[n] == ' ' || n == s.Length - 1)
{
if (n == s.Length - 1)
n++;
pFloatBuffer[index++] = ParseFloat((pStringBuffer + startpos), n - startpos); // int.Parse(new string(pStringBuffer, startpos, n - startpos));
startpos = n + 1;
}
}
}
}
return floats;
}
public static unsafe int ParseInt(char* input, int len)
{
int pos = 0; // read pointer position
int part = 0; // the current part (int, float and sci parts of the number)
bool neg = false; // true if part is a negative number
int* ret = stackalloc int[1];
while (pos < len && (*(input + pos) > '9' || *(input + pos) < '0') && *(input + pos) != '-')
pos++;
// sign
if (*(input + pos) == '-')
{
neg = true;
pos++;
}
// integer part
while (pos < len && !(input[pos] > '9' || input[pos] < '0'))
part = part * 10 + (input[pos++] - '0');
*ret = neg ? (part * -1) : part;
return *ret;
}
public static unsafe float ParseFloat(char* input, int len)
{
//float ret = 0f; // return value
int pos = 0; // read pointer position
int part = 0; // the current part (int, float and sci parts of the number)
bool neg = false; // true if part is a negative number
float* ret = stackalloc float[1];
// find start
while (pos < len && (input[pos] < '0' || input[pos] > '9') && input[pos] != '-' && input[pos] != '.')
pos++;
// sign
if (input[pos] == '-')
{
neg = true;
pos++;
}
// integer part
while (pos < len && !(input[pos] > '9' || input[pos] < '0'))
part = part * 10 + (input[pos++] - '0');
*ret = neg ? (float)(part * -1) : (float)part;
// float part
if (pos < len && input[pos] == '.')
{
pos++;
double mul = 1;
part = 0;
while (pos < len && !(input[pos] > '9' || input[pos] < '0'))
{
part = part * 10 + (input[pos] - '0');
mul *= 10;
pos++;
}
if (neg)
*ret -= (float)part / (float)mul;
else
*ret += (float)part / (float)mul;
}
// scientific part
if (pos < len && (input[pos] == 'e' || input[pos] == 'E'))
{
pos++;
neg = (input[pos] == '-'); pos++;
part = 0;
while (pos < len && !(input[pos] > '9' || input[pos] < '0'))
{
part = part * 10 + (input[pos++] - '0');
}
if (neg)
*ret /= (float)Math.Pow(10d, (double)part);
else
*ret *= (float)Math.Pow(10d, (double)part);
}
return (float)*ret;
}
So, are there built-in functions to parse directly from a substring within a string (i.e.
using the given start and length of a string) in order to avoid generating a temporary
string? If not, are there any libraries that provide efficient functions to do this?
It seems that you want to use a lexing buffer and a lexer, similar to what OCaml can provide with ocamllex and the Lexbuf buffer. (I cannot provide references for F#.)
If your benchmark involving a huge string of integers separated by other tokens is your typical case, it will work well. But in other situations, it could be impractical.
Not sure if this is any good, but have you tried something like:
var stringValues = input.split(" ");
var intValues = Array.ConvertAll(stringValues, s => int.Parse(s));