Calculator using all the input data on a single line - c#

(later edit)
Just finished this assignment. I was limited to using System and using System.collections.Generic. Nothing else. I ended up using a List<> from the from the collections.Generic. and the principles of Polish Notation. After every "Encounter" of a operation sign (+-/*) I used the index - 1 and - 2 to get the previous 2 numbers and make the operation. Then I used .Insert to insert the result at the current index, afterwards I used .Remove to substract from the list the numbers and the operator which I just used and after I continued with the recursive function on the new list obtained starting with the index from 0 again. The polish notation articles posted in the comments helped me the most to understand the logic behind this
*
I'm trying to figure out from the past several days a way to implement the following logic into a c# program. Using only System.
This is a small console calculator where the input is inserted on a single line in the console.
For example the following input + / * + 65 32 46 2 - 1 1.25 should be translated into a math operation looking like this => ((65 + 32) * 46) / 2 + (1 - 1.25)
Another example would be * + 3 2 - 9.5 6.5: this should be calculated in the following order 3 + 2 * (9.5 - 6.5).
Another example / + 5 3 2 equals with => (5 + 3) / 2
I have to make the function recursive.
I figured out how to make it if all the operations sings are in front of the digits in the input. (I just separate the operator list and inverse it and I get two separate lists: one containing the operation signs and the other containing the numbers). What I'm struggling is to figure out a way to do the operations if there is a math sign in between the numbers (like in the first an the second example).
I don't necessarily need a code for this, maybe an explanation or if somebody could point me to the right direction where I can read about some algorithm / math formula or something that could help me better understand how to implement this.
Thank you in advance.

The normal method of evaluating Polish Notation expressions doesn't require recursion, you use a stack (like Forth or RPN) and evaluate as you go.
An easy way to create a recursive version is to consider the expression language BNF then crafting a recursive descent parser from the grammer.
For example, a possible BNF would be:
expr = op arg arg
op = [+-*/] // cheating; use regex to describe terminal
arg = number | expr
number = [0-9]+ // using Regex to describe terminal
So now you would create methods for each element:
double expr() {
string opStr = op();
double arg1 = arg();
double arg2 = arg();
double ans;
switch (opStr) {
case "+":
ans = arg1+arg2;
break;
// case and so on
}
return ans;
}
static string operators = "+-*/";
string op() {
if (operators.Contains(peekChar()))
return nextCharAsStr();
else
throw new Exception("Missing operator");
}
double arg() {
double? num = number();
if (num.HasValue)
return num.Value;
else
return expr();
}
double? number() {
string ans = "";
while (Char.IsDigit(peekChar()))
ans += nextCharAsStr();
if (String.IsNullOrEmpty(ans))
return null;
else
return Double.Parse(ans);
}
NOTE: Whitespace and end of string is left as an exercise to the reader.
You could also use a tokenizer that extracts terminals from the string instead of working directly with characters in the parser terminal methods.

There are different kinds of recursion. The most common (code recursion) is probably what you're asking about, wherein a function (or set of functions) call each other until some sort of exit condition is reached.
For this, I'd go with more of a data recursive approach. This version only supports single-digit operands and binary operators.
(Really bad pseudocode below).
stack<char> operators;
stack<char> operands;
for(var i=input.Length-1; i>=0; --i)
{
var c = input[i];
if (Char.IsDigit(c)) /// note that this only handles single-digit numbers.
operands.push(c);
else
operators.push(c);
if (operators.count >= 1 && operands.count >= 2)
{
var operator = operators.pop();
/// handle binary operators
left = operands.pop();
right = operands.pop();
switch(operator) {
case '+' : result = left + right; break;
case '-' : result = left - right; break;
case '*' : result = left * right; break;
case '/' : result = left / right; break;
}
operands.push(result);
}
}
var result = operands.pop();
When that completes, your operands stack should have only one item which is the result of the expression. and operators should be empty.
If you have leftover operators, then there weren't enough values in the input. If you have more than one value in operands, there weren't enough operators in the input. If you have zero operands (ie, no result), then there weren't any in the input to start with.
For a real implementation, you'd want to parse the string to get multi-digit operands, handle unary operators, ignore whitespace, etc.
Edit: reversed the loop direction.

So you want something like this:
static void Main(string[] args)
{
Expression expr = Parse("+ / * + 65 32 46 2 - 1 1.25");
Console.WriteLine($"f = {expr}");
// f = ((((65 + 32) * 46) / 2) + (1 - 1.25))
Func<double> f = Compile(expr);
Console.WriteLine($"f = {f()}");
// f = 2230.75
}
public static Func<double> Compile(Expression expr)
{
return Expression.Lambda(expr).Compile() as Func<double>;
}
public static Expression Parse(string input)
{
var tokens = new Tokenizer(input);
return Parse(tokens);
}
There are two parts this.
First split the input into substrings using the white space. Use input.Split() for that. Then, scan the parts from right to left (from last to first) and extract tokens. A token is a substring that might represent a number or an operation (or a variable if you extend the code below). This is the job of Tokenizer class, the implements IEnumerable<TokenInfo> to act as a collection. A TokenInfo is a data type that describes each token, and it contains the following:
An identifying tag of enum Token.Number, Token.Operator or Token.End
The string text identifying the operator (when applicable)
The double value representing a number (when applicable)
public enum Token
{
Unknown,
Number,
Operator,
End
}
public struct TokenInfo
{
public TokenInfo(Token token)
{
Token=token;
Text = string.Empty;
Value = 0;
}
public TokenInfo(string op) : this(Token.Operator)
{
Text = op;
}
public TokenInfo(double value) : this(Token.Number)
{
Value = value;
}
public TokenInfo(Token token, string text) : this(token)
{
Text = text;
}
public Token Token { get; }
public string Text { get; }
public double Value { get; }
}
public class Tokenizer : IEnumerable<TokenInfo>
{
readonly string[] operators = new[] { "+", "-", "*", "/" };
readonly List<TokenInfo> tokens;
public Tokenizer(string input)
{
tokens = new List<TokenInfo>();
string[] parts = input.Split(' ');
for (int i = parts.Length - 1; i >= 0; i--)
{
if (operators.Contains(parts[i]))
{
tokens.Add(new TokenInfo(parts[i]));
}
else if (double.TryParse(parts[i], out var x))
{
tokens.Add(new TokenInfo(x));
}
else
{
tokens.Add(new TokenInfo(Token.Unknown, parts[i]));
}
}
tokens.Add(new TokenInfo(Token.End));
}
public IEnumerator<TokenInfo> GetEnumerator()
{
return tokens.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Parse the list of tokens and built the expression tree. Keep a Stack<Expression> and assume the top expression is always the result of the previous operation. Also what gets returned in the end is the top of the stack, if it is only one expression tall. Otherwise, there is an imbalance in the operators and/or the values.
Do the following for each Token type
Token.Number parse the value of the number using double.TryParse() and add a ConstantExpression on the stack
Token.Operator pull two values from the top of the stack and add an appropriate BinaryEpression to the stack. Possible options are Expression.Add(), Expression.Subtract(), Expression.Multiply(), and Expression.Divide()
Token.End return the last expression of the top of the stack.
public static Expression Parse(IEnumerable<TokenInfo> tokens)
{
Stack<Expression> stack = new Stack<Expression>();
foreach (var item in tokens)
{
switch (item.Token)
{
case Token.Number:
{
var expr = Expression.Constant(item.Value, typeof(double));
stack.Push(expr);
}
break;
case Token.Operator:
{
var arg1 = stack.Pop();
var arg2 = stack.Pop();
switch (item.Text)
{
case "+":
stack.Push(Expression.Add(arg1, arg2));
break;
case "-":
stack.Push(Expression.Subtract(arg1, arg2));
break;
case "*":
stack.Push(Expression.Multiply(arg1, arg2));
break;
case "/":
stack.Push(Expression.Divide(arg1, arg2));
break;
default:
throw new NotSupportedException($"Invalid operator {item.Text}.");
}
}
break;
case Token.End:
{
if (stack.Count != 1)
{
throw new InvalidOperationException("Imbalanced expression tree.");
}
return stack.Pop();
}
}
}
throw new ArgumentException($"Invalid expression.", nameof(tokens));
}
Optionally the Expression can be compiled into a function, using the Compile() function above. This code assumes there are no ParameterExpression nodes in the expression tree which would need to be accounted for as arguments to the compiled function.
The addition of variables in the expression and the subsequent compilation into unary or binary functions is pretty straightforward. Add a Token for a variable, produce an appropriate TokenInfo when a string that is not an operator is encountered in the tokenizer and then add ParameterExpression objects to the stack, just like numeric values are added. The compilation parts need references to these parameters from the tree so a custom ExpressionVistor would be needed to extract those nodes.

Related

How to parse string and prepare it for math

I want to parse latex string to math expression with following code
static string ParseString(List<string> startItems, string input)
{
foreach (var item in startItems)
{
int charLocation = input.IndexOf(item, StringComparison.Ordinal);
if (charLocation > 0)
{
switch (input.Substring(0, charLocation).Last())
{
case '{':
case '+':
case '-':
case '*':
break;
default:
input = input.Replace(item, "*" + item);
break;
}
}
}
return input;
}
static void Main(string[] args)
{
string ToParse = #"\sqrt{3}\sqrt{3}*4\sqrt{3}";
ToParse = ParseString(new System.Collections.Generic.List<string> { #"\frac", #"\sqrt" }, ToParse);
Console.WriteLine(ToParse);
}
I should get before every \sqrt word multiply char.But my program breaks in first \sqrt and cant parse others.
Result is
\sqrt{3}\sqrt{3}*4\sqrt{3}
And i want to get
\sqrt{3}*\sqrt{3}*4*\sqrt{3}
Sorry for bad English.
Based on provided example you can do as simple as input = input.Replace(item, "*" + item).TrimStart('*'); (Replace replaces all occurrences of string/char).
As for your code, you have next issues:
First inclusion of \sqrt returns charLocation set to 0 (arrays are xero-based in C#), so your check charLocation > 0 evaluates to false, so nothing happens
input.Substring(i, l) accepts 2 parameters - start index and length, so it is incorrect to use your charLocation as second parameter(even if you remove previous check, input.Substring(0, charLocation).Last() will end in exception Sequence contains no elements).
So if you want to go through all occurrences and insert symbol based on some logic then you'll need to have some cycle and move start index and calculate length(and also use something else, not Replace). Or you can dive into Regular Expressions.

Convert string into mathematical expression [duplicate]

This question already has answers here:
Is there a string math evaluator in .NET?
(18 answers)
Closed 3 years ago.
Is there an easy way to parse a simple math expression represented as a string such as (x+(2*x)/(1-x)), provide a value for x, and get a result?
I looked at the VSAEngine per several online examples, however, I am getting a warning that this assembly has been deprecated and not to use it.
If it makes any differences, I am using .NET 4.0.
I urge caution against choosing an existing generic expression evaluator over a purpose-built math evaluator. The reason for this is expression evaluators are not limited to math. A clever individual could use this to create an instance of any type in the framework and call any method on the type, and that would allow him to do some decidedly unwelcome things. For example: new System.Net.WebClient().DownloadFile("illegalchildpornurl", "C:\openme.gif") will evaluate just fine in most of those, and do just what it sounds like it would (and make you a felon at the same time).
This doesn't mean don't look for something that's already written; it just means be careful. You want one that does math, and only math. Most of what's already out there isn't that picky.
I was recently using mXparser, which is a math parser library. It gives you a lot of flexibility, such as variables, functions, constants, operators. You will find below a few usage examples:
Example 1 - simple formula
Expression e = new Expression("1 + pi");
double v = e.calculate();
Example 2 - formula with variables, functions, etc.
Argument x = new Argument("x = 2");
Constant a = new Constant("a = sin(10)");
Function f = new Function("f(t) = t^2");
Expression e = new Expression("2*x + a - f(10)", x, a, f);
double v = e.calculate();
https://mxparser.codeplex.com/
https://mathparser.org/
Found recntly - you can try the syntax (and see the use case) via the Scalar Calculator app that is powered by mXparser.
Best regards
You can try using DataTable.Compute.
A related one is DataColumn.Expression.
Also check out: Doing math in vb.net like Eval in javascript
Note: I haven't used these myself.
I would also have a look at Jace (https://github.com/pieterderycke/Jace). Jace is a high performance math parser and calculation engine that supports all the .NET flavors (.NET 4.x, Windows Phone, Windows Store, ...). Jace is also available through NuGet: https://www.nuget.org/packages/Jace
Here is one way to do it. This code is written in Java. Note it does not handle negative numbers right now, but you can add that.
public class ExpressionParser {
public double eval(String exp, Map<String, Double> vars){
int bracketCounter = 0;
int operatorIndex = -1;
for(int i=0; i<exp.length(); i++){
char c = exp.charAt(i);
if(c == '(') bracketCounter++;
else if(c == ')') bracketCounter--;
else if((c == '+' || c == '-') && bracketCounter == 0){
operatorIndex = i;
break;
}
else if((c == '*' || c == '/') && bracketCounter == 0 && operatorIndex < 0){
operatorIndex = i;
}
}
if(operatorIndex < 0){
exp = exp.trim();
if(exp.charAt(0) == '(' && exp.charAt(exp.length()-1) == ')')
return eval(exp.substring(1, exp.length()-1), vars);
else if(vars.containsKey(exp))
return vars.get(exp);
else
return Double.parseDouble(exp);
}
else{
switch(exp.charAt(operatorIndex)){
case '+':
return eval(exp.substring(0, operatorIndex), vars) + eval(exp.substring(operatorIndex+1), vars);
case '-':
return eval(exp.substring(0, operatorIndex), vars) - eval(exp.substring(operatorIndex+1), vars);
case '*':
return eval(exp.substring(0, operatorIndex), vars) * eval(exp.substring(operatorIndex+1), vars);
case '/':
return eval(exp.substring(0, operatorIndex), vars) / eval(exp.substring(operatorIndex+1), vars);
}
}
return 0;
}
}
You need to import java.util.Map.
Here is how I use this code:
ExpressionParser p = new ExpressionParser();
Map vars = new HashMap<String, Double>();
vars.put("x", 2.50);
System.out.println(p.eval(" 5 + 6 * x - 1", vars));
Another option you may want to look into is the Spring.NET Framework's expression evaluation functionality. It can do a lot more than math, too.
However, the entire Spring.NET Framework might be a bit of overkill for your needs if you don't require the rest of the functionality.
Related: Equation expression parser with precedence.
As I answered in this thread (Best Free C# Math Parser using variables, user defined functions, custom operators), you can use Mathos Parser, which you can simply paste into your source code.
Mathos.Parser.MathParser parser = new Mathos.Parser.MathParser();
string expr = "(x+(2*x)/(1-x))"; // the expression
decimal result = 0; // the storage of the result
parser.LocalVariables.Add("x", 41); // 41 is the value of x
result = parser.Parse(expr); // parsing
Console.WriteLine(result); // 38.95
I recomend you to use MEEL for this.
// parse string to IExpression (symbolic type)
IExpression expression = BaseExpression.Parse("(x+(2*x)/(1-x))");
// create your own collection for attributes
var attributes = new MathAttributeCollection();
// create local variable named "x" with value 5
var attributeX = new ScalarAttrInt("x") {Value = new ScalarConstInt(5)};
attributes.Add(attributeX);
// execute math expression where x=5
var result = expression.Execute(attributes);
MessageBox.Show(result.GetText());
// result: 2.5

TPIN number validation in java, c++ or c#

I have small requirement for TPIN validation in my college project, The requirement is, we should not allow the user to set his TPIN in the below scenarios .
TPIN should not be in sequence. (either ascending or descending ex: 123456, 456789, 987654 or 654321)
TPIN should not start from Zero (ex: 0127865, 098764)
TPIN should not be repetitive digit (ex: 888888,222222 etc.)
For the third one my idea is to divide the number by 11 and check for the reminder..
Any ideas plz..
public class Validate
{
public static void main(String[] args)
{
int iTpin = Integer.parseInt(args[0]); // Converted string to int
System.out.println("The entered TPIN is:"+iTpin);
boolean flag = validate(iTpin);
if (flag== true)
System.out.println("The entered TPIN is valid");
else
System.out.println("The entered TPIN is Invalid");
}
public static boolean validate(int passedTpin)
{
if (passedTpin == 0)
return false;
else if ( passedTpin%11 == 0)
return false;
else
return true;
}
}
Finally created code for the sequence of digits. It might be useful for others
private static boolean containsRepetitiveDigits(String tpin) {
char firstChar = tpin.charAt(0);
for (int i = 1; i < tpin.length(); i++) {
char nextChar = tpin.charAt(i);
if ((Character.valueOf(nextChar)).compareTo(Character
.valueOf(firstChar)) != 0) {
return false;
}
}
System.out.println("Error:TPIN contains repetitive digits");
return true;
}
For start, using Int32 to store a number means it shouldn't exceed 2,147,483,647. And apart from this, you won't be able to check for the leading zero once you have converted to a number, since leading zeros obviously disappear once you get a number.
This means you should keep the input as a string during validation. This actually makes your job easier, since you can index individual characters, without the need to use arithmetic operations.
Since you are working with strings, you should also check if the input string contains invalid (non-digit) characters before anything else:
bool ContainsInvalidCharacters(string input)
{
// check if there is a non-digit character
foreach (char c in input)
if (!char.IsDigit(c))
return true;
return false;
}
Then you can continue adding individual rules. For example, to check if characters are repeating, you will do something like:
bool ContainsRepetitiveDigits(string input)
{
if (input.Length == 0)
return false;
// get the first character
char firstChar = input[0];
// check if there is a different character
foreach (char c in input)
if (c != firstChar)
return false;
// if not, it means it's repetitive
return true;
}
bool StartsWithZero(string input)
{
if (input.Length == 0)
return false;
return (input[0] == '0');
}
To detect sequences, the most straightforward way is to get the difference of first two characters, and then check if it changes through the entire string:
bool IsSequence(string input)
{
// we need at least two characters
// for a sequence
if (input.Length < 2)
return false;
// get the "delta" between first two
// characters
int difference = input[1] - input[0];
// allowed differences are:
// -1: descending sequence
// 0: repetitive digits
// 1: ascending sequence
if (difference < -1 || difference > 1)
return false;
// check if all characters are equally
// distributed
for (int i = 2; i < input.Length; i++)
if (input[i] - input[i - 1] != difference)
return false;
// this is a sequence
return true;
}
Once you've defined all of your rules, you can create a single method which will test them one by one:
bool Validate(string input)
{
// list of all predicates to check
IEnumerable<Predicate<string>> rules = new Predicate<string>[]
{
ContainsInvalidCharacters,
ContainsRepetitiveDigits,
StartsWithZero,
IsSequence
};
// check if any rule matches
foreach (Predicate<string> check in rules)
if (check(input))
return false;
// if no match, it means input is valid
return true;
}
Note that IsSequence detects repetitive digit patterns also (when character difference is zero). If you want to explicitly prevent this, alter the condition where allowed differences are checked. Alternatively, you can remove the ContainsRepetitiveDigits rule altogether.
[Edit]
Since I see you are using Java instead of C#, I will try to provide a better example.
Disclaimer: I don't usually program in Java, but from what I know, Java doesn't support delegates the way C# does. So I will try to provide a Java example (hope it will work), which expresses my "composite validation" intent better.
(Suspicious Java code follows)
First, define an interface which all validation rules will implement:
// (java code)
/**
* Defines a validation rule.
*/
public interface IValidationRule
{
/**
* Returns a description of this
* validation rule.
*/
String getDescription();
/**
* Returns true if this rule
* is matched.
*/
boolean matches(String tpin);
}
Next, define each rule in a separate class, implementing both getDescription and matches methods:
// (java code)
public class RepetitiveDigitsRule implements IValidationRule
{
public String getDescription()
{
return "TPIN contains repetitive digits";
}
public boolean matches(String tpin)
{
char firstChar = tpin.charAt(0);
for (int i = 1; i < tpin.length(); i++)
if (tpin.charAt(i) != firstChar)
return false;
return true;
}
}
public class StartsWithZeroRule implements IValidationRule
{
public String getDescription()
{
return "TPIN starts with zero";
}
public boolean matches(String tpin)
{
if (tpin.length() < 1)
return false;
return tpin.charAt(0) == '0';
}
}
You can see that matches method does not print anything to console. It simply returns true if rule is matched, and leaves to its caller to decide whether to print its description (to console, a message box, web page, whatever).
Finally, you can instantiate all known rules (implementations of IValidationRule) and check them one by one:
// (java code)
public class Validator
{
// instantiate all known rules
IValidationRule[] rules = new IValidationRule[] {
new RepetitiveDigitsRule(),
new StartsWithZeroRule()
};
// validate tpin using all known rules
public boolean validate(String tpin)
{
System.out.print("Validating TPIN " + tpin + "... ");
// for all known rules
for (int i = 0; i < rules.length; i++)
{
IValidationRule rule = rules[i];
// if rule is matched?
if (rule.matches(tpin))
{
// print rule description
System.out.println("Error: " + rule.getDescription());
return false;
}
}
System.out.println("Success.");
return true;
}
}
I recommend trying to follow this pattern. You will end up with code much easier to reuse and maintain.
You can generate sequetial tpin (ascending & decending) based on the first digit then compare it to the inputted tpin. If it matches then it is invalid.
public static bool IsSequential(string pin, bool descending)
{
int p = Convert.ToInt32(pin.Substring(0,1));
string tpin = string.Empty;
for (int i = 0; i < 6; i++)
{
tpin += descending ? (p--).ToString() : (p++).ToString();
}
return pin.Equals(tpin);
}
For item 3, you cannot just divide by 11 because some pin will have remainder 0. (i.e. 221199 is a valid one but the remainder is 0). You can get the first digit and use a loop to compare to the remaining digits.

Best solution for an StringToInt function in C#

I were asked to do an StringToInt / Int.parse function on the white board in an job interview last week and did not perform very good but I came up with some sort of solution. Later when back home I made one in Visual Studion and I wonder if there are any better solution than mine below.
Have not bothred with any more error handling except checking that the string only contains digits.
private int StrToInt(string tmpString)
{
int tmpResult = 0;
System.Text.Encoding ascii = System.Text.Encoding.ASCII;
byte[] tmpByte = ascii.GetBytes(tmpString);
for (int i = 0; i <= tmpString.Length-1; i++)
{
// Check whatever the Character is an valid digit
if (tmpByte[i] > 47 && tmpByte[i] <= 58)
// Here I'm using the lenght-1 of the string to set the power and multiply this to the value
tmpResult += (tmpByte[i] - 48) * ((int)Math.Pow(10, (tmpString.Length-i)-1));
else
throw new Exception("Non valid character in string");
}
return tmpResult;
}
I'll take a contrarian approach.
public int? ToInt(this string mightBeInt)
{
int convertedInt;
if (int.TryParse(mightBeInt, out convertedInt))
{
return convertedInt;
}
return null;
}
After being told that this wasn't the point of the question, I'd argue that the question tests C coding skills, not C#. I'd further argue that treating strings as arrays of characters is a very bad habit in .NET, because strings are unicode, and in any application that might be globalized, making any assumption at all about character representations will get you in trouble, sooner or later. Further, the framework already provides a conversion method, and it will be more efficient and reliable than anything a developer would toss off in such a hurry. It's always a bad idea to re-invent framework functionality.
Then I would point out that by writing an extension method, I've created a very useful extension to the string class, something that I would actually use in production code.
If that argument loses me the job, I probably wouldn't want to work there anyway.
EDIT: As a couple of people have pointed out, I missed the "out" keyword in TryParse. Fixed.
Converting to a byte array is unnecessary, because a string is already an array of chars. Also, magic numbers such as 48 should be avoided in favor of readable constants such as '0'. Here's how I'd do it:
int result = 0;
for (int i = str.Length - 1, factor = 1; i >= 0; i--, factor *= 10)
result += (str[i] - '0') * factor;
For each character (starting from the end), add its numeric value times the correct power of 10 to the result. The power of 10 is calculated by multiplying it with 10 repeatedly, instead of unnecessarily using Math.Pow.
I think your solution is reasonably ok, but instead of doing math.pow, I would do:
tmpResult = 10 * tmpResult + (tmpByte[i] - 48);
Also, check the length against the length of tmpByte rather than tmpString. Not that it normally should matter, but it is rather odd to loop over one array while checking the length of another.
And, you could replace the for loop with a foreach statement.
If you want a simple non-framework using implementation, how 'bout this:
"1234".Aggregate(0, (s,c)=> c-'0'+10*s)
...and a note that you'd better be sure that the string consists solely of decimal digits before using this method.
Alternately, use an int? as the aggregate value to deal with error handling:
"12x34".Aggregate((int?)0, (s,c)=> c>='0'&&c<='9' ? c-'0'+10*s : null)
...this time with the note that empty strings evaluate to 0, which may not be most appropriate behavior - and no range checking or negative numbers are supported; both of which aren't hard to add but require unpretty looking wordy code :-).
Obviously, in practice you'd just use the built-in parsing methods. I actually use the following extension method and a bunch of nearly identical siblings in real projects:
public static int? ParseAsInt32(this string s, NumberStyles style, IFormatProvider provider) {
int val;
if (int.TryParse(s, style, provider, out val)) return val;
else return null;
}
Though this could be expressed slightly shorter using the ternary ? : operator doing so would mean relying on side-effects within an expression, which isn't a boon to readability in my experience.
Just because i like Linq:
string t = "1234";
var result = t.Select((c, i) => (c - '0') * Math.Pow(10, t.Length - i - 1)).Sum();
I agree with Cyclon Cat, they probably want someone who will utilize existing functionality.
But I would write the method a little bit different.
public int? ToInt(this string mightBeInt)
{
int number = 0;
if (Int32.TryParse(mightBeInt, out number))
return number;
return null;
}
Int32.TryParse does not allow properties to be given as out parameter.
I was asked this question over 9000 times on interviews :) This version is capable of handling negative numbers and handles other conditions very well:
public static int ToInt(string s)
{
bool isNegative = false, gotAnyDigit = false;
int result = 0;
foreach (var ch in s ?? "")
{
if(ch == '-' && !(gotAnyDigit || isNegative))
{
isNegative = true;
}
else if(char.IsDigit(ch))
{
result = result*10 + (ch - '0');
gotAnyDigit = true;
}
else
{
throw new ArgumentException("Not a number");
}
}
if (!gotAnyDigit)
throw new ArgumentException("Not a number");
return isNegative ? -result : result;
}
and a couple of lazy tests:
[TestFixture]
public class Tests
{
[Test]
public void CommonCases()
{
foreach (var sample in new[]
{
new {e = 123, s = "123"},
new {e = 110, s = "000110"},
new {e = -011000, s = "-011000"},
new {e = 0, s = "0"},
new {e = 1, s = "1"},
new {e = -2, s = "-2"},
new {e = -12223, s = "-12223"},
new {e = int.MaxValue, s = int.MaxValue.ToString()},
new {e = int.MinValue, s = int.MinValue.ToString()}
})
{
Assert.AreEqual(sample.e, Impl.ToInt(sample.s));
}
}
[Test]
public void BadCases()
{
var samples = new[] { "1231a", null, "", "a", "-a", "-", "12-23", "--1" };
var errCount = 0;
foreach (var sample in samples)
{
try
{
Impl.ToInt(sample);
}
catch(ArgumentException)
{
errCount++;
}
}
Assert.AreEqual(samples.Length, errCount);
}
}

How should I convert a number with a text suffix to an integer in c#?

What's the cleanest/best way in C# to convert something like 400AMP or 6M to an integer? I won't always know what the suffix is, and I just want whatever it is to go away and leave me with the number.
You could use a regular expression:
Regex reg = new Regex("[0-9]*");
int result = Convert.ToInt32(reg.Match(input));
Okay, here's a long-winded solution which should be reasonably fast. It's similar to Guffa's middle answer, but I've put the conditions inside the body of the loop as I think that's simpler (and allows us to fetch the character just once). It's a matter of personal taste really.
It deliberately doesn't limit the number of digits that it matches, because if the string is an integer which overflows Int32, I think I'd rather see an exception than just a large integer :)
Note that this also handles negative numbers, which I don't think any of the other solutions so far do...
using System;
class Test
{
static void Main()
{
Console.WriteLine(ParseLeadingInt32("-1234AMP"));
Console.WriteLine(ParseLeadingInt32("+1234AMP"));
Console.WriteLine(ParseLeadingInt32("1234AMP"));
Console.WriteLine(ParseLeadingInt32("-1234"));
Console.WriteLine(ParseLeadingInt32("+1234"));
Console.WriteLine(ParseLeadingInt32("1234"));
}
static int ParseLeadingInt32(string text)
{
// Declared before loop because we need the
// final value
int i;
for (i=0; i < text.Length; i++)
{
char c = text[i];
if (i==0 && (c=='-' || c=='+'))
{
continue;
}
if (char.IsDigit(c))
{
continue;
}
break;
}
return int.Parse(text.Substring(0, i));
}
}
It's possibly not the cleanest method, but it's reasonably simple (a one liner) and I would imagine faster than a regex (uncompiled, for sure).
var str = "400AMP";
var num = Convert.ToInt32(str.Substring(0, str.ToCharArray().TakeWhile(
c => char.IsDigit(c)).Count()));
Or as an extension method:
public static int GetInteger(this string value)
{
return Convert.ToInt32(str.Substring(0, str.ToCharArray().TakeWhile(
c => char.IsDigit(c)).Count()));
}
Equivalently, you could construct the numeric string from the result of the TakeWhile function, as such:
public static int GetInteger(this string value)
{
return new string(str.ToCharArray().TakeWhile(
c => char.IsNumber(c)).ToArray());
}
Haven't benchmarked them, so I wouldn't know which is quicker (though I'd very much suspect the first). If you wanted to get better performance, you would just convert the LINQ (extension method calls on enumerables) to a for loop.
Hope that helps.
There are several options...
Like using a regular expression:
int result = int.Parse(Regex.Match(input, #"^\d+").Groups[0].Value);
Among the fastest; simply looping to find digits:
int i = 0;
while (i < input.Length && Char.IsDigit(input, i)) i++;
int result = int.Parse(input.Substring(0, i));
Use LastIndexOfAny to find the last digit:
int i = input.LastIndexOfAny("0123456789".ToCharArray()) + 1;
int result = int.Parse(input.Substring(0, i));
(Note: breaks with strings that has digits after the suffix, like "123asdf123".)
Probably fastest; parse it yourself:
int i = 0;
int result = 0;
while (i < input.Length) {
char c = input[i];
if (!Char.IsDigit(c)) break;
result *= 10;
result += c - '0';
i++;
}
If all you want to do is remove an unknown postfix from what would otherwise be an int, here is how I would do it:
I like a utility static method I call IsInt(string possibleInt) which will, as the name implies, return True if the string will parse into an int. You could write this same static method into your utility class (if it's not there already) and try:
`string foo = "12345SomePostFix";
while (!Tools.ToolBox.IsInt(foo))
{
foo = foo.Remove(foo.Length - 1);
}
int fooInt = int.Parse(foo);`

Categories