Convert ISpreadsheetName to CellRange - c#

I am using Telerik controls to manipulate Excel workbook. Here I am accessing Workbook Named Ranges the following way:
ISpreadsheetName Name_Level2;
workbook.Names.TryGetSpreadsheetName("IP_Stredisko_Level2_End", out Name_Level2);
Afterwards the result I get from Name_Level2.RefersTo is a plain string such as following:"=Sheet1!$A$5"
I am trying to convert this reference to a proper CellRange object, which can be used when manipulating specific cell values.
Is there any built-in method how to do the conversion, or I have to write the method myself?

In the end, as I did not manage to find anything in the documentation, I have created my own converter. Hope it helps someone.
private static CellRange GetCellRange(string sReference, string sWorkbookName)
{
if (string.IsNullOrWhiteSpace(sReference))
return null;
sReference = sReference.Replace("=" + sWorkbookName + "!", string.Empty);
sReference = sReference.Replace("$", string.Empty);
string sColumn = string.Empty;
string sRow = string.Empty;
for (int i = 0; i < sReference.Length; i++)
{
if (char.IsDigit(sReference[i]))
sRow += sReference[i];
else
sColumn += sReference[i];
}
int iRow = Convert.ToInt16(sRow) -1;
int iCol = GetColIndex(sColumn);
if (iRow >= 0 && iCol >= 0)
return new CellRange(iRow, iCol,iRow,iCol);
else
return null;
}
//accepting up to AZ
private static int GetColIndex(string sReference)
{
if (sReference.Length > 1)
{
int x = (int)sReference[1];
return 25 + ((int)sReference[1] - 65);//25 as 26 letters in alphabet -1 (start from 0)
}
return (int)sReference[0] - 65;
}

Related

Check the difference

Hi I'm building a program that will look at files at have been placed into SVN and show what files have been changed in each commit.
As i'm only wanting to show the file path. if the path is that same I only want to show the difference.
example:
First file path is:
/GEM4/trunk/src/Tools/TaxMarkerUpdateTool/Tax Marker Ripper v1/DataModifier.cs
Second file path is:
/GEM4/trunk/src/Tools/TaxMarkerUpdateTool/Tax Marker Ripper v1/Tax Marker Ripper v1.csproj
What I'd like to do is substring at the point of difference.
So in this case:
/GEM4/trunk/src/Tools/TaxMarkerUpdateTool/Tax Marker Ripper v1/
would be substringed
I hope this helps:
public string GetString(string Path1, string Path2)
{
//Split and Put everything between / in the arrays
string[] Arr_String1 = Path1.Split('/');
string[] Arr_String2 = Path2.Split('/');
string Result = "";
for (int i = 0; i <= Arr_String1.Length; i++)
{
if (Arr_String1[i] == Arr_String2[i])
{
//Puts the Content that is the same in an Result string with /
Result += Arr_String1[i] + '/';
}
else
break;
}
// If Path is identical he would add a / which we dont want
if (Result.Contains('.'))
{
Result = Result.TrimEnd('/');
}
return Result;
}
You can do this pretty easily with a loop. Basically:
public String FindCommonStart(string a, string b)
{
int length = Math.Min(a.Length, b.Length);
var common = String.Empty;
for (int i = 0; i < length; i++)
{
if (a[i] == b[i])
{
common += a[i];
}
else
{
break;
}
}
return common;
}
Something like this:
public string GetCommonStart(string a, string b)
{
if ((a == null) || (b == null))
throw new ArgumentNullException();
int Delim = 0;
int I = 0;
while ((I < a.Length) && (I < b.Length) && (a[I] == b[I]))
{
if (a[I++] == Path.AltDirectorySeparatorChar) // or Path.DirectorySeparatorChar
Delim = I;
}
return a.Substring(0, Delim);
}
Keep in mind, that this code is case-sensitive (and paths in windows in general are not).

Splitting CSV files with commas in the values [duplicate]

How to split the CSV file in c sharp? And how to display this?
I've been using the TextFieldParser Class in the Microsoft.VisualBasic.FileIO namespace for a C# project I'm working on. It will handle complications such as embedded commas or fields that are enclosed in quotes etc. It returns a string[] and, in addition to CSV files, can also be used for parsing just about any type of structured text file.
Display where? About splitting, the best way is to use a good library to that effect.
This library is pretty good, I can recommend it heartily.
The problems using naïve methods is that the usually fail, there are tons of considerations without even thinking about performance:
What if the text contains commas
Support for the many existing formats (separated by semicolon, or text surrounded by quotes, or single quotes, etc.)
and many others
Import Micorosoft.VisualBasic as a reference (I know, its not that bad) and use Microsoft.VisualBasic.FileIO.TextFieldParser - this handles CSV files very well, and can be used in any .Net language.
read the file one line at a time, then ...
foreach (String line in line.Split(new char[] { ',' }))
Console.WriteLine(line);
This is a CSV parser I use on occasion.
Usage: (dgvMyView is a datagrid type.)
CSVReader reader = new CSVReader("C:\MyFile.txt");
reader.DisplayResults(dgvMyView);
Class:
using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Forms;
public class CSVReader
{
private const string ESCAPE_SPLIT_REGEX = "({1}[^{1}]*{1})*(?<Separator>{0})({1}[^{1}]*{1})*";
private string[] FieldNames;
private List<string[]> Records;
private int ReadIndex;
public CSVReader(string File)
{
Records = new List<string[]>();
string[] Record = null;
StreamReader Reader = new StreamReader(File);
int Index = 0;
bool BlankRecord = true;
FieldNames = GetEscapedSVs(Reader.ReadLine());
while (!Reader.EndOfStream)
{
Record = GetEscapedSVs(Reader.ReadLine());
BlankRecord = true;
for (Index = 0; Index <= Record.Length - 1; Index++)
{
if (!string.IsNullOrEmpty(Record[Index])) BlankRecord = false;
}
if (!BlankRecord) Records.Add(Record);
}
ReadIndex = -1;
Reader.Close();
}
private string[] GetEscapedSVs(string Data)
{
return GetEscapedSVs(Data, ",", "\"");
}
private string[] GetEscapedSVs(string Data, string Separator, string Escape)
{
string[] Result = null;
int Index = 0;
int PriorMatchIndex = 0;
MatchCollection Matches = Regex.Matches(Data, string.Format(ESCAPE_SPLIT_REGEX, Separator, Escape));
Result = new string[Matches.Count];
for (Index = 0; Index <= Result.Length - 2; Index++)
{
Result[Index] = Data.Substring(PriorMatchIndex, Matches[Index].Groups["Separator"].Index - PriorMatchIndex);
PriorMatchIndex = Matches[Index].Groups["Separator"].Index + Separator.Length;
}
Result[Result.Length - 1] = Data.Substring(PriorMatchIndex);
for (Index = 0; Index <= Result.Length - 1; Index++)
{
if (Regex.IsMatch(Result[Index], string.Format("^{0}[^{0}].*[^{0}]{0}$", Escape))) Result[Index] = Result[Index].Substring(1, Result[Index].Length - 2);
Result[Index] = Result[Index].Replace(Escape + Escape, Escape);
if (Result[Index] == null) Result[Index] = "";
}
return Result;
}
public int FieldCount
{
get { return FieldNames.Length; }
}
public string GetString(int Index)
{
return Records[ReadIndex][Index];
}
public string GetName(int Index)
{
return FieldNames[Index];
}
public bool Read()
{
ReadIndex = ReadIndex + 1;
return ReadIndex < Records.Count;
}
public void DisplayResults(DataGridView DataView)
{
DataGridViewColumn col = default(DataGridViewColumn);
DataGridViewRow row = default(DataGridViewRow);
DataGridViewCell cell = default(DataGridViewCell);
DataGridViewColumnHeaderCell header = default(DataGridViewColumnHeaderCell);
int Index = 0;
ReadIndex = -1;
DataView.Rows.Clear();
DataView.Columns.Clear();
for (Index = 0; Index <= FieldCount - 1; Index++)
{
col = new DataGridViewColumn();
col.CellTemplate = new DataGridViewTextBoxCell();
header = new DataGridViewColumnHeaderCell();
header.Value = GetName(Index);
col.HeaderCell = header;
DataView.Columns.Add(col);
}
while (Read())
{
row = new DataGridViewRow();
for (Index = 0; Index <= FieldCount - 1; Index++)
{
cell = new DataGridViewTextBoxCell();
cell.Value = GetString(Index).ToString();
row.Cells.Add(cell);
}
DataView.Rows.Add(row);
}
}
}
I had got the result for my query. its like simple like i had read a file using io.file. and all the text are stored into a string. After that i splitted with a seperator. The code is shown below.
using System;
using System.Collections.Generic;
using System.Text;
namespace CSV
{
class Program
{
static void Main(string[] args)
{
string csv = "user1, user2, user3,user4,user5";
string[] split = csv.Split(new char[] {',',' '});
foreach(string s in split)
{
if (s.Trim() != "")
Console.WriteLine(s);
}
Console.ReadLine();
}
}
}
The following function takes a line from a CSV file and splits it into a List<string>.
Arguments:
string line = the line to split
string textQualifier = what (if any) text qualifier (i.e. "" or "\"" or "'")
char delim = the field delimiter (i.e. ',' or ';' or '|' or '\t')
int colCount = the expected number of fields (0 means don't check)
Example usage:
List<string> fields = SplitLine(line, "\"", ',', 5);
// or
List<string> fields = SplitLine(line, "'", '|', 10);
// or
List<string> fields = SplitLine(line, "", '\t', 0);
Function:
private List<string> SplitLine(string line, string textQualifier, char delim, int colCount)
{
List<string> fields = new List<string>();
string origLine = line;
char textQual = '"';
bool hasTextQual = false;
if (!String.IsNullOrEmpty(textQualifier))
{
hasTextQual = true;
textQual = textQualifier[0];
}
if (hasTextQual)
{
while (!String.IsNullOrEmpty(line))
{
if (line[0] == textQual) // field is text qualified so look for next unqualified delimiter
{
int fieldLen = 1;
while (true)
{
if (line.Length == 2) // must be final field (zero length)
{
fieldLen = 2;
break;
}
else if (fieldLen + 1 >= line.Length) // must be final field
{
fieldLen += 1;
break;
}
else if (line[fieldLen] == textQual && line[fieldLen + 1] == textQual) // escaped text qualifier
{
fieldLen += 2;
}
else if (line[fieldLen] == textQual && line[fieldLen + 1] == delim) // must be end of field
{
fieldLen += 1;
break;
}
else // not a delimiter
{
fieldLen += 1;
}
}
string escapedQual = textQual.ToString() + textQual.ToString();
fields.Add(line.Substring(1, fieldLen - 2).Replace(escapedQual, textQual.ToString())); // replace escaped qualifiers
if (line.Length >= fieldLen + 1)
{
line = line.Substring(fieldLen + 1);
if (line == "") // blank final field
{
fields.Add("");
}
}
else
{
line = "";
}
}
else // field is not text qualified
{
int fieldLen = line.IndexOf(delim);
if (fieldLen != -1) // check next delimiter position
{
fields.Add(line.Substring(0, fieldLen));
line = line.Substring(fieldLen + 1);
if (line == "") // final field must be blank
{
fields.Add("");
}
}
else // must be last field
{
fields.Add(line);
line = "";
}
}
}
}
else // if there is no text qualifier, then use existing split function
{
fields.AddRange(line.Split(delim));
}
if (colCount > 0 && colCount != fields.Count) // count doesn't match expected so throw exception
{
throw new Exception("Field count was:" + fields.Count.ToString() + ", expected:" + colCount.ToString() + ". Line:" + origLine);
}
return fields;
}
Problem: Convert a comma separated string into an array where commas in "quoted strings,,," should not be considered as separators but as part of an entry
Input:
String: First,"Second","Even,With,Commas",,Normal,"Sentence,with ""different"" problems",3,4,5
Output:
String-Array: ['First','Second','Even,With,Commas','','Normal','Sentence,with "different" problems','3','4','5']
Code:
string sLine;
sLine = "First,\"Second\",\"Even,With,Commas\",,Normal,\"Sentence,with \"\"different\"\" problems\",3,4,5";
// 1. Split line by separator; do not split if separator is within quotes
string Separator = ",";
string Escape = '"'.ToString();
MatchCollection Matches = Regex.Matches(sLine,
string.Format("({1}[^{1}]*{1})*(?<Separator>{0})({1}[^{1}]*{1})*", Separator, Escape));
string[] asColumns = new string[Matches.Count + 1];
int PriorMatchIndex = 0;
for (int Index = 0; Index <= asColumns.Length - 2; Index++)
{
asColumns[Index] = sLine.Substring(PriorMatchIndex, Matches[Index].Groups["Separator"].Index - PriorMatchIndex);
PriorMatchIndex = Matches[Index].Groups["Separator"].Index + Separator.Length;
}
asColumns[asColumns.Length - 1] = sLine.Substring(PriorMatchIndex);
// 2. Remove quotes
for (int Index = 0; Index <= asColumns.Length - 1; Index++)
{
if (Regex.IsMatch(asColumns[Index], string.Format("^{0}[^{0}].*[^{0}]{0}$", Escape))) // If "Text" is sourrounded by quotes (but ignore double quotes => "Leave ""inside"" quotes")
{
asColumns[Index] = asColumns[Index].Substring(1, asColumns[Index].Length - 2); // "Text" => Text
}
asColumns[Index] = asColumns[Index].Replace(Escape + Escape, Escape); // Remove double quotes ('My ""special"" text' => 'My "special" text')
if (asColumns[Index] == null) asColumns[Index] = "";
}
The output array is asColumns

c# Add Specific columns from a TEXT file to DataGridView

Hello Everyone,
As shown in the above image I want to add the decimal numbers column wise from a text file to datagrid control.
Following is my code snippet
List<string> str = new List<string>();
String st = "";
int k = 0;
string[] s ;
//Path to write contents to text file
string filename = #"E:\Vivek\contentcopy\clientlist.txt";
Form.CheckForIllegalCrossThreadCalls = false;
OpenFileDialog ofd = new OpenFileDialog();
ofd.FileName = "";
ofd.ShowDialog();
st = ofd.FileName;
if (string.IsNullOrEmpty(ofd.FileName))
return;
string Name = "", No1 = "",No2="";
string[] lines = File.ReadAllLines(st).Where(sw => !string.IsNullOrWhiteSpace(sw)).ToArray();
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].Contains("VENTURA SECURITIES LIMITED (NSE F&O)")) continue;
if (lines[i].Contains("ALL EXCHANGES DERIVATIVES CLIENTWISE STATEMENT AS ON 16-05-2012")) continue;
if (lines[i].Contains("-------------------------------------------------------")) continue;
s = lines[i].Split(' ');
if (s[0] == "PARTY" || s[0] == "") continue;
int z;
Name = "";
for (z = 1; z < s.Length; z++)
{
if (s[z] == "") continue;
if (s[z].Contains('.'))
{
No1+=s[z]+" ";
No2 = No1 + " ";
}
else
{
Name += s[z];
str.Add(s[0]+" "+Name);
}
}
dataGridView1.Rows.Add();
dataGridView1.Rows[k].Cells[0].Value = s[0];
dataGridView1.Rows[k].Cells[1].Value = Name;
dataGridView1.Rows[k].Cells[2].Value = No1;
dataGridView1.Rows[k].Cells[3].Value = No2;
k++;
}
File.WriteAllLines(filename, str);
dataGridView1.ReadOnly = true;
}
The line No1=s[z] directly takes the last column values ie 46,123.19 and so on.I want to fetch each column from the text file and store it in a string variable and then assign it to the datagrid view
I hope my doubt is clear.If not please let me know
Here is the simplest Solution:
Add a DataGrid View to Form and add a Button:
private void button1_Click(object sender, EventArgs e)
{
ReadAndFileter();
}
private void ReadAndFileter()
{
try
{
using(System.IO.StreamReader reader = new System.IO.StreamReader("file.txt"))
{
string line;
string []array;
int rowcount= 0;
decimal number;
string[] separators = { "\t", " " };
int columnCount = 0;
while ((line = reader.ReadLine()) != null)
{
array = line.Split(separators, StringSplitOptions.RemoveEmptyEntries);
dataGridView1.Rows.Add();
foreach (string str in array)
{
if (Decimal.TryParse(str,out number))
{
dataGridView1.Rows[rowcount].Cells[columnCount++].Value = number;
}
}
rowcount++;
columnCount = 0;
}
}
}
catch (Exception ex)
{
}
}
The File Contents are:
Abc 20.122 69.33 0.00 693.25 0.00
def 36.20 96.20 1.15 69.56 8.96
And the final output:
Lets say, you have for lines in your test file, then u need to do following things:
Use StreamReader.ReadLine(), to read one line at time.
Spilt the line using split(' ') and store it in a array
Remove all the empty ones from the array
Now at index 2,3,4,5,6 of the resulting array will have the string equivalent of the decimal numbers.
Repeat this for each StreamReader.ReadLine()
Hope this will help.
Your problem is that you are overwriting No1 every time you read a string, which explains why you only get the last value. What you could do is either;
Append the string:
No1 += s[z] + " ";
Which will put all the values behind eachother, seperated by a whitespace.
Or, you could make a List<String> and add each value to the list, meaning you have them stored seperated:
List<String> values = new List<String>();
foreach(...)
{
if (s[z] == "") continue;
if (s[z].Contains('.'))
{
values.Add(s[z])
}
else
{
Name += s[z];
str.Add(s[0] + " " + Name);
}
}
You can thereafter loop through the list and add each value to a row. Considering your code piece;
int i = 2;
foreach(string value in values)
{
dataGridView1.Rows[k].Cells[i].Value = value;
i++;
}
This should work.
Hope this helps.
Here is edited code: but for future I must suggest to give a try at least..
private void ReadAndFileter1()
{
try
{
using (System.IO.StreamReader reader = new System.IO.StreamReader("file.txt"))
{
string line;
string[] array;
int rowcount = 0;
decimal number;
string[] separators = { "\t", " " };
int columnCount = 1;
string[] lines = File.ReadAllLines("file.txt");
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].Contains("VENTURA SECURITIES LIMITED (NSE F&O)")) continue;
if (lines[i].Contains("ALL EXCHANGES DERIVATIVES CLIENTWISE STATEMENT AS ON 16-05-2012")) continue;
if (lines[i].Contains("-------------------------------------------------------")) continue;
array = lines[i].Split(separators, StringSplitOptions.RemoveEmptyEntries);
if (array[0] == "PARTY" || array[0] == "") continue;
dataGridView1.Rows.Add();
foreach (string str in array)
{
if (Decimal.TryParse(str, out number))
{
dataGridView1.Rows[rowcount].Cells[columnCount++].Value = number;
}
}
dataGridView1.Rows[rowcount].Cells[0].Value = array[0];
rowcount++;
columnCount = 1;
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
Here it is:
static void Main(string[] args)
{
Decimal result;
string[] splitchar = new string[]{" "};
using(StreamReader reader = new StreamReader(#"C:\Users\Dell\Desktop\input.txt"))
{
while(!reader.EndOfStream)
{
string[] splittedArray = reader.ReadLine().Split(splitchar, StringSplitOptions.RemoveEmptyEntries).Where(x => Decimal.TryParse(x, out result)).ToArray();
// put your code here to get insert the values in datagrid
}
}
}

How to disable formula error check in Excel programmatically

I try to set formula to Excel cell in C# in several steps, however during the process I set it, it seems Excel throws exception due to invalid formula. I wonder how can I turn off the formula error check in C#. thanks
Edit
The formula is too long, longer than 255 characters.
So I can't set formula in one step.
Have to set a short formula, then replace see http://netoffice.codeplex.com/discussions/402947
see code below
but I get an error in rng.Formula = onePart;
where Constants.CUT_LENGTH = 253, Constants.MAX_FORMULA_LENGTH = 255, Separator = "||"
I try to set EvaluateToError to false, still get an error
XLApp.ErrorCheckingOptions.InconsistentFormula = false;
XLApp.ErrorCheckingOptions.EvaluateToError = false;
SetFormula(rangeFunction, formula);
public static void SetFormula(Range rng, string origFormula)
{
int i = 0;
foreach (var onePart in CutStringIntoSubstrings(origFormula))
{
if(i==0)
{
rng.Formula = onePart;
i++;
}
else
{
rng.Replace(Constants.Separator, onePart);
}
}
}
public static IEnumerable<string> CutStringIntoSubstrings(string origFormula)
{
if (origFormula == null) yield return string.Empty;
if (string.IsNullOrEmpty(origFormula)) yield return string.Empty;
if (origFormula.Length <= Constants.MAX_FORMULA_LENGTH) yield return origFormula;
int startIdx = 0;
int endIdx = startIdx + Constants.CUT_LENGTH;
while(endIdx < origFormula.Length)
{
var substr = origFormula.Substring(startIdx, Constants.CUT_LENGTH);
if(startIdx + Constants.CUT_LENGTH < origFormula.Length)
{
substr += Constants.Separator;
}
yield return substr;
startIdx += Constants.CUT_LENGTH;
endIdx = startIdx + Constants.CUT_LENGTH;
}
if (startIdx < origFormula.Length) yield return origFormula.Substring(startIdx);
}
You can refer to this article
http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.errorcheckingoptions_members.aspx
http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.error.ignore(v=office.11).aspx
May b it might help :)

Evaluating string "3*(4+2)" yield int 18 [duplicate]

This question already has answers here:
Is it possible to compile and execute new code at runtime in .NET?
(15 answers)
Closed 9 years ago.
Is there a function the .NET framework that can evaluate a numeric expression contained in a string and return the result? F.e.:
string mystring = "3*(2+4)";
int result = EvaluateExpression(mystring);
Console.Writeln(result); // Outputs 18
Is there a standard framework function that you can replace my EvaluateExpression method with?
If you want to evaluate a string expression use the below code snippet.
using System.Data;
DataTable dt = new DataTable();
var v = dt.Compute("3 * (2+4)","");
Using the compiler to do implies memory leaks as the generated assemblies are loaded and never released. It's also less performant than using a real expression interpreter. For this purpose you can use Ncalc which is an open-source framework with this solely intent. You can also define your own variables and custom functions if the ones already included aren't enough.
Example:
Expression e = new Expression("2 + 3 * 5");
Debug.Assert(17 == e.Evaluate());
Try this:
static double Evaluate(string expression) {
var loDataTable = new DataTable();
var loDataColumn = new DataColumn("Eval", typeof (double), expression);
loDataTable.Columns.Add(loDataColumn);
loDataTable.Rows.Add(0);
return (double) (loDataTable.Rows[0]["Eval"]);
}
You could look at "XpathNavigator.Evaluate" I have used this to process mathematical expressions for my GridView and it works fine for me.
Here is the code I used for my program:
public static double Evaluate(string expression)
{
return (double)new System.Xml.XPath.XPathDocument
(new StringReader("<r/>")).CreateNavigator().Evaluate
(string.Format("number({0})", new
System.Text.RegularExpressions.Regex(#"([\+\-\*])")
.Replace(expression, " ${1} ")
.Replace("/", " div ")
.Replace("%", " mod ")));
}
This is a simple Expression Evaluator using Stacks
public class MathEvaluator
{
public static void Run()
{
Eval("(1+2)");
Eval("5*4/2");
Eval("((3+5)-6)");
}
public static void Eval(string input)
{
var ans = Evaluate(input);
Console.WriteLine(input + " = " + ans);
}
public static double Evaluate(String input)
{
String expr = "(" + input + ")";
Stack<String> ops = new Stack<String>();
Stack<Double> vals = new Stack<Double>();
for (int i = 0; i < expr.Length; i++)
{
String s = expr.Substring(i, 1);
if (s.Equals("(")){}
else if (s.Equals("+")) ops.Push(s);
else if (s.Equals("-")) ops.Push(s);
else if (s.Equals("*")) ops.Push(s);
else if (s.Equals("/")) ops.Push(s);
else if (s.Equals("sqrt")) ops.Push(s);
else if (s.Equals(")"))
{
int count = ops.Count;
while (count > 0)
{
String op = ops.Pop();
double v = vals.Pop();
if (op.Equals("+")) v = vals.Pop() + v;
else if (op.Equals("-")) v = vals.Pop() - v;
else if (op.Equals("*")) v = vals.Pop()*v;
else if (op.Equals("/")) v = vals.Pop()/v;
else if (op.Equals("sqrt")) v = Math.Sqrt(v);
vals.Push(v);
count--;
}
}
else vals.Push(Double.Parse(s));
}
return vals.Pop();
}
}
static double Evaluate(string expression) {
var loDataTable = new DataTable();
var loDataColumn = new DataColumn("Eval", typeof (double), expression);
loDataTable.Columns.Add(loDataColumn);
loDataTable.Rows.Add(0);
return (double) (loDataTable.Rows[0]["Eval"]);
}
Explanation of how it works:
First, we make a table in the part var loDataTable = new DataTable();, just like in a Data Base Engine (MS SQL for example).
Then, a column, with some specific parameters (var loDataColumn = new DataColumn("Eval", typeof (double), expression);).
The "Eval" parameter is the name of the column (ColumnName attribute).
typeof (double) is the type of data to be stored in the column, which is equal to put System.Type.GetType("System.Double"); instead.
expression is the string that the Evaluate method receives, and is stored in the attribute Expression of the column. This attribute is for a really specific purpose (obvious), which is that every row that's put on the column will be fullfilled with the "Expression", and it accepts practically wathever can be put in a SQL Query. Refer to http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression(v=vs.100).aspx to know what can be put in the Expression attribute, and how it's evaluated.
Then, loDataTable.Columns.Add(loDataColumn); adds the column loDataColumn to the loDataTable table.
Then, a row is added to the table with a personalized column with a Expression attribute, done via loDataTable.Rows.Add(0);. When we add this row, the cell of the column "Eval" of the table loDataTable is fullfilled automatically with its "Expression" attribute, and, if it has operators and SQL Queries, etc, it's evaluated and then stored to the cell, so, here happens the "magic", the string with operators is evaluated and stored to a cell...
Finally, just return the value stored to the cell of the column "Eval" in row 0 (it's an index, starts counting from zero), and making a conversion to a double with return (double) (loDataTable.Rows[0]["Eval"]);.
And that's all... job done!
And here a code eaiser to understand, which does the same... It's not inside a method, and it's explained too.
DataTable MyTable = new DataTable();
DataColumn MyColumn = new DataColumn();
MyColumn.ColumnName = "MyColumn";
MyColumn.Expression = "5+5/5"
MyColumn.DataType = typeof(double);
MyTable.Columns.Add(MyColumn);
DataRow MyRow = MyTable.NewRow();
MyTable.Rows.Add(MyRow);
return (double)(MyTable.Rows[0]["MyColumn"]);
First, create the table with DataTable MyTable = new DataTable();
Then, a column with DataColumn MyColumn = new DataColumn();
Next, we put a name to the column. This so we can search into it's contents when it's stored to the table. Done via MyColumn.ColumnName = "MyColumn";
Then, the Expression, here we can put a variable of type string, in this case there's a predefined string "5+5/5", which result is 6.
The type of data to be stored to the column MyColumn.DataType = typeof(double);
Add the column to the table... MyTable.Columns.Add(MyColumn);
Make a row to be inserted to the table, which copies the table structure DataRow MyRow = MyTable.NewRow();
Add the row to the table with MyTable.Rows.Add(MyRow);
And return the value of the cell in row 0 of the column MyColumn of the table MyTable with return (double)(MyTable.Rows[0]["MyColumn"]);
Lesson done!!!
This is right to left execution, so need to use proper parathesis to execute expression
// 2+(100/5)+10 = 32
//((2.5+10)/5)+2.5 = 5
// (2.5+10)/5+2.5 = 1.6666
public static double Evaluate(String expr)
{
Stack<String> stack = new Stack<String>();
string value = "";
for (int i = 0; i < expr.Length; i++)
{
String s = expr.Substring(i, 1);
char chr = s.ToCharArray()[0];
if (!char.IsDigit(chr) && chr != '.' && value != "")
{
stack.Push(value);
value = "";
}
if (s.Equals("(")) {
string innerExp = "";
i++; //Fetch Next Character
int bracketCount=0;
for (; i < expr.Length; i++)
{
s = expr.Substring(i, 1);
if (s.Equals("("))
bracketCount++;
if (s.Equals(")"))
if (bracketCount == 0)
break;
else
bracketCount--;
innerExp += s;
}
stack.Push(Evaluate(innerExp).ToString());
}
else if (s.Equals("+")) stack.Push(s);
else if (s.Equals("-")) stack.Push(s);
else if (s.Equals("*")) stack.Push(s);
else if (s.Equals("/")) stack.Push(s);
else if (s.Equals("sqrt")) stack.Push(s);
else if (s.Equals(")"))
{
}
else if (char.IsDigit(chr) || chr == '.')
{
value += s;
if (value.Split('.').Length > 2)
throw new Exception("Invalid decimal.");
if (i == (expr.Length - 1))
stack.Push(value);
}
else
throw new Exception("Invalid character.");
}
double result = 0;
while (stack.Count >= 3)
{
double right = Convert.ToDouble(stack.Pop());
string op = stack.Pop();
double left = Convert.ToDouble(stack.Pop());
if (op == "+") result = left + right;
else if (op == "+") result = left + right;
else if (op == "-") result = left - right;
else if (op == "*") result = left * right;
else if (op == "/") result = left / right;
stack.Push(result.ToString());
}
return Convert.ToDouble(stack.Pop());
}
You could fairly easily run this through the CSharpCodeProvider with suitable fluff wrapping it (a type and a method, basically). Likewise you could go through VB etc - or JavaScript, as another answer has suggested. I don't know of anything else built into the framework at this point.
I'd expect that .NET 4.0 with its support for dynamic languages may well have better capabilities on this front.
I recently needed to do this for a project and I ended up using IronPython to do it. You can declare an instance of the engine, and then pass any valid python expression and get the result. If you're just doing simple math expressions, then it would suffice. My code ended up looking similar to:
IronPython.Hosting.PythonEngine pythonEngine = new IronPython.Hosting.PythonEngine();
string expression = "3*(2+4)";
double result = pythonEngine.EvaluateAs<double>(expression);
You'd probably not want to create the engine for each expression. You also need a reference to IronPython.dll
EDIT: Realised i should really bring the addition and subtraction out seperately aswell to make it a little bit more BODMAS compliant.
Big thanks to Rajesh Jinaga for his Stack based approach. I found it really useful for my needs. The following code is a slight modification of Rajesh's method, which processes divisions first, then multiplications, then finishes up with addition and subtraction. It will also allow the use of booleans in the expressions, where true is treated as 1 and false 0. allowing the use of boolean logic in expressions.
public static double Evaluate(string expr)
{
expr = expr.ToLower();
expr = expr.Replace(" ", "");
expr = expr.Replace("true", "1");
expr = expr.Replace("false", "0");
Stack<String> stack = new Stack<String>();
string value = "";
for (int i = 0; i < expr.Length; i++)
{
String s = expr.Substring(i, 1);
// pick up any doublelogical operators first.
if (i < expr.Length - 1)
{
String op = expr.Substring(i, 2);
if (op == "<=" || op == ">=" || op == "==")
{
stack.Push(value);
value = "";
stack.Push(op);
i++;
continue;
}
}
char chr = s.ToCharArray()[0];
if (!char.IsDigit(chr) && chr != '.' && value != "")
{
stack.Push(value);
value = "";
}
if (s.Equals("("))
{
string innerExp = "";
i++; //Fetch Next Character
int bracketCount = 0;
for (; i < expr.Length; i++)
{
s = expr.Substring(i, 1);
if (s.Equals("(")) bracketCount++;
if (s.Equals(")"))
{
if (bracketCount == 0) break;
bracketCount--;
}
innerExp += s;
}
stack.Push(Evaluate(innerExp).ToString());
}
else if (s.Equals("+") ||
s.Equals("-") ||
s.Equals("*") ||
s.Equals("/") ||
s.Equals("<") ||
s.Equals(">"))
{
stack.Push(s);
}
else if (char.IsDigit(chr) || chr == '.')
{
value += s;
if (value.Split('.').Length > 2)
throw new Exception("Invalid decimal.");
if (i == (expr.Length - 1))
stack.Push(value);
}
else
{
throw new Exception("Invalid character.");
}
}
double result = 0;
List<String> list = stack.ToList<String>();
for (int i = list.Count - 2; i >= 0; i--)
{
if (list[i] == "/")
{
list[i] = (Convert.ToDouble(list[i - 1]) / Convert.ToDouble(list[i + 1])).ToString();
list.RemoveAt(i + 1);
list.RemoveAt(i - 1);
i -= 2;
}
}
for (int i = list.Count - 2; i >= 0; i--)
{
if (list[i] == "*")
{
list[i] = (Convert.ToDouble(list[i - 1]) * Convert.ToDouble(list[i + 1])).ToString();
list.RemoveAt(i + 1);
list.RemoveAt(i - 1);
i -= 2;
}
}
for (int i = list.Count - 2; i >= 0; i--)
{
if (list[i] == "+")
{
list[i] = (Convert.ToDouble(list[i - 1]) + Convert.ToDouble(list[i + 1])).ToString();
list.RemoveAt(i + 1);
list.RemoveAt(i - 1);
i -= 2;
}
}
for (int i = list.Count - 2; i >= 0; i--)
{
if (list[i] == "-")
{
list[i] = (Convert.ToDouble(list[i - 1]) - Convert.ToDouble(list[i + 1])).ToString();
list.RemoveAt(i + 1);
list.RemoveAt(i - 1);
i -= 2;
}
}
stack.Clear();
for (int i = 0; i < list.Count; i++)
{
stack.Push(list[i]);
}
while (stack.Count >= 3)
{
double right = Convert.ToDouble(stack.Pop());
string op = stack.Pop();
double left = Convert.ToDouble(stack.Pop());
if (op == "<") result = (left < right) ? 1 : 0;
else if (op == ">") result = (left > right) ? 1 : 0;
else if (op == "<=") result = (left <= right) ? 1 : 0;
else if (op == ">=") result = (left >= right) ? 1 : 0;
else if (op == "==") result = (left == right) ? 1 : 0;
stack.Push(result.ToString());
}
return Convert.ToDouble(stack.Pop());
}
I know there is likely to be a cleaner way of doing it, thought id just share the first look at it in case anyone finds it usefull.
Many thanks to Ramesh. I used a version of his simple code to pull a string out a database and use it to do boolean operations in my code.
x is a number like 1500 or 2100 or whatever.
function would be a stored evaluation like x > 1400 and x < 1600
function = relation[0].Replace("and","&&").Replace("x",x);
DataTable f_dt = new DataTable();
var f_var = f_dt.Compute(function,"");
if (bool.Parse(f_var.ToString()) { do stuff }
There is not. You will need to use some external library, or write your own parser. If you have the time to do so, I suggest to write your own parser as it is a quite interesting project. Otherwise you will need to use something like bcParser.
Short answer: I don't think so. C# .Net is compiled (to bytecode) and can't evaluate strings at runtime, as far as I know. JScript .Net can, however; but I would still advise you to code a parser and stack-based evaluator yourself.

Categories

Resources