Background:
I'm working on a Console Application that for each line is saved to the specified days log file (for instance January 5, 2017 is 01_05_2017_log_.txt)
Each log gets added as its supposed to, but the problem I can't seem to figure out is why it's adding all the previous values of the List containing the log values again.
So when CurrentLogs.Add("Some log here") is being called it adds Some log here to the CurrentLogs List.
Let's call it again, but this time the string used is another log here. The items in the List are as follows:
Some log here
Some log here
another log here
And then those 3 are re-added if I try to use the .Add method again
I've looked and looked at ways around this, bing, google, no avail.
// This is the List object I'm using.
public class LoggerList<T> : List<T>
{
public event EventHandler OnAdd;
public new void Add(T item)
{
OnAdd?.Invoke(this, null);
base.Add(item);
}
}
Now here is the Logger class where Logger.CurrentLogs.Add() is called f rom
public class Logger
{
// Directory in which logs are stored
private static string _loggerPath = "Logs/";
// Used to check if the Old Logs have already been added to the OldLogs Object
private static bool _alreadyPulled = false;
// Previously saved logs before the current use of the console app
private static readonly List<string> OldLogs = new List<string>();
// Current logs to be added here
public static LoggerList<string> CurrentLogs = new LoggerList<string>();
//EventHandler to Save Logs on add
public static void OnAdd(object sender, EventArgs e)
{
SaveLog();
}
/// <summary>
/// Saves the logs
/// </summary>
public static void SaveLog()
{
if (!Directory.Exists(_loggerPath))
{
Directory.CreateDirectory(_loggerPath);
}
string fileName = _loggerPath + DateTime.Now.ToString("MM_dd_yyyy") + "_log_.txt";
// If the File Exists, contiue with loading
if (File.Exists(fileName))
{
// If the OldLogs have not already been pulled, pull them
if (!_alreadyPulled)
{
var oldLogs = File.ReadAllLines(fileName).ToList();
foreach (var i in oldLogs)
{
OldLogs.Add(i);
}
_alreadyPulled = true; // Let know that the OldLogs have already been pulled this instance
}
List<string> lines = OldLogs;
lines.AddRange(CurrentLogs);
File.Delete(fileName);
var sr = File.CreateText(fileName);
foreach (string x in lines)
{
sr.WriteLine(x);
}
sr.Flush();
sr.Close();
}
// Only go here if the file doesn't exist
else
{
var sr = File.CreateText(fileName);
foreach (string x in CurrentLogs)
{
sr.WriteLine(x);
}
sr.Flush();
sr.Close();
}
}
}
Here is Program.cs (Adding this as this is where the Messenger class is called
class Program
{
static void Main(string[] args)
{
Logger.CurrentLogs.OnAdd += Logger.OnAdd;
CommandManager.InitCommands();
CheckForEntries();
}
private static void CheckForEntries()
{
while (true)
{
Console.ForegroundColor = ConsoleColor.White;
var text = Console.ReadLine();
try
{
if (!SendEntry(text))
{
Console.WriteLine("Failed to send");
}
}
catch (CommandNotFoundException ex)
{
Messenger.Send("Commmand does not exist (" + text.GetWords()[0] + ")");
}
}
}
private static bool SendEntry(string text)
{
try
{
if (text.FirstCharacter() != "/")
{
return false;
}
else
{
string text2 = text.Substring(1);
string commandname = text2.GetWords()[0];
foreach (Command c in CommandManager.RegisteredCommands)
{
if (c.Name.ToLower() == commandname.ToLower())
{
c.Run(text2.Substring(commandname.Length));
return true;
}
else
{
foreach (string alias in c.Aliases)
{
if (alias.ToLower() == commandname.ToLower())
{
c.Run(text2.Substring(commandname.Length + 1));
return true;
}
}
}
}
throw new CommandNotFoundException();
}
}
catch (CommandNotFoundException ex)
{
throw ex; // Placed here so that the catch (Exception) does not go beyond
}
catch (Exception ex)
{
Console.WriteLine(ex);
return false;
}
}
}
And here is Messenger.cs (Where the .Add directly happens)
// Color codes
private static readonly string[] codes =
{
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"a", "b", "c", "d", "e", "f"
};
private static string Format(string rawText)
{
return "&e" + TimeStamp() + " " + rawText;
}
public static string TimeStamp()
{
return "<" + DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss") + ">";
}
public static void Send(string textRaw)
{
Logger.CurrentLogs.Add(Format(textRaw).Substring(2));
string text = Format(textRaw);
List<int> skipOver = new List<int>();
for (int txt = 0; txt <= text.Length - 1; txt++)
{
char[] chars = text.ToCharArray();
if (chars[txt].ToString() == "&")
{
List<string> hi = codes.ToList();
if (hi.Contains(chars[txt + 1].ToString().ToLower()))
{
skipOver.Add(txt);
skipOver.Add(txt + 1);
}
}
}
for (int x = 0; x <= text.Length - 1; x++)
{
char[] chars = text.ToCharArray();
if (chars[x] == "&".ToCharArray()[0]) continue;
if (x <= 1 || skipOver.Contains(x)) continue;
char behind2 = chars[x - 2];
char behind1 = chars[x - 1];
if (behind2.ToString() == "&")
{
bool isGoodCode = false;
foreach (string s in codes)
{
if (s.ToLower() == behind1.ToString().ToLower())
{
isGoodCode = true;
}
}
if (isGoodCode)
{
skipOver.Add(x - 2);
skipOver.Add(x - 1);
if (x < text.Length - 1)
{
Color baseColor = ToColor(behind1.ToString());
ConsoleColor cColor = ToConsoleColor(baseColor);
Console.ForegroundColor = cColor;
Console.Write(chars[x]);
}
else if (x == text.Length - 1)
{
Color baseColor = ToColor(behind1.ToString());
ConsoleColor cColor = ToConsoleColor(baseColor);
Console.ForegroundColor = cColor;
Console.WriteLine(chars[x]);
}
}
else
{
if (x < text.Length - 1)
{
Console.Write(chars[x - 2]);
Console.Write(chars[x - 1]);
Console.Write(chars[x]);
}
else if (x == text.Length - 1)
{
Console.Write(chars[x - 2]);
Console.Write(chars[x - 1]);
Console.WriteLine(chars[x]);
}
}
}
else
{
if (x < text.Length - 1)
{
Console.Write(chars[x]);
}
else if (x == text.Length - 1)
{
Console.WriteLine(chars[x]);
}
}
}
}
In these lines
List<string> lines = OldLogs;
lines.AddRange(CurrentLogs);
"lines" points to the same object as "OldLogs". Therefore you keep adding entries to the same list.
You could try copying instead:
var lines = new List<string>(OldLogs);
Related
I have this method:
public static string ReplaceVarReferences(string input,char open, char close)
{
string inner = "";
for (int i = 0; i < input.Length; i++)
{
if (input[i] == open)
{
i++;
string varInner = "";
while (input[i] != close)
{
varInner += input[i];
i++;
}
inner += GetVariable(varInner);
}
else
{
inner += input[i];
}
}
return inner;
}
and this memory allocation and retrieval methods:
public static Dictionary<string, string> memory = new Dictionary<string, string>();
public static string GetVariable(string key)
{
if (memory.ContainsKey(key))
{
return memory[key];
}
else
{
return key;
}
}
public static void SetVariable(string key, string value)
{
if (memory.ContainsKey(key))
{
memory[key] = value;
}
else
{
memory.Add(key, value);
}
}
I want to to return a string with the 'ReplaceVarReferences' method and return a string with multiple spaces like this:
this code is from my interpreter and not real code.
var a = hello world;
print(variable a = {a})
the 'ReplaceVarReferences' method should be able to return this with multiple spaces:
"variable a = hello world"
but instead, it returns:
"variable a = hello world"
how can I fix this?
Ok so I looked at the wrong place I have another method that had the problem:
public static string GetNested(string Open, string Close, List<string> t, ref int i)
{
string inner = "";
int nested = 0;
while (true)
{
if (i < t.Count - 1)
{
i++;
if (string.IsNullOrWhiteSpace(t[i]))
{
for (int x = 0; x < t[i].Length; x++)
{
inner += " ";
}
}
else if (t[i] == Close)
{
nested--;
if (nested > 0)
{
inner += t[i];
}
}
else if (t[i] == Open)
{
if (nested > 0)
{
inner += t[i];
}
nested++;
}
else
{
inner += t[i];
}
if (nested == 0)
{
break;
}
}
}
return inner;
}
i did not include:
if (string.IsNullOrWhiteSpace(t[i]))
{
for (int x = 0; x < t[i].Length; x++)
{
inner += " ";
}
}
and it resulted in the spaces being dumped when retrieving the nested string.
I am looking to store values from a CSV file with two Columns, I have the following class ReadFromCSVhandling the reading of the CSV file but I am having difficulty using this list to display the contents once a button is clicked. The code I have to read the CSV file is as follows;
namespace ELMFS
{
public class ReadFromCSV
{
static void ReadCSV(string[] args)
{
List<TextSpeak> TxtSpk = File.ReadAllLines(#"C:\textwords.csv")
.Skip(1)
.Select(t => TextSpeak.FromCsv(t))
.ToList();
}
}
public class TextSpeak
{
string Abreviated;
string Expanded;
public static TextSpeak FromCsv(string csvLine)
{
string[] TxtSpk = csvLine.Split(',');
TextSpeak textSpeak = new TextSpeak();
textSpeak.Abreviated = TxtSpk[0];
textSpeak.Expanded = TxtSpk[1];
return textSpeak;
}
}
}
I am trying to display the textSpeak.Abreviated in a message box but cannot seem to access it from the WPF window.
How do I use this list in other windows within the application?
any advice would be appreciated!
Thanks in advance!
First, ReadCSV method should return the generated List object (or you cannot use the list anywhere else).
Second, TextSpeak class should have properties so that you can access its member variables outside the class.
I.e. something like this should work:
namespace ELMFS
{
public class ReadFromCSV
{
public static List<TextSpeak> ReadCSV(string[] args)
{
List<TextSpeak> TxtSpk = File.ReadAllLines(#"C:\textwords.csv")
.Skip(1)
.Select(t => TextSpeak.FromCsv(t))
.ToList();
return TxtSpk;
}
}
public class TextSpeak
{
public string Abreviated { get; private set; }
public string Expanded { get; private set; }
public static TextSpeak FromCsv(string csvLine)
{
string[] TxtSpk = csvLine.Split(',');
TextSpeak textSpeak = new TextSpeak();
textSpeak.Abreviated = TxtSpk[0];
textSpeak.Expanded = TxtSpk[1];
return textSpeak;
}
}
}
private void Display(int count)
{
textBox1.Text = "";
for (int i = 0; i <= count ; i++)
{
textBox1.Text += ((dataGridView1.Rows[i].Cells[1].Value).ToString()) + (dataGridView1.Rows[i].Cells[2].Value.ToString()) + Environment.NewLine;
}
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
// your code here
string CSVFilePathName =Path.GetDirectoryName(Application.ExecutablePath).Replace(#"\bin\Debug", #"\NewFolder1\TEST.csv");
string[] Lines = File.ReadAllLines(CSVFilePathName);
string[] Fields;
Fields = Lines[0].Split(new char[] { ',' });
int Cols = Fields.GetLength(0);
DataTable dt = new DataTable();
//1st row must be column names; force lower case to ensure matching later on.
for (int i = 0; i < Cols; i++)
dt.Columns.Add(Fields[i].ToLower(), typeof(string));
DataRow Row;
for (int i = 1; i < Lines.GetLength(0); i++)
{
Fields = Lines[i].Split(new char[] { ',' });
Row = dt.NewRow();
for (int f = 0; f < Cols; f++)
Row[f] = Fields[f];
dt.Rows.Add(Row);
}
dataGridView1.DataSource = dt;
int count = 0;
if (dataGridView1.RowCount > 0)
{
count = dataGridView1.Rows.Count;
}
buttons = new Button[count];
for (int i = 0; i <count; i++)
{
buttons[i] = new Button();
buttons[i].Name = "buttons_Click" + i.ToString();
buttons[i].Text = "Click";
buttons[i].Click += new EventHandler(buttons_Click);
this.Controls.Add(buttons[i]);
buttons[i].Visible = false;
}
buttons[0].Visible = true;
// buttons[1].Visible = true;
}
catch (Exception ex)
{
MessageBox.Show("Error is " + ex.ToString());
throw;
}
}
private void buttons_Click(object sender, EventArgs e)
{
int count = dataGridView1.Rows.Count-1;
if(c <= count)
{
if (buttons[c].Name == "buttons_Click" + c.ToString())
{
buttons[c].Visible = false;
int j = c;
Display(j);
if (c != count)
{
c = c + 1;
buttons[c].Visible = true;
}
}
}
if (c == count)
{
buttons[0].Visible = true;
}
}
}
}
My C# classes will create a GUI that takes in csv files and plot a line graph accordingly. Currently, my graphs are plotted and saved using the GUI file dialog.
What I am trying to do now is to read, plot and save the graph using command-line instead (NO GUI needed).
May I know how could I call my Read and Plot classes using the "-f" flag (file path, can have multiple csv file) and save the plotted graph using the "-o" flag (output file path, only 1 file produced)?
Thanks.
You can use this:
class Program:
static class Program
{
[System.Runtime.InteropServices.DllImport("kernel32.dll")] // ### Edit 3 ###
static extern bool AttachConsole(int dwProcessId); // ### Edit 3 ###
/// <summary>The main entry point for the application.</summary>
[STAThread]
static void Main(string[] args)
{
// redirect console output to parent process;
// must be before any calls to Console.WriteLine()
AttachConsole(-1);// ### Edit 3 ###
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//program.exe -f c:\\desktop\\1.csv -o c:\\desktop\\1.png
var inputFile = new List<string>();
string outputFile = null;
if (args.Length > 0)
{
for (int i = 0; i < args.Length; i++)
{
string a = args[i].ToLower();
switch (a)
{
case "-f":
for (i = i + 1; i < args.Length ; i++)
{
string f = args[i]; if (f.StartsWith("-")) { i--; break; }
inputFile.Add(f); //get next arg as inputFile
}
break;
case "-o":
outputFile = args[++i]; //get next arg as outputFile
break;
}
}
if (inputFile.Count > 0 && outputFile != null)
{
var form = new Form2(); //specify your form class
form.showErrorsInConsole = true; // ### Edit 3 ###
//form.Visible = true;
form.DoReadFiles(inputFile.ToArray());
form.DoPlot();
form.SavePic(outputFile);
form.Dispose();
return;
}
}
//else
Application.Run(new Form2()); //show GUI
//MessageBox.Show("Args:\r\n" + s);
}
}
Form class (the form conatining your chart, in my code it is Form2):
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
}
List<Read> rList = new List<Read>();
public bool showErrorsInConsole = false; //### Edit 3 ###
public void DoReadFiles(string[] fileNames)
{
try
{
rList.Clear();
foreach (String file in fileNames) //if ((myStream = ff.OpenFile()) != null)
{
Read r = new Read(file);
rList.Add(r);
}
}
catch (Exception err)
{
//Inform the user if we can't read the file
if (showErrorsInConsole) //### Edit 3 ###
Console.WriteLine("\r\n *** Error: " + err.Message); //### Edit 3 ###
else
MessageBox.Show(err.Message);
}
}
public void DoPlot(int indX = 0, int indY = 1)
{
Plot.Draw(chart, rList, indX, indY);
}
public void SavePic(string outputFile)
{
bool isPng = outputFile.EndsWith(".png", StringComparison.OrdinalIgnoreCase);
chart.SaveImage(outputFile, isPng ? ChartImageFormat.Png : ChartImageFormat.Jpeg);
}
void openToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog ff = new OpenFileDialog();
ff.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); //"C:\\";
ff.Filter = "csv files (*.csv)|*.csv|All files (*.*)|*.*";
ff.Multiselect = true;
ff.FilterIndex = 1;
ff.RestoreDirectory = true;
if (ff.ShowDialog() == DialogResult.OK)
{
try
{
DoReadFiles(ff.FileNames);
//Populate the ComboBoxes
if (rList.Count > 0)
{
string[] header = rList[0].header; //header of first file
xBox.DataSource = header;
yBox.DataSource = header.Clone(); //without Clone the 2 comboboxes link together!
}
if (yBox.Items.Count > 1) yBox.SelectedIndex = 1; //select second item
}
catch (Exception err)
{
//Inform the user if we can't read the file
MessageBox.Show(err.Message);
}
}
}
private void button1_Click(object sender, EventArgs e)
{
DoPlot(xBox.SelectedIndex, yBox.SelectedIndex);
}
} //end class Form2
class Read:
public class Read
{
public int nLines { get; private set; }
public int nColumns { get; private set; }
public string[] header { get; private set; }
public float[,] data { get; private set; }
public string fileName { get; set; }
public string[] section { get; private set; }
public Read(string file)
{
string[] pieces;
fileName = Path.GetFileName(file);
string[] lines = File.ReadAllLines(file); // read all lines
if (lines == null || lines.Length < 2) return; //no data in file
header = lines[0].Split(','); //first line is header
nLines = lines.Length - 1; //first line is header
nColumns = header.Length;
//read the numerical data and section name from the file
data = new float[nLines, nColumns - 1]; // 1 less than nColumns as last col is sectionName
section = new string[nLines];
for (int i = 0; i < nLines; i++)
{
pieces = lines[i + 1].Split(','); // i(+1) is because first line is header
if (pieces.Length != nColumns) { MessageBox.Show("Invalid data at line " + (i + 2) + " of file " + fileName); return; }
for (int j = 0; j < nColumns - 1; j++)
{
float.TryParse(pieces[j], out data[i, j]); //data[i, j] = float.Parse(pieces[j]);
}
section[i] = pieces[nColumns - 1]; //last item
}
}
}
class Plot:
public class Plot
{
public static void Draw(Chart chart, List<Read> rList, int indX = 0, int indY = 1)
{
chart.Series.Clear(); //ensure that the chart is empty
chart.Legends.Clear();
Legend myLegend = chart.Legends.Add("myLegend");
myLegend.Title = "myTitle";
Color[] colors = new Color[] { Color.Black, Color.Blue, Color.Red, Color.Green, Color.Magenta, Color.DarkCyan, Color.Chocolate, Color.DarkMagenta };
var sectionColors = new Dictionary<string, int>();
bool separateSections = (rList.Count == 1); // #Edit: 4
int i = 0;
int iColor = -1, maxColor = -1;
foreach (Read rr in rList)
{
float[,] data = rr.data;
int nLines = rr.nLines;
int nColumns = rr.nColumns;
string[] header = rr.header;
chart.Series.Add("Series" + i);
chart.Series[i].ChartType = SeriesChartType.Line;
chart.Series[i].LegendText = rr.fileName; // #Edit: 4
if (separateSections) chart.Series[i].IsVisibleInLegend = false; // #Edit: 4
chart.ChartAreas[0].AxisX.LabelStyle.Format = "{F2}";
chart.ChartAreas[0].AxisX.Title = header[indX];
chart.ChartAreas[0].AxisY.Title = header[indY];
for (int j = 0; j < nLines; j++)
{
int k = chart.Series[i].Points.AddXY(data[j, indX], data[j, indY]);
if (separateSections) // #Edit: 4
{
string curSection = rr.section[j];
if (sectionColors.ContainsKey(curSection))
{
iColor = sectionColors[curSection];
}
else
{
maxColor++;
iColor = maxColor; sectionColors[curSection] = iColor;
}
chart.Series[i].Points[k].Color = colors[iColor];
}
}
i++; //series#
} //end foreach rr
//fill legend based on series
foreach (var x in sectionColors)
{
string section = x.Key;
iColor = x.Value;
myLegend.CustomItems.Add(colors[iColor], section); //new LegendItem()
}
}
}
If you are looking for something simple then you can just have a loop and get the values like this:
string f=null;
string o=null;
for (var i = 0; i < args.Length; i++)
{
if (args[i] == "-f") { f = args[i + 1]; }
else if (args[i] == "-o") { o = args[i + 1]; }
}
if (f != null)
{
}
if (o != null)
{
}
You can use command parser libraries. Command Line Parser ( http://commandline.codeplex.com/ ) is very easy to use.
[Option("f", "input", Required = true]
public string InputFile { get; set; }
[Option("o", "output", Required = true]
public string OutputFile { get; set; }
What C# template engine
that uses 'pure' HTML having only text and markers
sans any control flow like if, while, loop or expressions,
separating html from control code ?
Below is the example phone book list code,
expressing how this should be done:
string html=#"
<html><head><title>#title</title></head>
<body>
<table>
<tr>
<td> id</td> <td> name</td> <td> sex</td> <td>phones</td>
</tr><!--#contacts:-->
<tr>
<td>#id</td> <td>#name</td> <td>#sex</td>
<td>
<!--#phones:-->#phone <br/>
<!--:#phones-->
</td>
</tr><!--:#contacts-->
</table>
</body>
</html>";
var contacts = from c in db.contacts select c;
Marker m = new Marker(html);
Filler t = m.Mark("title");
t.Set("Phone book");
Filler c = m.Mark("contacts", "id,name,sex");
// **foreach** expressed in code, not in html
foreach(var contact in contacts) {
int id = contact.id;
c.Add(id, contact.name, contact.sex);
Filler p = c.Mark("phones", "phone");
var phones = from ph in db.phones
where ph.id == id
select new {ph.phone};
if (phones.Any()) {
foreach(var ph in phones) {
p.Add(ph);
}
} else {
fp.Clear();
}
}
Console.Out.WriteLine(m.Get());
Use this code:
Templet.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace templaten.com.Templaten
{
public class tRange
{
public int head, toe;
public tRange(int _head, int _toe)
{
head = _head;
toe = _toe;
}
}
public enum AType
{
VALUE = 0,
NAME = 1,
OPEN = 2,
CLOSE = 3,
GROUP = 4
}
public class Atom
{
private AType kin;
private string tag;
private object data;
private List<Atom> bag;
public Atom(string _tag = "",
AType _kin = AType.VALUE,
object _data = null)
{
tag = _tag;
if (String.IsNullOrEmpty(_tag))
_kin = AType.GROUP;
kin = _kin;
if (_kin == AType.GROUP)
bag = new List<Atom>();
else
bag = null;
data = _data;
}
public AType Kin
{
get { return kin; }
}
public string Tag
{
get { return tag; }
set { tag = value; }
}
public List<Atom> Bag
{
get { return bag; }
}
public object Data
{
get { return data; }
set { data = value; }
}
public int Add(string _tag = "",
AType _kin = AType.VALUE,
object _data = null)
{
if (bag != null)
{
bag.Add(new Atom(_tag, _kin, _data));
return bag.Count - 1;
}
else
{
return -1;
}
}
}
public class Templet
{
private string content;
string namepat = "\\w+";
string justName = "(\\w+)";
string namePre = "#";
string namePost = "";
string comment0 = "\\<!--\\s*";
string comment1 = "\\s*--\\>";
private Atom tokens; // parsed contents
private Dictionary<string, int> iNames; // name index
private Dictionary<string, tRange> iGroups; // groups index
private Atom buffer; // output buffer
private Dictionary<string, int> _iname; // output name index
private Dictionary<string, tRange> _igroup; // output index
public Templet(string Content = null)
{
Init(Content);
}
private int[] mark(string[] names, string group)
{
if (names == null || names.Length < 1) return null;
tRange t = new tRange(0, buffer.Bag.Count - 1);
if (group != null)
{
if (!_igroup.ContainsKey(group)) return null;
t = _igroup[group];
}
int[] marks = new int[names.Length];
for (int i = 0; i < marks.Length; i++)
marks[i] = -1;
for (int i = t.head; i <= t.toe; i++)
{
if (buffer.Bag[i].Kin == AType.NAME)
{
for (int j = 0; j < names.Length; j++)
{
if (String.Compare(
names[j],
buffer.Bag[i].Tag,
true) == 0)
{
marks[j] = i;
break;
}
}
}
}
return marks;
}
public Filler Mark(string group, string names)
{
Filler f = new Filler(this, names);
f.di = mark(f.names, group);
f.Group = group;
tRange t = null;
if (_igroup.ContainsKey(group)) t = _igroup[group];
f.Range = t;
return f;
}
public Filler Mark(string names)
{
Filler f = new Filler(this, names);
f.di = mark(f.names, null);
f.Group = "";
f.Range = null;
return f;
}
public void Set(int[] locations, object[] x)
{
int j = Math.Min(x.Length, locations.Length);
for (int i = 0; i < j; i++)
{
int l = locations[i];
if ((l >= 0) && (buffer.Bag[l] != null))
buffer.Bag[l].Data = x[i];
}
}
public void New(string group, int seq = 0)
{
// place new group copied from old group just below it
if (!( iGroups.ContainsKey(group)
&& _igroup.ContainsKey(group)
&& seq > 0)) return;
tRange newT = null;
tRange t = iGroups[group];
int beginRange = _igroup[group].toe + 1;
for (int i = t.head; i <= t.toe; i++)
{
buffer.Bag.Insert(beginRange,
new Atom(tokens.Bag[i].Tag,
tokens.Bag[i].Kin,
tokens.Bag[i].Data));
beginRange++;
}
newT = new tRange(t.toe + 1, t.toe + (t.toe - t.head + 1));
// rename past group
string pastGroup = group + "_" + seq;
t = _igroup[group];
buffer.Bag[t.head].Tag = pastGroup;
buffer.Bag[t.toe].Tag = pastGroup;
_igroup[pastGroup] = t;
// change group indexes
_igroup[group] = newT;
}
public void ReMark(Filler f, string group)
{
if (!_igroup.ContainsKey(group)) return;
Map(buffer, _iname, _igroup);
f.di = mark(f.names, group);
f.Range = _igroup[group];
}
private static void Indexing(string aname,
AType kin,
int i,
Dictionary<string, int> dd,
Dictionary<string, tRange> gg)
{
switch (kin)
{
case AType.NAME: // index all names
dd[aname] = i;
break;
case AType.OPEN: // index all groups
if (!gg.ContainsKey(aname))
gg[aname] = new tRange(i, -1);
else
gg[aname].head = i;
break;
case AType.CLOSE:
if (!gg.ContainsKey(aname))
gg[aname] = new tRange(-1, i);
else
gg[aname].toe = i;
break;
default:
break;
}
}
private static void Map(Atom oo,
Dictionary<string, int> dd,
Dictionary<string, tRange> gg)
{
for (int i = 0; i < oo.Bag.Count; i++)
{
string aname = oo.Bag[i].Tag;
Indexing(oo.Bag[i].Tag, oo.Bag[i].Kin, i, dd, gg);
}
}
public void Init(string Content = null)
{
content = Content;
tokens = new Atom("", AType.GROUP);
iNames = new Dictionary<string, int>();
iGroups = new Dictionary<string, tRange>();
// parse content into tokens
string namePattern = namePre + namepat + namePost;
string patterns =
"(?<var>" + namePattern + ")|" +
"(?<head>" + comment0 + namePattern + ":" + comment1 + ")|" +
"(?<toe>" + comment0 + ":" + namePattern + comment1 + ")";
Regex jn = new Regex(justName, RegexOptions.Compiled);
Regex r = new Regex(patterns, RegexOptions.Compiled);
MatchCollection ms = r.Matches(content);
int pre = 0;
foreach (Match m in ms)
{
tokens.Add(content.Substring(pre, m.Index - pre));
int idx = -1;
if (m.Groups.Count >= 3)
{
string aname = "";
MatchCollection x = jn.Matches(m.Value);
if (x.Count > 0 && x[0].Groups.Count > 1)
aname = x[0].Groups[1].ToString();
AType t = AType.VALUE;
if (m.Groups[1].Length > 0) t = AType.NAME;
if (m.Groups[2].Length > 0) t = AType.OPEN;
if (m.Groups[3].Length > 0) t = AType.CLOSE;
if (aname.Length > 0)
{
tokens.Add(aname, t);
idx = tokens.Bag.Count - 1;
}
Indexing(aname, t, idx, iNames, iGroups);
}
pre = m.Index + m.Length;
}
if (pre < content.Length)
tokens.Add(content.Substring(pre, content.Length - pre));
// copy tokens into buffer
buffer = new Atom("", AType.GROUP);
for (int i = 0; i < tokens.Bag.Count; i++)
buffer.Add(tokens.Bag[i].Tag, tokens.Bag[i].Kin);
// initialize index of output names
_iname = new Dictionary<string, int>();
foreach (string k in iNames.Keys)
_iname[k] = iNames[k];
// initialize index of output groups
_igroup = new Dictionary<string, tRange>();
foreach (string k in iGroups.Keys)
{
tRange t = iGroups[k];
_igroup[k] = new tRange(t.head, t.toe);
}
}
public string Get()
{
StringBuilder sb = new StringBuilder("");
for (int i = 0; i < buffer.Bag.Count; i++)
{
switch (buffer.Bag[i].Kin)
{
case AType.VALUE:
sb.Append(buffer.Bag[i].Tag);
break;
case AType.NAME:
sb.Append(buffer.Bag[i].Data);
break;
case AType.OPEN:
case AType.CLOSE:
break;
default: break;
}
}
return sb.ToString();
}
}
public class Filler
{
private Templet t = null;
public int[] di;
public string[] names;
public string Group { get; set; }
public tRange Range { get; set; }
private int seq = 0;
public Filler(Templet tl, string markers = null)
{
t = tl;
if (markers != null)
names = markers.Split(new char[] { ',' },
StringSplitOptions.RemoveEmptyEntries);
else
names = null;
}
public void init(int length)
{
di = new int[length];
for (int i = 0; i < length; i++)
di[i] = -1;
seq = 0;
Group = "";
Range = null;
}
// clear contents inside marked object or group
public void Clear()
{
object[] x = new object[di.Length];
for (int i = 0; i < di.Length; i++)
x[i] = null;
t.Set(di, x);
}
// set value for marked object,
// or add row to group and set value to columns
public void Set(params object[] x)
{
t.Set(di, x);
}
public void Add(params object[] x)
{
if (Group.Length > 0)
{
t.New(Group, seq);
++seq;
t.ReMark(this, Group);
}
t.Set(di, x);
}
}
}
Testing program
Program.cs
Templet m = new Templet(html);
Filler f= m.Mark("title");
f.Set("Phone book");
Filler fcontacts = m.Mark("contacts", "id,name,sex,phone");
fcontacts.Add(1, "Akhmad", "M", "123456");
fcontacts.Add(2, "Barry", "M", "234567");
fcontacts.Add(1, "Charles", "M", "345678");
Console.Out.WriteLine(m.Get());
Still can't do nested loop- yet.
Just use ASP.NET. Whether you use webforms or MVC, it's super easy to have C# in your .cs files, and HTML in your .aspx files.
As with anything in programming, it's 99% up to you to do things right. Flexible UI engines aren't going to enforce that you follow good coding practices.
In principle most any template engine you choose can separate HTML from control logic with the proper architecture. using an MVC (Or MVVM) pattern, if you construct your model in such a way that the controller contains the if/then logic instead of the view you can eliminate it from the view.
That said, the syntax you use is very close to Razor syntax which is easily available for ASP.NET MVC through NuGet packages.
I totally hear you. I built SharpFusion, which has some other stuff in it but if you look for the template.cs file you will see the handler that parses a HTML file and simply replaces out tokens with values that you've made in c#.
Because no XML parsing is done like ASP.NET the framework loads much faster than even an MVC site.
Another alternative is ServiceStack.
I'm writing a function to take shorthand values and convert them into a standardized numeric format. Is there any standard code out there that would do "best possible" conversion of arbitrary measurement text and turn it into numeric measurements if the text is valid?
I guess I'm looking for something like bool TryParseMeasurement(string s, out decimal d). Does anyone know of a function like this?
Here's an example of some of the input values I've seen:
Imperial
6 inches
6in
6”
4 feet 2 inches
4’2”
4 ‘ 2 “
3 feet
3’
3 ‘
3ft
3ft10in
3ft 13in (should convert to 4’1”)
Metricc
1m
1.2m
1.321m
1 meter
481mm
Here's some code we wrote in an app quite some time ago, where we were doing something similar. It's not the best, but you may be able to adapt, or get some sort of jumping off point.
public static class UnitConversion
{
public static string[] lstFootUnits = new string[] {"foots", "foot", "feets", "feet", "ft", "f", "\""};
public static string sFootUnit = "ft";
public static string[] lstInchUnits = new string[] { "inches", "inchs", "inch", "in", "i", "\'" };
public static string sInchUnit = "in";
public static string[] lstPoundUnits = new string[] { "pounds", "pound", "pnds", "pnd", "lbs", "lb", "l", "p" };
public static string sPoundUnit = "lbs";
public static string[] lstOunceUnits = new string[] { "ounces", "ounce", "ozs", "oz", "o" };
public static string sOunceUnit = "oz";
public static string[] lstCentimeterUnits = new string[] { "centimeters", "centimeter", "centimetres", "centimetre", "cms", "cm", "c"};
public static string sCentimeterUnit = "cm";
public static string[] lstKilogramUnits = new string[] { "kilograms", "kilogram", "kilos", "kilo", "kgs", "kg", "k" };
public static string sKilogramsUnit = "kgs";
/// <summary>
/// Attempt to convert between feet/inches and cm
/// </summary>
/// <param name="sHeight"></param>
/// <returns></returns>
public static string ConvertHeight(string sHeight)
{
if (!String.IsNullOrEmpty(sHeight))
{
sHeight = UnitConversion.CleanHeight(sHeight);
if (sHeight.Contains(UnitConversion.sFootUnit))
{
sHeight = sHeight.Replace(UnitConversion.sFootUnit, "|");
sHeight = sHeight.Replace(UnitConversion.sInchUnit, "|");
string[] sParts = sHeight.Split('|');
double? dFeet = null;
double? dInches = null;
double dFeetParsed;
double dInchesParsed;
if (sParts.Length >= 2 && double.TryParse(sParts[0].Trim(), out dFeetParsed))
{
dFeet = dFeetParsed;
}
if (sParts.Length >= 4 && double.TryParse(sParts[2].Trim(), out dInchesParsed))
{
dInches = dInchesParsed;
};
sHeight = UnitConversion.FtToCm(UnitConversion.CalculateFt(dFeet ?? 0, dInches ?? 0)).ToString() + " " + UnitConversion.sCentimeterUnit;
}
else if (sHeight.Contains(UnitConversion.sCentimeterUnit))
{
sHeight = sHeight.Replace(UnitConversion.sCentimeterUnit, "|");
string[] sParts = sHeight.Split('|');
double? dCentimeters = null;
double dCentimetersParsed;
if (sParts.Length >= 2 && double.TryParse(sParts[0].Trim(), out dCentimetersParsed))
{
dCentimeters = dCentimetersParsed;
}
int? iFeet;
int? iInches;
if (UnitConversion.CmToFt(dCentimeters, out iFeet, out iInches))
{
sHeight = (iFeet != null) ? iFeet.ToString() + " " + UnitConversion.sFootUnit : "";
sHeight += (iInches != null) ? " " + iInches.ToString() + " " + UnitConversion.sInchUnit : "";
sHeight = sHeight.Trim();
}
else
{
sHeight = "";
}
}
else
{
sHeight = "";
}
}
else
{
sHeight = "";
}
return sHeight;
}
/// <summary>
/// Attempt to convert between Kgs and Lbs
/// </summary>
/// <param name="sWeight"></param>
/// <returns></returns>
public static string ConvertWeight(string sWeight)
{
if (!String.IsNullOrEmpty(sWeight))
{
sWeight = UnitConversion.CleanWeight(sWeight);
if (sWeight.Contains(UnitConversion.sKilogramsUnit))
{
sWeight = sWeight.Replace(UnitConversion.sKilogramsUnit, "|");
string[] sParts = sWeight.Split('|');
double? dKilograms = null;
double dKilogramsParsed;
if (sParts.Length >= 2 && double.TryParse(sParts[0].Trim(), out dKilogramsParsed))
{
dKilograms = dKilogramsParsed;
}
sWeight = UnitConversion.KgToLbs(dKilograms).ToString("#.###") + " " + UnitConversion.sPoundUnit;
}
else if (sWeight.Contains(UnitConversion.sPoundUnit))
{
sWeight = sWeight.Replace(UnitConversion.sPoundUnit, "|");
string[] sParts = sWeight.Split('|');
double? dPounds = null;
double dPoundsParsed;
if (sParts.Length >= 2 && double.TryParse(sParts[0].Trim(), out dPoundsParsed))
{
dPounds = dPoundsParsed;
}
sWeight = UnitConversion.LbsToKg(dPounds).ToString("#.###") + " " + UnitConversion.sKilogramsUnit;
}
else
{
sWeight = "";
}
}
else
{
sWeight = "";
}
return sWeight;
}
public static double? CalculateFt(double dFt, double dInch)
{
double? dFeet = null;
if (dFt >= 0 && dInch >= 0 && dInch <= 12)
{
dFeet = dFt + (dInch / 12);
}
return dFeet;
}
public static double KgToLbs(double? dKg)
{
if (dKg == null)
{
return 0;
}
return dKg.Value * 2.20462262;
}
public static double LbsToKg(double? dLbs)
{
if (dLbs == null)
{
return 0;
}
return dLbs.Value / 2.20462262;
}
public static double FtToCm(double? dFt)
{
if (dFt == null)
{
return 0;
}
return dFt.Value * 30.48;
}
public static bool CmToFt(double? dCm, out int? iFt, out int? iInch)
{
if (dCm == null)
{
iFt = null;
iInch = null;
return false;
}
double dCalcFeet = dCm.Value / 30.48;
double dCalcInches = dCalcFeet - Math.Floor(dCalcFeet);
dCalcFeet = Math.Floor(dCalcFeet);
dCalcInches = dCalcInches * 12;
iFt = (int)dCalcFeet;
iInch = (int)dCalcInches;
return true;
}
private static string CleanUnit(string sOriginal, string[] lstReplaceUnits, string sReplaceWithUnit)
{
System.Text.StringBuilder sbPattern = new System.Text.StringBuilder();
foreach (string sReplace in lstReplaceUnits)
{
if (sbPattern.Length > 0)
{
sbPattern.Append("|");
}
sbPattern.Append(sReplace);
}
sbPattern.Insert(0,#"(^|\s)(");
sbPattern.Append(#")(\s|$)");
System.Text.RegularExpressions.Regex rReplace = new System.Text.RegularExpressions.Regex(sbPattern.ToString(), System.Text.RegularExpressions.RegexOptions.IgnoreCase);
sOriginal = rReplace.Replace(sOriginal, sReplaceWithUnit);
/*foreach (string sReplace in lstReplaceUnits)
{
sOriginal = sOriginal.Replace(sReplace, " " + sReplaceWithUnit);
}*/
return sOriginal;
}
private static bool StringHasNumbers(string sText)
{
System.Text.RegularExpressions.Regex rxNumbers = new System.Text.RegularExpressions.Regex("[0-9]+");
return rxNumbers.IsMatch(sText);
}
private static string ReduceSpaces(string sText)
{
while (sText.Contains(" "))
{
sText = sText.Replace(" ", " ");
}
return sText;
}
private static string SeperateNumbers(string sText)
{
bool bNumber = false;
if (!String.IsNullOrEmpty(sText))
{
for (int iChar = 0; iChar < sText.Length; iChar++)
{
bool bIsNumber = (sText[iChar] >= '0' && sText[iChar] <= '9') ||
(sText[iChar] == '.' && iChar < sText.Length - 1 && sText[iChar + 1] >= '0' && sText[iChar + 1] <= '9');
if (iChar > 0 && bIsNumber != bNumber)
{
sText = sText.Insert(iChar, " ");
iChar++;
}
bNumber = bIsNumber;
}
}
return sText;
}
public static string CleanHeight(string sHeight)
{
if (UnitConversion.StringHasNumbers(sHeight))
{
sHeight = SeperateNumbers(sHeight);
sHeight = CleanUnit(sHeight, UnitConversion.lstFootUnits, UnitConversion.sFootUnit);
sHeight = CleanUnit(sHeight, UnitConversion.lstInchUnits, UnitConversion.sInchUnit);
sHeight = CleanUnit(sHeight, UnitConversion.lstCentimeterUnits, UnitConversion.sCentimeterUnit);
sHeight = SeperateNumbers(sHeight);
sHeight = ReduceSpaces(sHeight);
}
else
{
sHeight = "";
}
return sHeight;
}
public static string CleanWeight(string sWeight)
{
if (UnitConversion.StringHasNumbers(sWeight))
{
sWeight = SeperateNumbers(sWeight);
sWeight = CleanUnit(sWeight, UnitConversion.lstOunceUnits, UnitConversion.sOunceUnit);
sWeight = CleanUnit(sWeight, UnitConversion.lstPoundUnits, UnitConversion.sPoundUnit);
sWeight = CleanUnit(sWeight, UnitConversion.lstKilogramUnits, UnitConversion.sKilogramsUnit);
sWeight = SeperateNumbers(sWeight);
sWeight = ReduceSpaces(sWeight);
}
else
{
sWeight = "";
}
return sWeight;
}
}
It should serve you well to build an extension method of string for this purpose. When you build an extension method you attach a new function call to an existing class. In this we are go to attach a method to the 'string' class that returns a double, as the number of millimeters in a given imperial value, PROVIDED that the value can be parsed based on the examples you provide.
using System;
using System.Text;
namespace SO_Console_test
{
static class ConversionStringExtensions
{
//this is going to be a simple example you can
//fancy it up a lot...
public static double ImperialToMetric(this string val)
{
/*
* With these inputst we want to total inches.
* to do this we want to standardize the feet designator to 'f'
* and remove the inch designator altogether.
6 inches
6in
6”
4 feet 2 inches
4’2”
4 ‘ 2 “
3 feet
3’
3 ‘
3ft
3ft10in
3ft 13in (should convert to 4’1”) ...no, should convert to 49 inches, then to metric.
*/
//make the input lower case and remove blanks:
val = val.ToLower().Replace(" ", string.Empty);
//make all of the 'normal' feet designators to "ft"
string S = val.Replace("\'", "f").Replace("feet", "f").Replace("ft", "f").Replace("foot", "f").Replace("‘", "f").Replace("’", "f");
//and remove any inch designator
S = S.Replace("\"", string.Empty).Replace("inches", string.Empty).Replace("inch", string.Empty).Replace("in", string.Empty).Replace("“", string.Empty).Replace("”", string.Empty);
//finally we have to be certain we have a number of feet, even if that number is zero
S = S.IndexOf('f') > 0 ? S : "0f" + S;
//now, any of the inputs above will have been converted to a string
//that looks like 4 feet 2 inches => 4f2
string[] values = S.Split('f');
int inches = 0;
//as long as this produces one or two values we are 'on track'
if (values.Length < 3)
{
for (int i = 0; i < values.Length; i++)
{
inches += values[i] != null && values[i] != string.Empty ? int.Parse(values[i]) * (i == 0 ? 12 : 1) : 0 ;
}
}
//now inches = total number of inches in the input string.
double result = inches * 25.4;
return result;
}
}
}
With that in place "ImperialToMetric()" becomes a method of any string, and can be invoked anywhere the extension containing class ConversionStringExtensions is referenced. You can use it like:
namespace SO_Console_test
{
class Program
{
static void Main(string[] args)
{
showConversion();
Console.ReadLine();
}
private static void showConversion()
{
//simple start:
Console.WriteLine("6ft 2\"".ImperialToMetric().ToString() + " mm");
//more robust:
var Imperials = new List<string>(){"6 inches",
"6in",
"6”",
"4 feet 2 inches",
"4’2”",
"4 ‘ 2 “",
"3 feet",
"3’",
"3 ‘",
"3ft",
"3ft10in",
"3ft 13in"};
foreach (string imperial in Imperials)
{
Console.WriteLine(imperial + " converted to " + imperial.ImperialToMetric() + " millimeters");
}
}
}
Obviously, at this point a call to "Fred".ImperialToMetric is not going to play nice. You will need to had error handling and perhaps some options to turn 1234 mm 1.234 km etc. but once you flush this out you have a method you can use where ever you choose.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
double km,m,f,i,cm;
Console.WriteLine("The distance between karachi and lahore in (kilometer)km is=");
km = Convert.ToInt32(Console.ReadLine());
m = km * 1000;
Console.WriteLine("The distance between karachi and lahore in meter(m) is="+m);
f = km * 3280.84;
Console.WriteLine("The distance between karachi and lahore in feet(f) is="+f);
i = km * 39370.1;
Console.WriteLine("The distance between karachi and lahore in inches(i) is="+i);
cm = m * 100;
Console.WriteLine("The distance between karachi and lahore in centimeter(cm) is="+cm);
Console.ReadLine();
}
}
}
An extension for string I wrote only to find out that there is already a solution here :) The only thing left to do is to replace "feet", "ft", "’" to "'" and "inches", "inch", "in", "“", "\"" to "''".
using System;
namespace CustomExtensions
{
public static class StringExtension
{
const float mPerFeet = 30.48f / 100;
const float mPerInch = 2.54f / 100;
// input options:
// 5'
// 5'6''
// 18''
// 24''
// 5'6
// 5 ' 6 ''
// 5' 6''
// corner cases:
// '' will return 0
// 5''6'' will interpret as 5'6''
// 5'6' will interpret as 5'6''
// 6 will interpret as 6''
// 6''' will interpret as 6''
public static float MetersFromFeetInches(this string feetInches)
{
float feet = 0;
float inches = 0;
string[] separators = new string[] { "'", "''", " " };
string[] subs = feetInches.Split(separators, StringSplitOptions.RemoveEmptyEntries);
if (subs.Length == 1)
{
if (feetInches.Trim().EndsWith("''"))
{
float.TryParse(subs[0], out inches);
}
else if (!feetInches.Trim().EndsWith("''") && !feetInches.Trim().EndsWith("'"))
{
float.TryParse(subs[0], out inches);
}
else
{
float.TryParse(subs[0], out feet);
}
}
else if (subs.Length > 1)
{
float.TryParse(subs[0], out feet);
float.TryParse(subs[1], out inches);
}
return feet * mPerFeet + inches * mPerInch;
}
}
}