I've been given a small c# project to write, which basically is to wrap a few dlls in a console application.
I've hit what I see as a weird problem. I've declared a couple of local variables within the main method to use. The idea is that when the arguments are parsed they values are store in these variables ( the arguments are in key pairs e.g. -u:username ).
Below is the code that I am using to start the process..
namespace ziptogo
{
public class ZipToGo
{
public static void Main(string[] args)
{
string user = null;
int divisionid = 0;
string mysqlServer = null;
string mysqlName = null;
string mysqlUser = null;
string mysqlPwd = null;
string barcode = null;
bool zipped = false;
ZipToGo ziptogo = new ZipToGo();
if (args.Length == 0)
{
ziptogo.usage();
}
//we look throught the arguments and extract the values.
for (int i = 0; i < args.Length; i++)
{
string[] values = ziptogo.getArgValue(args[i]);
if (values[0].Equals("-U") || values[0].Equals("-u"))
{
user = values[1];
}
if (values[0].Equals("-D") || values[0].Equals("-d"))
{
divisionid = Int32.Parse(values[1]);
}
....
As I am new to writing in c# am I missing something obvious as to why the strings such as mysqlServer are being ignored by the main method??
The integer divisionid and string barcode is the only variables that are not being ignored by the method.
Thanks.
To test it quickly, you can add this line just after Main():
args = new String[] { "-u:username" };
Then go through the code step by step, using the debugger.
[Edit] If getArgValue looks something like this:
public String[] getArgValue(String s)
{
return s.Split(':');
}
then it should work IMHO (quick and dirty, just to get it running).
[Edit:] There are some nice solutions for command line parsing available, which remove the need to add all those conditionals, e.g. http://www.codeproject.com/KB/recipes/commandlineparser.aspx.
Do you know about debugger?
If you use Visual Studion do this.
Set you caret to string user = null;
Click F9 (Toggle breakpoint);
Click F5 (Strat debugging);
Click F10 to go through your application;
I think you quickly find what is wrong. Often this is stupid misspels...
What does getArgValue( string[] ) do?
I bet if you do...
public static void Main(string[] args)
{
if ( args.Length > 0 )
System.Diagnostics.Debug.WriteLine( args[0] );
/* etc */
}
You'll see your missing "-d".
Have a look at this code and see if it helps. It should be doing exactly what you want and is a bit cleaner.
string[] args = new string[] {"-u:username", "-dbs:servername", "-dbu:dbuser", "-dbp:dbpassword"};
string user = null;
int divisionid = 0;
string mysqlPwd = null;
foreach (string arg in args)
{
string[] parts = arg.Split(':');
switch (parts[0].ToUpper())
{
case "-U":
user = parts[1];
break;
case "-D":
divisionid = Int32.Parse(parts[1]);
break;
case "-DBP":
mysqlPwd = parts[1];
break;
// ....
default:
// Display usage
break;
}
}
Debug.WriteLine(string.Format("User {0} | Password {1}", user, mysqlPwd));
Obviously the args part should come from you command line and not be hard coded.
Related
I’ve been struggling wrapping my head around parsers and lexers, which I’m not even sure is the best way to tackle my challenge. I don’t want to use any third party libraries because of the unnecessary overhead, project size, and possible license issues.
Also there won’t be any arithmetic’s, loops, while’s, for’s or foreach’s
It’s a very simple parser that adds or instantiates objects from a text file.
For instance,
buildings.addWithMaterials([buildable:mansion], {{[materials: brick], [materials: wood]}, {[materials: wood], [materials: brick]}});
Parsing this text would add a Mansion made of two pieces of brick and wood to the buildings Collection.
The object buildable contains the properties Name which in this case is Mansion
and some building components which in this case are the materials Brick and Wood.
Any tip/direction to search for doing this?
I've looked and searched within stackoverflow, most entries I've stumbled upon refer to third parties
like Sprache and more.
If I missed an article, :/ sorry, please point it out to me
Thanks and kind regards, Nick
Providing all of the items in your script are in the same format, you can use something like this to parse it:
public static class ScriptParser
{
public static void Parse(string filename)
{
string[] script = null;
using (StreamReader reader = File.OpenText(filename))
{
// read the whole file, remove all line breaks and split
// by semicolons to get individual commands
script = reader.ReadToEnd().Replace("\r", string.Empty.Replace("\n", string.Empty)).Split(';');
}
foreach (string command in script)
HandleCommand(command);
}
// as arguments seem to be grouped you can't just split
// by commas so this will get the arguments recursively
private static object[] ParseArgs(string argsString)
{
int startPos = 0;
int depth = 0;
List<object> args = new List<object>();
Action<int> addArg = pos =>
{
string arg = argsString.Substring(startPos, pos - startPos).Trim();
if (arg.StartsWith("{") && arg.EndsWith("}"))
{
arg = arg.Substring(1, arg.Length - 2);
args.Add(ParseArgs(arg));
}
else
{
args.Add(arg);
}
}
for (int i = 0; i < argsString.Length; i++)
{
switch (argsString[i])
{
case ',':
if (depth == 0)
{
addArg(i);
startPos = i + 1;
}
break;
case '{':
// increase depth of parsing so that commas
// within braces are ignored
depth++;
break;
case '}':
// decrease depth when exiting braces
depth--;
break;
}
}
// as there is no final comma
addArg(argsString.Length);
return args.ToArray();
}
private static void HandleCommand(string commandString)
{
Command command = new Command();
string prefix = commandString.Substring(0, commandString.IndexOf("("));
command.Collection = prefix.Split('.')[0];
command.Action = prefix.Split('.')[1];
string commandArgs = commandString.Substring(commandString.IndexOf("("));
commandArgs = commandArgs.Substring(1, commandArgs.Length - 2);
command.Args = ParseArgs(commandArgs);
command.Execute();
}
private class Command
{
public string Collection;
public string Action;
public object[] Args;
public void Execute()
{
// need to handle any expected commands
if (Collection == "buildings")
{
if (Action == "addWithMaterials")
{
buildings.AddWithMaterials((string)Args[0], (object[])Args[1]);
}
}
}
}
//Eg.
public static BuildingCollection buildings = new BuildingCollection();
public class BuildingCollection
{
public void AddWithMaterials(string building, object[] materials)
{
Building building = new Building();
foreach (object[] materialsPart in materials)
// more stuff for you to work out
// you will need to match the strings to material classes
}
}
}
Usage:
ScriptParser.Parse("scriptfile.txt");
Note: This lacks error handling, which you will definitely want when parsing a file as it could contain anything.
I would want to know how do I display strings from a textfile in a textbox but only untill it reaches a '#" sign in the textfile in c# ?
string lines = outputToBox.ReadToEnd();
//outputToBox is streamreader var that holds the conent of the file
int index = lines.IndexOf('#');
txtDisplay.Text = lines.Substring(0, index);
The problem I now have is that it does not display all the lines in the textbox only the first one
It would help if you included an example of what you have and what you want it to look like. I assume your input looks something like this
x.field1#x.field2#x.field3
y.field1#y.field2#y.field3
z.field1#z.field2#z.field3
If there are multiple lines in the textbox you could turn it into an array and then run foreach through them (if you need an example I can show you)
string[] fileInput = System.IO.File.ReadAllLines(#FILE_PATH)
it would output like this
fileInput[0] = x.field1#x.field2#x.field3
then you can add
string[] myArray = fileInput[x].Split('#') // Into an array, so if you only want 'x.field1', you enter fileInput[0], and return myArray[0]
and implement your foreach. If you want very specific fields from the file that start with certain chars I recommend reading a bit about LINQ and how run small queries.
if your goal is to do this for every existing instance of a string in whatever file, you need a loop.
https://msdn.microsoft.com/en-us/library/bb308959.aspx (LINQ)
This should do the trick and is probably the optimal solution, without more information >:D.
void Main()
{
Debug.Print(Homework_Problem_Number_54_000_601.
Parse("NAME SURNAME NUMBER #2131231313"));
//this prints "NAME SURNAME NUMBER " without the quotes to the console
}
void SetTextBoxText(TextBox textBox, string value) { textBox.Text = value; }
unsafe static class Homework_Problem_Number_54_000_601
{
[ThreadStatic]static StringBuilder __builder;
public static string Parse(string textToParse)
{
if (textToParse == null) return null;
var builder = __builder = __builder ?? new StringBuilder();
builder.clear();
fixed (char* pTextToParse = textToParse)
{
var pTerminus = pTextToParse + textToParse.Length;
for (var pChar = pTextToParse; pChar < pTerminus; ++pChar)
{
switch (*pChar)
{
case '#':
return builder.ToString();
break;
default:
builder.Append(*pChar);
break;
}
}
}
throw new ArgumentException("textToParse was not in the expected format");
}
}
As for reading from the file, that's hard to say without a file format specification from you, but now that you have posted code I'd try this:
string lines = outputToBox.ReadToEnd();
StringBuilder builder = new StringBuilder();
foreach (string line in lines.Split('\n'))
{
int index = line.IndexOf('#');
if (index != -1) builder.AppendLine(line.Substring(0, index));
}
txtDisplay.Text = builder.ToString();
Don't forget to switch the TextBox to multi-line mode if needed.
The purpose of my program is to take a data txt file and edit it, and/or make additions and subtractions to it.
The text file format is like this:
Name|Address|Phone|# of people coming|isRSVP
The code I have seems to be doing it's job all the way up until I try to click one of the names within a listbox and it needs to search through the multidimensional array to pull information out and place within a set of textboxes where they can be edited. The problem is that the foreach loop I use gives me an out of bounds exception. I tried to do a step into debug to make sure the data is correct in the array and see the process. Everything seems to do correctly but for some reason in the conditional statement person[0]==listbox1.selectedIndex isn't returning true even though both are the same as I seen through the step into process. Any help would be greatly appreciated.
This is my code:
StringBuilder newFile = new StringBuilder();
static string txtList= "guest_list.txt";
static string[] file = File.ReadAllLines(txtList);
static int x = file.Count();
string[,] list = new string[x ,5];
public void loadGuestList()
{
int count2 = 0;
foreach (string line in file)
{
string[] sliced = line.Split('|');
int count = 0;
list[count2, count] = sliced[0];
count++;
list[count2, count] = sliced[1];
count++;
list[count2,count] = sliced[2];
count++;
list[count2,count]= sliced[3];
count++;
list[count2, count] = sliced[4];
count++;
listBox1.Items.Add(list[count2,0]);
count2++;
}
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
foreach (string person in list)
{
if ( person[0].ToString()==listBox1.SelectedItem.ToString())
{
addTxt.Text = char.ToString(person[1]);
textPhone.Text = char.ToString(person[2]);
textPeople.Text = char.ToString(person[3]);
if (person[4] == 'n' )
{
}
else
{
chkRSVP.Checked = true;
}
break;
}
}
}
The problem lies in this line:
foreach (string person in list)
The list is defined as being string[,] which when you for each over will do every element, not just the column of data. You really should do something such as:
for(int index = 0; index <= list.GetUpperBound(0); index++)
{
string slice1 = list[index, 0];
string slice2 = list[index, 1];
....
}
or switch to using a Dictionary<string, string[]>().
Try to use a "Person" object and override equals(). Right now you're trying to put your multidimensional array (list[0]) into a string, it'll give you a unwanted result. You should use list[0,0] instead.
In agreement with Adam Gritt, I tested the following code and it seemed to work:
using System;
namespace so_foreach_bounds
{
class MainClass
{
public static void Main (string[] args)
{
//System.Text.StringBuilder newFile = new System.Text.StringBuilder();
string txtList= "guest_list.txt";
string[] file = System.IO.File.ReadAllLines(txtList);
int x = file.Length;
System.Collections.Generic.List<string[]> list = new System.Collections.Generic.List<string[]> ();
foreach (string line in file)
{
string[] sliced = line.Split('|');
list.Add(sliced);
}
foreach (string[] person in list)
{
Console.WriteLine (String.Join(", ", person));
if (person[0] =="Gary")
{
string txtAdd = person[1];
string txtPhone = person[2];
string txtpeople = person[3];
if (person[4] == "n" )
{
}
else
{
bool has_resvped = true;
}
break;
}
}
}
}
}
The issue is how you are iterating over the 2d array. It is usually a better idea to create a "Person" class, than try to manage it via arrays though. Oh yes, and it's usually a good idea to check that a list box item is selected before attempting to use one of its members.
i am using command line arguments and if conditions are used to check the input values but it is not looking good can i change it to switch but i have no idea how to change it my code is
if (args.Length > 0 && args.Length == 4)
{
string programName = args[0];
string file1= args[2];
string file2= args[3];
bool flag = false;
int num= 0;
bool isNum = Int32.TryParse(args[1].ToString(), out num);
if (!(programName.Equals("Army")))
{
Console.WriteLine("Error");
}
if (!Int32.TryParse(args[1].ToString(), out isNum ))
{
Console.WriteLine("value should be a number");
}
if (!File.Exists(file1))
{
Console.WriteLine("file 1 does not exist");
}
if (!File.Exists(file2))
{
Console.WriteLine("file 2 does not exist");
}
A switch statement isn't really called for here. That's useful when you have a single value and need to select from a series of possible mutually-exclusive steps based on that value. But that's not what you're doing here. These aren't a chain of if/else if statements keying off a value, these are more like guard clauses. All of them need to run in order to determine all of the output to show to the user.
You can shorten the code by removing the curly braces:
if (!(programName.Equals("Army")))
Console.WriteLine("Error");
if (!Int32.TryParse(args[1].ToString(), out isNum ))
Console.WriteLine("value should be a number");
if (!File.Exists(file1))
Console.WriteLine("file 1 does not exist");
if (!File.Exists(file2))
Console.WriteLine("file 2 does not exist");
You could also extract these lines of code into their own method, which would make the Main method a little cleaner. You could even extract the conditional checks themselves into very small methods to make it more prose-like for readability. But the conditional structure itself doesn't really need to change.
You can create class which will be responsible for retrieving and checking your application arguments. E.g. if your application has name Zorg, you can create following class:
public class ZorgConfiguration
{
private string num;
private string programName;
private string file1;
private string file2;
public static ZorgConfiguration InitializeFrom(string[] args)
{
if (args.Length < 4)
throw new ZorgConfigurationException("At least 4 arguments required");
return new ZorgConfiguration {
ProgramName = args[0],
Num = args[1],
File1 = args[2],
File2 = args[3]
};
}
// to be continued
}
As you can see, it's responsibility is to hold application settings. It has static method for creating instance of configuration from args array. This method checks if arguments count correct and then initializes each property of configuration class with appropriate argument. Checking argument value moved to properties:
public string ProgramName
{
get { return programName; }
private set {
if (value == "Army")
throw new ZorgConfigurationException("Error");
programName = value;
}
}
public string Num
{
get { return num; }
private set {
int i;
if (!Int32.TryParse(value, out i))
throw new ZorgConfigurationException("value should be a number");
num = value;
}
}
public string File1
{
get { return file1; }
private set {
if (!File.Exists(value))
throw new ZorgConfigurationException("file 1 does not exist");
file1 = value;
}
}
Each property is responsible for verifying corresponding argument value. If value is incorrect, then custom ZorgConfigurationException (that is simply class inherited from Exception) is thrown.
Now main application code looks very clean:
try
{
var config = ZorgConfiguration.InitializeFrom(args);
// you can use config.File1 etc
}
catch (ZorgConfigurationException e)
{
Console.WriteLine(e.Message);
// exit application
}
I use this class to parse command line arguments, I've found it somewhere, but I can't remember where:
public class Arguments
{
// Variables
private StringDictionary Parameters;
// Constructor
public Arguments(string[] Args)
{
Parameters = new StringDictionary();
Regex Spliter = new Regex(#"^-{1,2}|^/|=|:",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
Regex Remover = new Regex(#"^['""]?(.*?)['""]?$",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
string Parameter = null;
string[] Parts;
// Valid parameters forms:
// {-,/,--}param{ ,=,:}((",')value(",'))
// Examples:
// -param1 value1 --param2 /param3:"Test-:-work"
// /param4=happy -param5 '--=nice=--'
foreach (string Txt in Args)
{
// Look for new parameters (-,/ or --) and a
// possible enclosed value (=,:)
Parts = Spliter.Split(Txt, 3);
switch (Parts.Length)
{
// Found a value (for the last parameter
// found (space separator))
case 1:
if (Parameter != null)
{
if (!Parameters.ContainsKey(Parameter))
{
Parts[0] =
Remover.Replace(Parts[0], "$1");
Parameters.Add(Parameter, Parts[0]);
}
Parameter = null;
}
// else Error: no parameter waiting for a value (skipped)
break;
// Found just a parameter
case 2:
// The last parameter is still waiting.
// With no value, set it to true.
if (Parameter != null)
{
if (!Parameters.ContainsKey(Parameter))
Parameters.Add(Parameter, "true");
}
Parameter = Parts[1];
break;
// Parameter with enclosed value
case 3:
// The last parameter is still waiting.
// With no value, set it to true.
if (Parameter != null)
{
if (!Parameters.ContainsKey(Parameter))
Parameters.Add(Parameter, "true");
}
Parameter = Parts[1];
// Remove possible enclosing characters (",')
if (!Parameters.ContainsKey(Parameter))
{
Parts[2] = Remover.Replace(Parts[2], "$1");
Parameters.Add(Parameter, Parts[2]);
}
Parameter = null;
break;
}
}
// In case a parameter is still waiting
if (Parameter != null)
{
if (!Parameters.ContainsKey(Parameter))
Parameters.Add(Parameter, "true");
}
}
// Retrieve a parameter value if it exists
// (overriding C# indexer property)
public string this[string Param]
{
get
{
return (Parameters[Param]);
}
}
}
I use it this way:
var cmdParams = new Arguments(args);
if (cmdParams["File"] != null && parametros["cmdParams"] == "Filename.txt) { }
Hope it helps!
Command line arguments can get complicated if there are different functions and arguments..
Best way is to tokenize your arguments, function switch examples are /p /a, or -h, -g etc...Your cmd arg parser looks for these tokens (pattern) - once found you know which cmd it is.. Have switch - case or any other mechanism for this. Also tokenise the other data arguments. Hence you have two sets of arguments - easy to manage.
using asp.net 4
we do a lot of Word merges at work. rather than using the complicated conditional statements of Word i want to embed my own syntax. something like:
Dear Mr. { select lastname from users where userid = 7 },
Your invoice for this quarter is: ${ select amount from invoices where userid = 7 }.
......
ideally, i'd like this to get turned into:
string.Format("Dear Mr. {0}, Your invoice for this quarter is: ${1}", sqlEval[0], sqlEval[1]);
any ideas?
Well, I don't really recommend rolling your own solution for this, however I will answer the question as asked.
First, you need to process the text and extract the SQL statements. For that you'll need a simple parser:
/// <summary>Parses the input string and extracts a unique list of all placeholders.</summary>
/// <remarks>
/// This method does not handle escaping of delimiters
/// </remarks>
public static IList<string> Parse(string input)
{
const char placeholderDelimStart = '{';
const char placeholderDelimEnd = '}';
var characters = input.ToCharArray();
var placeHolders = new List<string>();
string currentPlaceHolder = string.Empty;
bool inPlaceHolder = false;
for (int i = 0; i < characters.Length; i++)
{
var currentChar = characters[i];
// Start of a placeholder
if (!inPlaceHolder && currentChar == placeholderDelimStart)
{
currentPlaceHolder = string.Empty;
inPlaceHolder = true;
continue;
}
// Start of a placeholder when we already have one
if (inPlaceHolder && currentChar == placeholderDelimStart)
throw new InvalidOperationException("Unexpected character detected at position " + i);
// We found the end marker while in a placeholder - we're done with this placeholder
if (inPlaceHolder && currentChar == placeholderDelimEnd)
{
if (!placeHolders.Contains(currentPlaceHolder))
placeHolders.Add(currentPlaceHolder);
inPlaceHolder = false;
continue;
}
// End of a placeholder with no matching start
if (!inPlaceHolder && currentChar == placeholderDelimEnd)
throw new InvalidOperationException("Unexpected character detected at position " + i);
if (inPlaceHolder)
currentPlaceHolder += currentChar;
}
return placeHolders;
}
Okay, so that will get you a list of SQL statements extracted from the input text. You'll probably want to tweak it to use properly typed parser exceptions and some input guards (which I elided for clarity).
Now you just need to replace those placeholders with the results of the evaluated SQL:
// Sample input
var input = "Hello Mr. {select firstname from users where userid=7}";
string output = input;
var extractedStatements = Parse(input);
foreach (var statement in extractedStatements)
{
// Execute the SQL statement
var result = Evaluate(statement);
// Update the output with the result of the SQL statement
output = output.Replace("{" + statement + "}", result);
}
This is obviously not the most efficient way to do this, but I think it sufficiently demonstrates the concept without muddying the waters.
You'll need to define the Evaluate(string) method. This will handle executing the SQL.
I just finished building a proprietary solution like this for a law firm here.
I evaluated a product called Windward reports. It's a tad pricy, esp if you need a lot of copies, but for one user it's not bad.
it can pull from XML or SQL data sources (or more if I remember).
Might be worth a look (and no I don't work for 'em, just evaluated their stuff)
You might want to check out the razor engine project on codeplex
http://razorengine.codeplex.com/
Using SQL etc within your template looks like a bad idea. I'd suggest you make a ViewModel for each template.
The Razor thing is really easy to use. Just add a reference, import the namespace, and call the Parse method like so:
(VB guy so excuse syntax!)
MyViewModel myModel = new MyViewModel("Bob",150.00); //set properties
string myTemplate = "Dear Mr. #Model.FirstName, Your invoice for this quarter is: #Model.InvoiceAmount";
string myOutput = Razor.Parse(myTemplate, myModel);
Your string can come from anywhere - I use this with my templates stored in a database, you could equally load it from files or whatever. It's very powerful as a view engine, you can do conditional stuff, loops, etc etc.
i ended up rolling my own solution but thanks. i really dislike if statements. i'll need to refactor them out. here it is:
var mailingMergeString = new MailingMergeString(input);
var output = mailingMergeString.ParseMailingMergeString();
public class MailingMergeString
{
private string _input;
public MailingMergeString(string input)
{
_input = input;
}
public string ParseMailingMergeString()
{
IList<SqlReplaceCommand> sqlCommands = new List<SqlReplaceCommand>();
var i = 0;
const string openBrace = "{";
const string closeBrace = "}";
while (string.IsNullOrWhiteSpace(_input) == false)
{
var sqlReplaceCommand = new SqlReplaceCommand();
var open = _input.IndexOf(openBrace) + 1;
var close = _input.IndexOf(closeBrace);
var length = close != -1 ? close - open : _input.Length;
var newInput = _input.Substring(close + 1);
var nextClose = newInput.Contains(openBrace) ? newInput.IndexOf(openBrace) : newInput.Length;
if (i == 0 && open > 0)
{
sqlReplaceCommand.Text = _input.Substring(0, open - 1);
_input = _input.Substring(open - 1);
}
else
{
sqlReplaceCommand.Command = _input.Substring(open, length);
sqlReplaceCommand.PlaceHolder = openBrace + i + closeBrace;
sqlReplaceCommand.Text = _input.Substring(close + 1, nextClose);
sqlReplaceCommand.NewInput = _input.Substring(close + 1);
_input = newInput.Contains(openBrace) ? sqlReplaceCommand.NewInput : string.Empty;
}
sqlCommands.Add(sqlReplaceCommand);
i++;
}
return sqlCommands.GetParsedString();
}
internal class SqlReplaceCommand
{
public string Command { get; set; }
public string SqlResult { get; set; }
public string PlaceHolder { get; set; }
public string Text { get; set; }
protected internal string NewInput { get; set; }
}
}
internal static class SqlReplaceExtensions
{
public static string GetParsedString(this IEnumerable<MailingMergeString.SqlReplaceCommand> sqlCommands)
{
return sqlCommands.Aggregate("", (current, replaceCommand) => current + (replaceCommand.PlaceHolder + replaceCommand.Text));
}
}