Convert Text into boolean expression in C# - c#

i have a project to create a boolean table (incidence matrix) for an information retrieval system, so i created 3 files and i took the words from each file and removed the stopping words from them, and added them to a listView using c# language, and all i need is to be able to take the query from a textBox and convert it into a suitable boolean expression so that it will return the names of documents that the satisfies the boolean expression.
eg. "Ahmed and love and not ali"
This should give me Ahmed && love && ! Ali which will be true for doc1.

Using String.Replace
string src = "Ahmed and love and not ali";
string res = src.Replace("and", "&&")
.Replace("not", "!");
string[] parts = res.Split(' '); // { Ahmed , && , love , && , ! , ali }
//Do Shunting-Yard algorithm to convert infix expression to postfix
ToPostfix(parts); // Ahmed love && ali ! &&
bool result = PerformRPN(parts); //Calculate postfix, you should read the Reverse Polish Notation article
Link to the wikis (updated the shunting-yard reference to a closer version to the OP problem):
Shunting-Yard algorithm
Reverse Polish Notation
There are a few key concept in the algorithm that i'll explain further
IsOperator
bool IsOperator(string part) {return part=="&&" || part=="||" || part == "!" || ...}
IsOperand - is not Operator
If you need anymore help, let me know
Extra: (as Jared suggest)
You should initialize your table value in a dictionary
Dictionary<string, bool> doc1 = new Dictionary<string, bool>();
doc1.Add("Ahmed", doc1BooleanArray[0]);
doc1.Add("love", doc1BooleanArray[1]);
...
So when you need to get your operand value to calculate expression just use:
//Get operand_part = "Ahmed"
bool val = doc1[operand_part]

The simplest thing I could think of was to use a DataTable.
This should handle all "happy paths" correctly. Like it says in the comments you may need to do additional validation to explicitly error out on what you consider invalid input.
class Program
{
static void Main(string[] args)
{
// Unit test the EvaluateBinaryExpression method.
Console.Out.WriteLine(EvaluateBinaryExpression("true")); // true
Console.Out.WriteLine(EvaluateBinaryExpression("false")); // false
Console.Out.WriteLine(EvaluateBinaryExpression("true or false")); // true
Console.Out.WriteLine(EvaluateBinaryExpression("false or false")); // false
Console.Out.WriteLine(EvaluateBinaryExpression("true and false")); // false
Console.Out.WriteLine(EvaluateBinaryExpression("true and true")); // true
Console.Out.WriteLine(EvaluateBinaryExpression("true and not false")); // false
//
// This should give me Ahmed && love && ! Ali which will be true for doc1.
// TODO: get these values out of the table.
var doc1Results = new Dictionary<string, bool>()
{
{ "Ahmed", true },
{ "love", true },
{ "ali", false }
};
// Then just call it like this
Console.Out.WriteLine(EvaluateResults("Ahmed and love and not ali", doc1Results)); // true
}
public static bool EvaluateResults(string expression, IDictionary<string, bool> results)
{
// TODO validate input expression and results
// Make sure the expression is padded with whitespace for replacing.
var replaced = " " + expression + " ";
foreach (var kvp in results)
{
var search = " " + kvp.Key + " ";
var replacement = " " + kvp.Value.ToString() + " ";
// TODO make sure casing, etc. doesn't matter.
replaced = replaced.Replace(search, replacement);
}
return EvaluateBinaryExpression(replaced);
}
public static bool EvaluateBinaryExpression(string expression)
{
// TODO what if this throws an error?
var dt = new System.Data.DataTable();
dt.Columns.Add("Test", typeof(object));
dt.Rows.Add(new object[] { null });
dt.Columns[0].Expression = expression;
var value = dt.Rows[0][0] as bool?;
return value.Value;
}
}

Related

parse string with condition to boolean

I am looking for a way to get a boolean value from a string.
I have a string, stored in a database, that is a condition to be tested with.
suppose the string = "[number] < 10000"
In my code I then plan to replace [number] with a actual number so the string would become "100 < 10000".
Is there a simple way to evaluate this string to a boolean.
In this case it would be False.
Another example: "[number] > 100 && [number] < 1000"
If number would be 200, the evaluation would be True.
What if I add code to this condition, like "if ([number] > 100 && [number] < 1000) { return true;} else {return false;}"
If there a simple way to get the return value from this string with code?
You can make use of DynamicExpression class, it is available in this nuget package. It can evaluate expressions on the fly.
Below is an example,
var expression = System.Linq.Dynamic.DynamicExpression.ParseLambda(
new System.Linq.Expressions.ParameterExpression[] { },
typeof(bool),
"100 > 1000",
new object[] { });
var compiledDelegate = expression.Compile();
var result = compiledDelegate.DynamicInvoke(null);
The result will be false as the provided expression "100 > 1000" is false.
If you need more control, you have to create expressions depending upon dynamic strings representing logical operations. For example, I have taken your first case of "[number]<1000" You can try below:
static void Main(string[] args)
{
string code = "[number] < 10000".Replace("[number]", "100");
string[] operands = code.Split('<');
var comparison = Expression.LessThan(
Expression.Constant(operands[0]),
Expression.Constant(operands[1]));
var lambda = Expression.Lambda<Func<bool>>(comparison).Compile();
bool result = lambda();
}
Or Alternatively you can use existing libraries or nuget packages for doing same thing in a more simpler way (under hood they do same though):
string code = "[number] < 10000".Replace("[number]", "100");
func = ExpressionParser.Compile<Func<bool>>(code);
bool result = func()
It makes use of Lambda parser as shown here

validation rule for only numbers to be entered [duplicate]

If I have these strings:
"abc" = false
"123" = true
"ab2" = false
Is there a command, like IsNumeric() or something else, that can identify if a string is a valid number?
int n;
bool isNumeric = int.TryParse("123", out n);
Update As of C# 7:
var isNumeric = int.TryParse("123", out int n);
or if you don't need the number you can discard the out parameter
var isNumeric = int.TryParse("123", out _);
The var s can be replaced by their respective types!
This will return true if input is all numbers. Don't know if it's any better than TryParse, but it will work.
Regex.IsMatch(input, #"^\d+$")
If you just want to know if it has one or more numbers mixed in with characters, leave off the ^ + and $.
Regex.IsMatch(input, #"\d")
Edit:
Actually I think it is better than TryParse because a very long string could potentially overflow TryParse.
You can also use:
using System.Linq;
stringTest.All(char.IsDigit);
It will return true for all Numeric Digits (not float) and false if input string is any sort of alphanumeric.
Test case
Return value
Test result
"1234"
true
✅Pass
"1"
true
✅Pass
"0"
true
✅Pass
""
true
⚠️Fail (known edge case)
"12.34"
false
✅Pass
"+1234"
false
✅Pass
"-13"
false
✅Pass
"3E14"
false
✅Pass
"0x10"
false
✅Pass
Please note: stringTest should not be an empty string as this would pass the test of being numeric.
I've used this function several times:
public static bool IsNumeric(object Expression)
{
double retNum;
bool isNum = Double.TryParse(Convert.ToString(Expression), System.Globalization.NumberStyles.Any, System.Globalization.NumberFormatInfo.InvariantInfo, out retNum);
return isNum;
}
But you can also use;
bool b1 = Microsoft.VisualBasic.Information.IsNumeric("1"); //true
bool b2 = Microsoft.VisualBasic.Information.IsNumeric("1aa"); // false
From Benchmarking IsNumeric Options
(source: aspalliance.com)
(source: aspalliance.com)
This is probably the best option in C#.
If you want to know if the string contains a whole number (integer):
string someString;
// ...
int myInt;
bool isNumerical = int.TryParse(someString, out myInt);
The TryParse method will try to convert the string to a number (integer) and if it succeeds it will return true and place the corresponding number in myInt. If it can't, it returns false.
Solutions using the int.Parse(someString) alternative shown in other responses works, but it is much slower because throwing exceptions is very expensive. TryParse(...) was added to the C# language in version 2, and until then you didn't have a choice. Now you do: you should therefore avoid the Parse() alternative.
If you want to accept decimal numbers, the decimal class also has a .TryParse(...) method. Replace int with decimal in the above discussion, and the same principles apply.
You can always use the built in TryParse methods for many datatypes to see if the string in question will pass.
Example.
decimal myDec;
var Result = decimal.TryParse("123", out myDec);
Result would then = True
decimal myDec;
var Result = decimal.TryParse("abc", out myDec);
Result would then = False
In case you don't want to use int.Parse or double.Parse, you can roll your own with something like this:
public static class Extensions
{
public static bool IsNumeric(this string s)
{
foreach (char c in s)
{
if (!char.IsDigit(c) && c != '.')
{
return false;
}
}
return true;
}
}
If you want to catch a broader spectrum of numbers, à la PHP's is_numeric, you can use the following:
// From PHP documentation for is_numeric
// (http://php.net/manual/en/function.is-numeric.php)
// Finds whether the given variable is numeric.
// Numeric strings consist of optional sign, any number of digits, optional decimal part and optional
// exponential part. Thus +0123.45e6 is a valid numeric value.
// Hexadecimal (e.g. 0xf4c3b00c), Binary (e.g. 0b10100111001), Octal (e.g. 0777) notation is allowed too but
// only without sign, decimal and exponential part.
static readonly Regex _isNumericRegex =
new Regex( "^(" +
/*Hex*/ #"0x[0-9a-f]+" + "|" +
/*Bin*/ #"0b[01]+" + "|" +
/*Oct*/ #"0[0-7]*" + "|" +
/*Dec*/ #"((?!0)|[-+]|(?=0+\.))(\d*\.)?\d+(e\d+)?" +
")$" );
static bool IsNumeric( string value )
{
return _isNumericRegex.IsMatch( value );
}
Unit Test:
static void IsNumericTest()
{
string[] l_unitTests = new string[] {
"123", /* TRUE */
"abc", /* FALSE */
"12.3", /* TRUE */
"+12.3", /* TRUE */
"-12.3", /* TRUE */
"1.23e2", /* TRUE */
"-1e23", /* TRUE */
"1.2ef", /* FALSE */
"0x0", /* TRUE */
"0xfff", /* TRUE */
"0xf1f", /* TRUE */
"0xf1g", /* FALSE */
"0123", /* TRUE */
"0999", /* FALSE (not octal) */
"+0999", /* TRUE (forced decimal) */
"0b0101", /* TRUE */
"0b0102" /* FALSE */
};
foreach ( string l_unitTest in l_unitTests )
Console.WriteLine( l_unitTest + " => " + IsNumeric( l_unitTest ).ToString() );
Console.ReadKey( true );
}
Keep in mind that just because a value is numeric doesn't mean it can be converted to a numeric type. For example, "999999999999999999999999999999.9999999999" is a perfeclty valid numeric value, but it won't fit into a .NET numeric type (not one defined in the standard library, that is).
I know this is an old thread, but none of the answers really did it for me - either inefficient, or not encapsulated for easy reuse. I also wanted to ensure it returned false if the string was empty or null. TryParse returns true in this case (an empty string does not cause an error when parsing as a number). So, here's my string extension method:
public static class Extensions
{
/// <summary>
/// Returns true if string is numeric and not empty or null or whitespace.
/// Determines if string is numeric by parsing as Double
/// </summary>
/// <param name="str"></param>
/// <param name="style">Optional style - defaults to NumberStyles.Number (leading and trailing whitespace, leading and trailing sign, decimal point and thousands separator) </param>
/// <param name="culture">Optional CultureInfo - defaults to InvariantCulture</param>
/// <returns></returns>
public static bool IsNumeric(this string str, NumberStyles style = NumberStyles.Number,
CultureInfo culture = null)
{
double num;
if (culture == null) culture = CultureInfo.InvariantCulture;
return Double.TryParse(str, style, culture, out num) && !String.IsNullOrWhiteSpace(str);
}
}
Simple to use:
var mystring = "1234.56789";
var test = mystring.IsNumeric();
Or, if you want to test other types of number, you can specify the 'style'.
So, to convert a number with an Exponent, you could use:
var mystring = "5.2453232E6";
var test = mystring.IsNumeric(style: NumberStyles.AllowExponent);
Or to test a potential Hex string, you could use:
var mystring = "0xF67AB2";
var test = mystring.IsNumeric(style: NumberStyles.HexNumber)
The optional 'culture' parameter can be used in much the same way.
It is limited by not being able to convert strings that are too big to be contained in a double, but that is a limited requirement and I think if you are working with numbers larger than this, then you'll probably need additional specialised number handling functions anyway.
UPDATE of Kunal Noel Answer
stringTest.All(char.IsDigit);
// This returns true if all characters of the string are digits.
But, for this case we have that empty strings will pass that test, so, you can:
if (!string.IsNullOrEmpty(stringTest) && stringTest.All(char.IsDigit)){
// Do your logic here
}
You can use TryParse to determine if the string can be parsed into an integer.
int i;
bool bNum = int.TryParse(str, out i);
The boolean will tell you if it worked or not.
If you want to know if a string is a number, you could always try parsing it:
var numberString = "123";
int number;
int.TryParse(numberString , out number);
Note that TryParse returns a bool, which you can use to check if your parsing succeeded.
I guess this answer will just be lost in between all the other ones, but anyway, here goes.
I ended up on this question via Google because I wanted to check if a string was numeric so that I could just use double.Parse("123") instead of the TryParse() method.
Why? Because it's annoying to have to declare an out variable and check the result of TryParse() before you know if the parse failed or not. I want to use the ternary operator to check if the string is numerical and then just parse it in the first ternary expression or provide a default value in the second ternary expression.
Like this:
var doubleValue = IsNumeric(numberAsString) ? double.Parse(numberAsString) : 0;
It's just a lot cleaner than:
var doubleValue = 0;
if (double.TryParse(numberAsString, out doubleValue)) {
//whatever you want to do with doubleValue
}
I made a couple extension methods for these cases:
Extension method one
public static bool IsParseableAs<TInput>(this string value) {
var type = typeof(TInput);
var tryParseMethod = type.GetMethod("TryParse", BindingFlags.Static | BindingFlags.Public, Type.DefaultBinder,
new[] { typeof(string), type.MakeByRefType() }, null);
if (tryParseMethod == null) return false;
var arguments = new[] { value, Activator.CreateInstance(type) };
return (bool) tryParseMethod.Invoke(null, arguments);
}
Example:
"123".IsParseableAs<double>() ? double.Parse(sNumber) : 0;
Because IsParseableAs() tries to parse the string as the appropriate type instead of just checking if the string is "numeric" it should be pretty safe. And you can even use it for non numeric types that have a TryParse() method, like DateTime.
The method uses reflection and you end up calling the TryParse() method twice which, of course, isn't as efficient, but not everything has to be fully optimized, sometimes convenience is just more important.
This method can also be used to easily parse a list of numeric strings into a list of double or some other type with a default value without having to catch any exceptions:
var sNumbers = new[] {"10", "20", "30"};
var dValues = sNumbers.Select(s => s.IsParseableAs<double>() ? double.Parse(s) : 0);
Extension method two
public static TOutput ParseAs<TOutput>(this string value, TOutput defaultValue) {
var type = typeof(TOutput);
var tryParseMethod = type.GetMethod("TryParse", BindingFlags.Static | BindingFlags.Public, Type.DefaultBinder,
new[] { typeof(string), type.MakeByRefType() }, null);
if (tryParseMethod == null) return defaultValue;
var arguments = new object[] { value, null };
return ((bool) tryParseMethod.Invoke(null, arguments)) ? (TOutput) arguments[1] : defaultValue;
}
This extension method lets you parse a string as any type that has a TryParse() method and it also lets you specify a default value to return if the conversion fails.
This is better than using the ternary operator with the extension method above as it only does the conversion once. It still uses reflection though...
Examples:
"123".ParseAs<int>(10);
"abc".ParseAs<int>(25);
"123,78".ParseAs<double>(10);
"abc".ParseAs<double>(107.4);
"2014-10-28".ParseAs<DateTime>(DateTime.MinValue);
"monday".ParseAs<DateTime>(DateTime.MinValue);
Outputs:
123
25
123,78
107,4
28.10.2014 00:00:00
01.01.0001 00:00:00
If you want to check if a string is a number (I'm assuming it's a string since if it's a number, duh, you know it's one).
Without regex and
using Microsoft's code as much as possible
you could also do:
public static bool IsNumber(this string aNumber)
{
BigInteger temp_big_int;
var is_number = BigInteger.TryParse(aNumber, out temp_big_int);
return is_number;
}
This will take care of the usual nasties:
Minus (-) or Plus (+) in the beginning
contains decimal character BigIntegers won't parse numbers with decimal points. (So: BigInteger.Parse("3.3") will throw an exception, and TryParse for the same will return false)
no funny non-digits
covers cases where the number is bigger than the usual use of Double.TryParse
You'll have to add a reference to System.Numerics and have
using System.Numerics; on top of your class (well, the second is a bonus I guess :)
Double.TryParse
bool Double.TryParse(string s, out double result)
The best flexible solution with .net built-in function called- char.IsDigit. It works with unlimited long numbers. It will only return true if each character is a numeric number. I used it lot of times with no issues and much easily cleaner solution I ever found. I made a example method.Its ready to use. In addition I added validation for null and empty input. So the method is now totally bulletproof
public static bool IsNumeric(string strNumber)
{
if (string.IsNullOrEmpty(strNumber))
{
return false;
}
else
{
int numberOfChar = strNumber.Count();
if (numberOfChar > 0)
{
bool r = strNumber.All(char.IsDigit);
return r;
}
else
{
return false;
}
}
}
Try the regex define below
new Regex(#"^\d{4}").IsMatch("6") // false
new Regex(#"^\d{4}").IsMatch("68ab") // false
new Regex(#"^\d{4}").IsMatch("1111abcdefg")
new Regex(#"^\d+").IsMatch("6") // true (any length but at least one digit)
With c# 7 it you can inline the out variable:
if(int.TryParse(str, out int v))
{
}
Use these extension methods to clearly distinguish between a check if the string is numerical and if the string only contains 0-9 digits
public static class ExtensionMethods
{
/// <summary>
/// Returns true if string could represent a valid number, including decimals and local culture symbols
/// </summary>
public static bool IsNumeric(this string s)
{
decimal d;
return decimal.TryParse(s, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.CurrentCulture, out d);
}
/// <summary>
/// Returns true only if string is wholy comprised of numerical digits
/// </summary>
public static bool IsNumbersOnly(this string s)
{
if (s == null || s == string.Empty)
return false;
foreach (char c in s)
{
if (c < '0' || c > '9') // Avoid using .IsDigit or .IsNumeric as they will return true for other characters
return false;
}
return true;
}
}
public static bool IsNumeric(this string input)
{
int n;
if (!string.IsNullOrEmpty(input)) //.Replace('.',null).Replace(',',null)
{
foreach (var i in input)
{
if (!int.TryParse(i.ToString(), out n))
{
return false;
}
}
return true;
}
return false;
}
Regex rx = new Regex(#"^([1-9]\d*(\.)\d*|0?(\.)\d*[1-9]\d*|[1-9]\d*)$");
string text = "12.0";
var result = rx.IsMatch(text);
Console.WriteLine(result);
To check string is uint, ulong or contains only digits one .(dot) and digits
Sample inputs
123 => True
123.1 => True
0.123 => True
.123 => True
0.2 => True
3452.434.43=> False
2342f43.34 => False
svasad.324 => False
3215.afa => False
Hope this helps
string myString = "abc";
double num;
bool isNumber = double.TryParse(myString , out num);
if isNumber
{
//string is number
}
else
{
//string is not a number
}
Pull in a reference to Visual Basic in your project and use its Information.IsNumeric method such as shown below and be able to capture floats as well as integers unlike the answer above which only catches ints.
// Using Microsoft.VisualBasic;
var txt = "ABCDEFG";
if (Information.IsNumeric(txt))
Console.WriteLine ("Numeric");
IsNumeric("12.3"); // true
IsNumeric("1"); // true
IsNumeric("abc"); // false
All the Answers are Useful. But while searching for a solution where the Numeric value is 12 digits or more (in my case), then while debugging, I found the following solution useful :
double tempInt = 0;
bool result = double.TryParse("Your_12_Digit_Or_more_StringValue", out tempInt);
Th result variable will give you true or false.
Here is the C# method.
Int.TryParse Method (String, Int32)
bool is_number(string str, char delimiter = '.')
{
if(str.Length==0) //Empty
{
return false;
}
bool is_delimetered = false;
foreach (char c in str)
{
if ((c < '0' || c > '9') && (c != delimiter)) //ASCII table check. Not a digit && not delimeter
{
return false;
}
if (c == delimiter)
{
if (is_delimetered) //more than 1 delimiter
{
return false;
}
else //first time delimiter
{
is_delimetered = true;
}
}
}
return true;
}

how to compare 2 strings in c#

I have some string like this:
str1 = STA001, str2 = STA002, str3 = STA003
and have code to compare strings:
private bool IsSubstring(string strChild, string strParent)
{
if (!strParent.Contains(strChild))
{
return false;
}
else return true;
}
If I have strChild = STA001STA002 and strParent = STA001STA002STA003 then return true but when I enter strChild = STA001STA003 and check with strParent = STA001STA002STA003 then return false although STA001STA003 have contains in strParent. How can i resolve it?
What you're describing is not a substring. It is basically asking of two collections the question "is this one a subset of the other one?" This question is far easier to ask when the collection is a set such as HashSet<T> than when the collection is a big concatenated string.
This would be a much better way to write your code:
var setOne = new HashSet<string> { "STA001", "STA003" };
var setTwo = new HashSet<string> { "STA001", "STA002", "STA003" };
Console.WriteLine(setOne.IsSubsetOf(setTwo)); // True
Console.WriteLine(setTwo.IsSubsetOf(setOne)); // False
Or, if the STA00 part was just filler to make it make sense in the context of strings, then use ints directly:
var setOne = new HashSet<int> { 1, 3 };
var setTwo = new HashSet<int> { 1, 2, 3 };
Console.WriteLine(setOne.IsSubsetOf(setTwo)); // True
Console.WriteLine(setTwo.IsSubsetOf(setOne)); // False
The Contains method only looks for an exact match, it doesn't look for parts of the string.
Divide the child string into its parts, and look for each part in the parent string:
private bool IsSubstring(string child, string parent) {
for (int i = 0; i < child.Length; i+= 6) {
if (!parent.Contains(child.Substring(i, 6))) {
return false;
}
}
return true;
}
You should however consider it cross-part matches are possible, and if that is an issue. E.g. looking for "1STA00" in "STA001STA002". If that would be a problem, then you should divide the parent string similarly, and only make direct comparisons between the parts, not using the Contains method.
Note: Using hungarian notation for the data type of variables is not encouraged in C#.
This may be a bit on the overkill side, but it might be incredibly beneficial.
private static bool ContainedWord(string input, string phrase)
{
var pattern = String.Format(#"\b({0})", phrase);
var result = Regex.Match(input, pattern);
if(string.Compare(result, phrase) == 0)
return true;
return false;
}
If the expression finds a match, then compare the result to your phrase. If they're zero, it matched. I may be misunderstanding your intent.

Convert enum to string having a space

I have an enum which I want to present as string using special way:
public enum FailureDescription
{
MemoryFailure,
Fragmentation,
SegmentationFault
}
I want to print the value of that enum as following : FailureDescription.MemoryFailure.ToString() - > Memory Failure
Can I do that ? How? Implement ToString?
You can write own extension method:
public static string ToFormattedText(this MyEnum value)
{
var stringVal = value.ToString();
var bld = new StringBuilder();
for (var i = 0; i < stringVal.Length; i++)
{
if (char.IsUpper(stringVal[i]))
{
bld.Append(" ");
}
bld.Append(stringVal[i]);
}
return bld.ToString();
}
If you want method available for all enums, just replace MyEnum with Enum.
Usage:
var test = MyEnum.SampleName.ToFormattedText();
Consider caching - building string everytime could not be efficient.
Use the Description attribute to decortate your enumeration values. I'd suggest adding a resx file for resources so that you can localise more easily. If you hardcoded "Memory Failure", it becomes more work to be able to change that to another language (as Hans Passant mentioned in the comments).
public enum FailureDescription
{
[Description("Memory Failure")] //hardcoding English
MemoryFailure,
[Description(MyStringsResourceFile.FragmentationDescription)] //reading from a resx file makes localisation easier
Fragmentation,
[Description(MyStringsResourceFile.SegmentationFaultDescription)]
SegmentationFault
}
You can then create a method, or extension method (as shown) to read the Description value.
public static class Extensions
{
public static string GetDescription(this Enum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(
typeof(DescriptionAttribute),
false);
if (attributes != null &&
attributes.Length > 0)
return attributes[0].Description;
else
return value.ToString();
}
}
Then call the method like so
Console.WriteLine(FailureDescription.MemoryFailure.GetDescription());
This extension method will do it for you:
public static string ToFormattedText(this FailureDescription value)
{
return new string(value.ToString()
.SelectMany(c =>
char.IsUpper(c)
? new [] { ' ', c }
: new [] { c })
.ToArray()).Trim();
}
You also can use a simple regex & linq mixture to extract and concatenate the words:
var withSpaces =
Regex
.Matches(
FailureDescription.MemoryFailureTest.ToString(),
#"([A-Z][a-z]+)(?=[A-Z]|$)")
.Cast<Match>()
.Select(m => m.Groups[1].Value)
.Aggregate((str, next) => (str = str + " " + next));
DEMO at ideone.com
where:
([A-Z][a-z]+)(?=[A-Z]|$)
matches words that begin with upper-case letters until the next upper-case letter or the end of string is found: DEMO at regex101
.Select(m => m.Groups[1].Value)
selects the matched values from the group 1
.Aggregate((str, next) => (str = str + " " + next));
concatenates words and inserts a space between them.
Here is one of the utilities I've been using. #HansPassant in his comment raised a good point about localizing.
This code takes Resource files into consideration. In the attribute with two params first param is the Key in Resource file, where as the second param is the namespace for the resource.
Checkout the git repo https://github.com/seanpaul/EnumExtensions
public enum TestEnum
{
//You can pass what ever string value you want
[StringValue("From Attribute")]
FromAttribute = 1,
//If localizing, you can use resource files
//First param is Key in resource file, second is namespace for Resources.
[StringValue("Test", "EnumExtensions.Tests.Resources")]
FromResource = 2,
//or don't mention anything and it will use built-in ToString
BuiltInToString = 3
}
[Test ()]
public void GetValueFromAttribute ()
{
var testEnum = TestEnum.FromAttribute;
Assert.AreEqual ("From Attribute", testEnum.GetStringValue ());
}
[Test ()]
public void GetValueFromResourceFile ()
{
var testEnum = TestEnum.FromResource;
Assert.AreEqual ("From Resource File", testEnum.GetStringValue ());
}
An elegant solution following the DRY and KISS principles would be using Humanizer:
"Memory Failure".DehumanizeTo<EnumUnderTest>(); // Returns FailureDescription.MemoryFailure.
"Fragmentation".DehumanizeTo<EnumUnderTest>(); // Returns FailureDescription.Fragmentation.
"Segmentation Fault".DehumanizeTo<EnumUnderTest>(); // Returns FailureDescription.SegmentationFault.

How would I manually (no third-party libraries) evaluate an equation from a string in C#? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
C# - Parse Math Expression
C#, User defined formula
The equation will only use addition, subtraction, multiplication, division operators and will not use brackets. I didn't think it would be so difficult, but I've been thinking about it for hours while trying different things and writing different ideas out.
I thought there might be some way by splitting the string on each of those characters and doing something with the output or by looping through the string character by character and coming up with something, but I'm not clever enough I guess.
Anyways, I'd love to hear other peoples' ideas because I'm stumped. I don't want to use a third-party library of some kind which is what everybody has suggested in old threads that I've been looking at.
For such simple equations it could be implemented with a split and two loops.
For a string like this: "4+5*6/2-8"
Split on operators, keeping them in the result:
"4", "+", "5", "*", "6", "/", "2", "-", "8"
Loop though the operators and calculate multiplication and division, putting the result back in the list:
"4", "+", "30", "/", "2", "-", "8"
"4", "+", "15", "-", "8"
Loop through the operators again and calculate addition and subtraction this time:
"19", "-", "8"
"11"
The easiest way to do that is take advantage of the JIT compiler to evaluate a calculation. Thant's what it's there for. you can even pass in code like Math.Acos(4) to the expression, or "create" a function Acos in the object you are using to allow users not to have to worry about the Math. prefix.
string code = string.Format // Note: Use "{{" to denote a single "{"
(
"public static class Func{{ public static Acos(double d) { return Math.ACos(d); }
public static int func(){{ return {0};}}}}", expression
);
Also you can include additional namespaces if you need any other functions, but Without any extra functions the code is like this:
using System;
using System.Reflection;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
class Program
{
static void Main()
{
TestExpression("2+1-(3*2)+8/2");
TestExpression("1*2*3*4*5*6");
TestExpression("Invalid expression");
}
static void TestExpression(string expression)
{
try
{
int result = EvaluateExpression(expression);
Console.WriteLine("'" + expression + "' = " + result);
}
catch (Exception)
{
Console.WriteLine("Expression is invalid: '" + expression + "'");
}
}
public static int EvaluateExpression(string expression)
{
string code = string.Format // Note: Use "{{" to denote a single "{"
(
"public static class Func{{ public static int func(){{ return {0};}}}}", expression
);
CompilerResults compilerResults = CompileScript(code);
if (compilerResults.Errors.HasErrors)
{
throw new InvalidOperationException("Expression has a syntax error.");
}
Assembly assembly = compilerResults.CompiledAssembly;
MethodInfo method = assembly.GetType("Func").GetMethod("func");
return (int)method.Invoke(null, null);
}
public static CompilerResults CompileScript(string source)
{
CompilerParameters parms = new CompilerParameters();
parms.GenerateExecutable = false;
parms.GenerateInMemory = true;
parms.IncludeDebugInformation = false;
CodeDomProvider compiler = CSharpCodeProvider.CreateProvider("CSharp");
return compiler.CompileAssemblyFromSource(parms, source);
}
}
The answer was copied from http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/abff98e3-93fe-44fa-bfd4-fcfe297dbc43/ for I did not like writing the code myself and thanks to Matthew Watson
I didn't have to.
I prefer Recursive Descent Parsing, as stated in a comment. Here is a very quick partial adaptation in C# of the C example found in the linked Wikipedia article.
I find a simple recursive-descent easier to read than the shunting yard method (notice how recursive descent functions closely match EBNF non-terminal definitions) and more extensible. The following can be trivially adapted to allow for parenthesis or "external" functions.
A more robust implementation would actually support symbol classes and handle invalid grammars more gracefully; once again, trivial to add in such a recursive descent parsing setup. Tokening the input (read: splitting the string and converting numbers to double) is left as an exercise to the reader.
class RecDec {
St x; // ugly shared state, it's a quick example
public double eval (params object[] tokens) {
x = new St(tokens);
return expression();
}
double expression() {
double res = term();
string accepted;
while ((accepted = x.acceptOp(new [] {"+", "-"})) != null) {
res = accepted == "+"
? res + term()
: res - term();
}
return res;
}
double term() {
double res = factor();
string accepted;
while ((accepted = x.acceptOp(new [] {"*", "/"})) != null) {
res = accepted == "*"
? res * factor();
: res / factor();
}
return res;
}
double factor() {
var val = x.acceptVal();
if (val == null) {
throw new Exception(x.ToString());
}
return (double)val;
}
}
The "state" / token-feader class:
class St {
IEnumerable<object> src;
public St (IEnumerable<object> src) {
this.src = src;
}
public object acceptVal () {
var first = src.FirstOrDefault();
if (first is double) {
src = src.Skip(1);
return first;
} else {
return null;
}
}
public string acceptOp (params string[] syms) {
var first = src.FirstOrDefault();
if (syms.Contains(first)) {
src = src.Skip(1);
return (string)first;
} else {
return null;
}
}
public override string ToString () {
return "[" + string.Join(",", src.ToArray()) + "]";
}
}
And usage (Dump is a LINQPad extension method, use eval return value as applicable):
void Main()
{
var rd = new RecDec();
// Use results - i.e. Remove Dump - if not on LINQPad
rd.eval(1d, "+", 2d).Dump();
rd.eval(2d, "*", 1d, "+", 2d, "*", 9d, "/", 4d).Dump();
}

Categories

Resources