|
I was able to fix this by:
First, changing the Remover so that it would match on quoted strings, instead of making the quotes optional: Regex Remover= new Regex(@"^['""](.*?)['""]$",RegexOptions.IgnoreCase|RegexOptions.Compiled);
Removing the ? following the character classes makes the classes required for a match. Now, you can differentiate between the quoted paramter value, and the parameter itself.
Then, before the switch change the single line that splits the Arg to
if (!Remover.IsMatch(Arg))
{
Parts = Splitter.Split(Arg, 3);
}
else
{
Parts = new String[] { Remover.Replace(Arg, "$1")};
}
Now, it will split the parameters correctly (since they will not start with a single, or double, quote.
And,if the parameters are separated by spaces, the value will have the quotes stripped before the switch.
|
|
|
|
|
He man, you've been doing a great job.
I wrote a slightly different version of your code.
The way i needed it was with 1 parameter who has got possible multiple values.
In my case i wanted 1 parameter database, and then be able to write down multiple database-names.
In my case there will be no true value if the parameter is standalone, it will have a value-colection with count == 0
this way i can put a line down like this:
program.exe -database db1 db2 db2 -debug -build:1401
where db1, db2 and db3 will be added to the parameter database
So here is what i did:
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
namespace Piramide.Bis.Conversie.Util
{
/// <summary>
/// CommandLine Argument Parser
/// </summary>
public class CmdArgParser
{
// Variables
private Hashtable _parameters;
public Hashtable Parameters
{
get { return _parameters; }
}
/// <summary>
/// Constructor
/// </summary>
public CmdArgParser(string[] Args)
{
_parameters = new Hashtable();
Regex splitter = new Regex(@"^-|^/|=|:",
RegexOptions.IgnoreCase|RegexOptions.Compiled);
Regex remover = new Regex(@"^['""]?(.*?)['""]?$",
RegexOptions.IgnoreCase|RegexOptions.Compiled);
string parameter = null;
string[] parts;
ArrayList values = new ArrayList();
// Valid parameters:
// {-,/}param{ ,=,:}((",')value(",'))
// Examples:
// -param1 value1 value2 -param2 /param3:"Test" /param4=happy
foreach (string arg in Args)
{
parts = splitter.Split(arg,3);
switch(parts.Length)
{
// Found value
case 1:
// if parameter still wait, add value to values-colection
if(parameter != null)
{
parts[0] = remover.Replace(parts[0], "$1");
values.Add(parts[0]);
}
break;
// found parameter
case 2:
// if paramater was still waiting,
// then add parameter and values-collection to _parameters
// clear values
if(parameter!=null)
{
if(!_parameters.ContainsKey(parameter))
{
_parameters.Add(parameter, values);
values = new ArrayList();
}
}
parameter = parts[1];
break;
// found parameter with enclosed value
case 3:
// if paramater was still waiting,
// then add parameter and values-collection to _parameters
// clear values
if(parameter != null)
{
if(!_parameters.ContainsKey(parameter))
{
_parameters.Add(parameter, values);
values = new ArrayList();
}
}
// Parameter with enclosed value is allowed only one value.
// add parameter and value to _parameters
parameter = parts[1];
parts[2] = remover.Replace(parts[2], "$1");
values.Add(parts[2]);
if(!_parameters.ContainsKey(parameter))
_parameters.Add(parameter, values);
parameter=null;
values = new ArrayList();
break;
}
}
// if final parameter is still waiting,
// add parameter and values to _parameters
if(parameter != null)
{
if(!_parameters.ContainsKey(parameter))
_parameters.Add(parameter, values);
}
}
// Retrieve a parameter value-collection if it exists
// (overriding C# indexer property)
public ArrayList this [string Param]
{
get
{
return (ArrayList)(_parameters[Param]);
}
}
}
}
Raymond Molenaar
r.molenaar@piramide.nl
|
|
|
|
|
Hi,
Thanks.
This is also a good variation on the subject.
One clear goal when designing such simple class is that everyone can easily get into the code and adapt it quickly to its very own requirements.
Thanks for sharing,
R. LOPES
Just programmer.
|
|
|
|
|
any idea on how to rewrite this for c++? i am really having trouble converting it at one place
// Retrieve a parameter value if it exists
public string this [string Param]{
get{
return(Parameters[Param]);
}
}
|
|
|
|
|
Hi,
Sorry to answer so late.
As far as I know you can't do this kind of "operator" overloading in C++.
I may be wrong because I didn't wrote a single line of C++ code in the past 4 years, and I know you can overload almost evrything, but this case is special.
Sorry,
R. LOPES
Just programmer.
|
|
|
|
|
porting to C++/CLI went quite straight-forward.
For the index access I used the so-called 'default indexed property':
<br />
public ref class Arguments<br />
{<br />
Collections::Specialized::StringDictionary^ parameters;<br />
public:<br />
Arguments(array<System::String ^> ^args);<br />
<br />
property String^ default [String^]<br />
{<br />
String^ get(String^ key)<br />
{<br />
return parameters[key];<br />
}<br />
}<br />
};<br />
You use it in the same way as in C#:
<br />
int main(array<System::String ^> ^args)<br />
{<br />
...<br />
Arguments^ arguments = gcnew Arguments(args);<br />
if (arguments["help"])<br />
Console::Write(helpMessage);<br />
...<br />
}<br />
The complete class can be downloaded from here
|
|
|
|
|
If the application name has quotes around it and there is a string argument with quotes, the Regex in Parser.ExtractApplicationName does a greedy match. The Regex needs to be changed from
Regex r = new Regex(@"^(?<commandline>("".+""|(\S)+))(?<remainder>.+)",
RegexOptions.ExplicitCapture);
to
Regex r = new Regex(@"^(?<commandline>(""[^""]+""|(\S)+))(?<remainder>.+)",
RegexOptions.ExplicitCapture);
|
|
|
|
|
Hi Chris,
Thanks fo sharing,
R. LOPES
Just programmer.
|
|
|
|
|
Just wanted to add my thanks, like some others this class didn't support the exact syntax of arguments I wanted but it only took a few mins to update it and as skeleton for custom argument requirements I highly recommend it.
|
|
|
|
|
The original version didnt fit well with the way I use command line params, so i made some adjustments.
This version dont support the space separator because its confusing. Say my app support params /debug /server [server] [some path]. With support for space separator the command line /server my-server /debug "c:\my path\here" would incorrectly interpret as /debug="c:\my path\here" when both /debug and the path was standalone params.
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
namespace Mozzarella.Utility
{
/// <summary>
/// Description résumée de Arguments.
/// </summary>
public class Arguments
{
// Variables
private StringDictionary prefixedParams = new StringDictionary();
private ArrayList anonParams=new ArrayList();
// Constructors
public Arguments(string[] args)
{
Regex spliter=new Regex(@"^([/-]|--){1}(?<name>\w+)([:=])?(?<value>.+)?$",RegexOptions.IgnoreCase|RegexOptions.Compiled);
char[] trimChars={'"','\''};
Match part;
foreach(string arg in args)
{
part = spliter.Match(arg);
if(part.Success)
prefixedParams[part.Groups["name"].Value] = part.Groups["value"].Value.Trim(trimChars);
else
anonParams.Add(arg);
}
}
// Retrieve a parameter value if it exists
public string this [string param]
{
get
{
return(prefixedParams[param]);
}
}
public string this [int i]
{
get
{
return anonParams.Count > i ? (string)anonParams[i] : null;
}
}
public string[] AnonParams
{
get
{
return (string[])anonParams.ToArray(typeof(string));
}
}
}
}
Example command line:
command line: "some string" /asd:t /debug /tyll=5 tull -frue --fry:45 asd=tyu some=test "c:\some\path\file.bat" /tull=bananas
Test output:
args[0]:some string
args[1]:tull
args[2]:asd=tyu
args[3]:some=test
args[4]:c:\some\path\file.bat
args[5]:(null)
args["asd"]:t
args["debug"]:
args["tyll"]:5
args["tull"]:bananas
args["frue"]:
args["fry"]:45
args["some"]:(null)
Usage:
by anon. index: if (args[4]!=null) Console.Writeline(args[4]) -> c:\some\path\file.bat
by key/value: if (args["tull"]!=null) Console.Writeline(args["tull"]) -> bananas
by existence: if (args["debug"]!=null) Console.Writeline("debug mode")
onsole.Writeline(args["tull"]) -> bananas
by existence: if (args["debug"]!=null) Console.Writeline("debug mode")
|
|
|
|
|
Another great spin-off !
R. LOPES
Just programmer.
|
|
|
|
|
awesome...this is what i was looking for...i had a quick question though...in you reg expression...
@"^([/-]|--){1}(?<name>\w+)([:=])?(?<value>.+)?$"
I was trying to get it where it would accept -flag"value". I have...
@"^([/-]|--){1}(?<name>\w+)([:\x22])?(?<value>.+)?$"
but it matches the tag as flagvalue. If I use any other character like the = or a single quote then it works.
I am not very good with regex yet so any help is appreciated. (I did try for a while before posting).
|
|
|
|
|
never mind! turns out the .net command line parser strips the quotes before it even gets to it.
|
|
|
|
|
its not the regex' problem. the problem is that -test"ddd" on the commandline becomes args[0] -> -testddd, so its .NETs fault (its being nice and removed "" for you because it thinks its a path).
A workaround is to get the full commandline via Environment.CommandLine, but then you have to parse and split the commandline manually yourself before handing it over the the regex parser. Or you could just use -test:"ddd"
Edit:
doh! i saw just now that you figured it you yourself
|
|
|
|
|
very useful!
works like a dream, i appreciate the time and effort -
saved me a bit of time and thinking
¡gracias con mucho gusto!
t hughes
|
|
|
|
|
Thank you so much.
Look inside the messages for an enhanced version submitted by a user.
Cheers,
R. LOPES
Just programmer.
|
|
|
|
|
I have used this and adapted it for use in my app. I am writing a small GUI and I need to grab those paramaters from the command line.... I have used everything including myValue=params["param1"];
As of now, the arguments I pass with the command are written to the console. I am just brain dead on how to seperate and assign the values of these parameters, so that I can populate global variables and text boxes with the values..
Any help would be MUCH appreciated.
Thanks, another programmer with too much work and not enough qualifications...

|
|
|
|
|
Hi,
Thanks for your interest. Please note that the latest source is in fact in one of the messages below. I still didn't updated the article code.
From what I understand from your problem it doesn't sound too difficult, but maybe I didn't catch every subtilities here.
Could you please post your code or send me your form with an explanation on what you want to achieve. Just send me the minimum.
Cheers,
R. LOPES
Just programmer.
|
|
|
|
|
The app I am writing basically combines several command line parameters and user input into a concat string which is sent as a request through an IP socket. I have most of everything else working, the socket, message etc. I have included some bits of my code below, as I am getting desperate. I need to take the command line parameters and assign them to strings that I declare in the beginning. I need these command line parameters to not only populate a few text boxes, but assign them to variables which are concatenated with other variables based on user input into one single string later on. Basicaly, I am woefully underqualified, and I do not know or underttand how or where or what syntax to take commandline parameter 1 which is called localIP and assign it to this.localIP so I can use it to populate a text box, and use in a new concatenated string. I just need to know what to type and where for each parameter.
I incorporated the code arguments.cs and test.cs and it works... It will list the command line params or tell me they were not defined! I am fine with that, I ust need to grab the values and have no clue.
Thanks!
[STAThread]
static void Main(string[] args)
{
Application.Run(new Form1());
// Command line parsing
Arguments CommandLine=new Arguments(args);
// Look for specific arguments values and display
// them if they exist (return null if they don't)
if(CommandLine["localIP"] != null)
Console.WriteLine("localIP value: " +
CommandLine["localIP"]);
else
Console.WriteLine("Local IP Address not defined !");
if(CommandLine["iMUSEnum"] != null)
Console.WriteLine("iMUSEnum value: " +
CommandLine["iMUSEnum"]);
else
Console.WriteLine("iMUSE Terminal Number not defined !");
if(CommandLine["appFunction"] != null)
Console.WriteLine("appFunction value: " +
CommandLine["appFunction"]);
else
Console.WriteLine("Application Function not defined !");
if(CommandLine["ICAO"] != null)
Console.WriteLine("ICAO value: " +
CommandLine["ICAO"]);
else
Console.WriteLine("ICAO not defined !");
if(CommandLine["checkType"] != null)
Console.WriteLine("checkType value: " +
CommandLine["checkType"]);
else
Console.WriteLine("Check-In Type not defined !");
if(CommandLine["vuelo"] != null)
Console.WriteLine("vuelo value: " +
CommandLine["vuelo"]);
else
Console.WriteLine("Vuelo not defined !");
if(CommandLine["schedTime"] != null)
Console.WriteLine("schedTime value: " +
CommandLine["schedTime"]);
else
Console.WriteLine("Scheduled Flight Time not defined !");
if(CommandLine["destIP"] != null)
Console.WriteLine("destIP value: " +
CommandLine["destIP"]);
else
Console.WriteLine("Destination IP Address not defined !");
if(CommandLine["destPort"] != null)
Console.WriteLine("destPort value: " +
CommandLine["destPort"]);
else
Console.WriteLine("Destination Port not defined !");
}
#endregion
private void param()
{
// MyValue=params["MyParam"]
//Assign Command Line Args To Variables
this.localIP = "";
this.iMUSEnum = "";
this.appFunction = "";
this.terminal = "";
this.ICAO = "";
this.checkType = "";
this.vuelo = "";
this.schedTime = "";
this.destIP = "";
this.destPort = "";
//Assign Variables To Diagnostic Text Boxes
Diagtxt1.Text = this.localIP;
Diagtxt2.Text = this.iMUSEnum;
Diagtxt3.Text = this.appFunction;
Diagtxt4.Text = this.terminal;
Diagtxt5.Text = this.ICAO;
Diagtxt6.Text = this.checkType;
Diagtxt7.Text = this.vuelo;
Diagtxt8.Text = this.schedTime;
Diagtxt10.Text = this.destIP;
Diagtxt11.Text = this.destPort;
Diagcmb1.Text = this.action;
}
// Creates The Request
public void createRequest()
{
// Message For Check-In Open In Simulataneous Mode
if (this.appFunction == "1" && this.action == "01" && this.checkType == "S")
{
string message = string.Concat(localIP, space, iMUSEnum, appFunction, terminal, space, setVal, ICAO, setVal2, action, checkType, ICAO);
this.Diagtxt12.Text = message;
}
// Message For Check-In Open In Differentiated Mode
if (this.appFunction == "1" && this.action == "01" && this.checkType == "D")
{
string message = string.Concat(localIP, space, iMUSEnum, appFunction, terminal, space, setVal, ICAO, setVal2, action, checkType, vuelo, schedTime);
this.Diagtxt12.Text = message;
}
// Message For Sending Complimentary Text
if (this.appFunction == "1" && this.action == "02")
{
string message = string.Concat(localIP, space, iMUSEnum, appFunction, terminal, space, setVal, ICAO, setVal2, action, space, compText);
this.Diagtxt12.Text = message;
}
// Message For Sending Manual Text
if (this.appFunction == "1" && this.action == "06")
{
string message = string.Concat(localIP, space, iMUSEnum, appFunction, terminal, space, setVal, ICAO, setVal2, action, freeText);
this.Diagtxt12.Text = message;
}
// Message For Close In Simultaneous Mode
if (this.appFunction == "1" && this.action == "06" && this.checkType == "S")
{
string message = string.Concat(localIP, space, iMUSEnum, appFunction, terminal, space, setVal, ICAO, setVal2, action, checkType, ICAO);
this.Diagtxt12.Text = message;
}
// Message For Close In Differentiated Mode
if (this.appFunction == "1" && this.action == "06" && this.checkType == "D")
{
string message = string.Concat(localIP, space, iMUSEnum, appFunction, terminal, space, setVal, ICAO, setVal2, action, checkType, vuelo, schedTime);
this.Diagtxt12.Text = message;
}
// Message For Boarding Gate Open
if (this.appFunction == "2" && this.action == "09")
{
string message = string.Concat(localIP, space, iMUSEnum, appFunction, terminal, space, setVal, ICAO, setVal2, action, checkType, ICAO);
this.Diagtxt12.Text = message;
}
// Message For Final Boarding Call
if (this.appFunction == "2" && this.action == "07")
{
string message = string.Concat(localIP, space, iMUSEnum, appFunction, terminal, space, setVal, ICAO, setVal2, action, checkType, ICAO);
this.Diagtxt12.Text = message;
}
// Message For Boarding Gate Close
if (this.appFunction == "2" && this.action == "06")
{
string message = string.Concat(localIP, space, iMUSEnum, appFunction, terminal, space, setVal, ICAO, setVal2, action, checkType, vuelo, schedTime);
this.Diagtxt12.Text = message;
}
string request = this.Diagtxt12.Text;
this.IPrequest = request;
}
|
|
|
|
|
Hi,
My guess is that you are parsing the command line at the wrong place in your code.
At least you should start by parsing the command line and then call:
Application.Run(new Form1());
You currently do the opposite. Application.Run(...) being a blocking call, that returns only when you close your Form1, the parsing code is only executed just before ending the application, which seems useless.
Another idea if you then want to initialize your form controls with command line arguments, is to pass the command line to the form and parse it from there.
Create another construtor, something like:
public Form1(string[] args){<br />
Arguments CommandLine=new Arguments(args);<br />
...<br />
}
And then change your Main function to Application.run(new Form1(args));
Please note that there is another way to get the command line arguments. Anywhere in your code, including in the parameterless Form1 constructor, you can write this:
Arguments CommandLine=new Arguments(Environment.CommandLine);
.NET automatically copy the command line to the Environment.CommandLine property when the application is started.
As soon as you have parsed your arguments, you can then type this.localIP=CommandLine["localIP"] and set a textbox value like this.localIPTextBox.Text=CommandLine["localIP"].ToString(); (do this after InitializeComponent() in your form)
Hope this helps,
R. LOPES
Just programmer.
|
|
|
|
|
Your light-weight, effecive, easy-to-understand code was just what I was looking for. Your well-documented example made it easy to plug-in to my code too. Thanks for sharing this. Unlike some other solutions, this graciously handles quoted values, too.
I'd recommend this to anyone who wants a quick pre-fabricated solution to handling command-line parameters.
You get my 5.
Très bien et merci!
|
|
|
|
|
Hi Christopher,
Thank you for your interest in this very simple piece of code (yet effective !), and your kind words.
Merci,
R. LOPES
Just programmer.
|
|
|
|
|
I like this parser a lot since it is simple but still powerful. However, it lackes one feature.
If I make a file association, so the application automatically is launched when the user double-clicks on a data-file, the data-filename will be sent to the application as a command line argument. This argument does not start with '-' or '/' but I still would like the parser to store it for me. I have therefore expanded the class to also support "unnamed" arguments and can retrieve them by their index value.
/// <summary>
/// Command line parser.
/// </summary>
/// <remarks>
/// Create an object from this class with the command line array
/// from the <c>Main</c> method. It will automatically parse the
/// command line arguments, find all parameters starting with -, -- or
/// / and all the values linked. Any value could be separated from the
/// parameter with a space, a : or a =. The parser also look for
/// enclosing characters like ' or " and remove them. Of course if you
/// have a value like 'Mike's house', only the first and last ' will be
/// removed.
/// </remarks>
/// <example>
/// Here is an example on how to retrieve the command line arguments.
/// <code>
/// static void Main(string[] Args)
/// {
/// CommandLineArguments cml = new CommandLineArguments(Args);
///
/// if (cml["Height"]!=null)
/// Console.WriteLine( "Height: "+CommandLine["Height"] );
/// else
/// Console.WriteLine( "Height is not defined !" );
///
/// if (cml["Width"]!=null)
/// Console.WriteLine( "Width: "+CommandLine["Width"] );
/// else
/// Console.WriteLine( "Width is not defined !" );
/// }
/// </code>
/// If a file extension has been associated with the program, and
/// the user double-clicks on the file, the filename will be added
/// as a command line argument by Windows. This is an unnamed argument
/// and must therefore be retrieved by its index value (of unnamed
/// arguments):
/// <code>
/// static void Main(string[] Args)
/// {
/// CommandLineArguments cml = new CommandLineArguments(Args);
///
/// if (cml[0]!=null)
/// Console.WriteLine( "Open file: " +CommandLine[0] );
/// else
/// Console.WriteLine( "No file to open..." );
/// }
/// </code>
/// </example>
public class CommandLineArguments
{
private StringDictionary namedParameters =
new StringDictionary();
private System.Collections.ArrayList unnamedParameters =
new System.Collections.ArrayList();
/// <summary>
/// Creates a <see cref="CommandLineArguments"/> object to parse
/// command lines.
/// </summary>
/// <param name="args">The command line to parse.</param>
public CommandLineArguments(string[] args)
{
Regex splitter = 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 str in args)
{
// Do we have a parameter (starting with -, /, or --)?
if (str.StartsWith("-") || str.StartsWith("/"))
{
// Look for new parameters (-,/ or --) and a possible
// enclosed value (=,
parts = splitter.Split(str, 3);
switch (parts.Length)
{
// Found a value (for the last parameter found
// (space separator))
case 1:
if (parameter != null)
{
if (!namedParameters.ContainsKey(parameter))
{
parts[0] =
remover.Replace(parts[0], "$1");
namedParameters.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 (!namedParameters.ContainsKey(parameter))
namedParameters.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 (!namedParameters.ContainsKey(parameter))
namedParameters.Add(parameter, "true");
}
parameter = parts[1];
// Remove possible enclosing characters (",')
if (!namedParameters.ContainsKey(parameter))
{
parts[2] = remover.Replace(parts[2], "$1");
namedParameters.Add(parameter, parts[2]);
}
parameter = null;
break;
}
}
else
{
unnamedParameters.Add(str);
}
}
// In case a parameter is still waiting
if (parameter != null)
{
if (!namedParameters.ContainsKey(parameter))
namedParameters.Add(parameter, "true");
}
}
/// <summary>
/// Retrieves the parameter with the specified name.
/// </summary>
/// <param name="name">
/// The name of the parameter. The name is case insensitive.
/// </param>
/// <returns>
/// The parameter or <c>null</c> if it can not be found.
/// </returns>
public string this[string name]
{
get { return (namedParameters[name]); }
}
/// <summary>
/// Retrieves an unnamed parameter (that did not start with '-'
/// or '/').
/// </summary>
/// <param name="name">The index of the unnamed parameter.</param>
/// <returns>The unnamed parameter or <c>null</c> if it does not
/// exist.</returns>
/// <remarks>
/// Primarily used to retrieve filenames which extension has been
/// associated to the application.
/// </remarks>
public string this[int index]
{
get
{
return (string)(index < unnamedParameters.Count ?
unnamedParameters[index] :
null);
}
}
}
|
|
|
|
|
Hi,
Thank you.
And again a great addition !
I didn't tested your code yet, but I will, since I use this simple class every time I need it.
I also want to take this occasion to tell everyone again that the source code is free for your use and may be modified to fit all your needs. Build everything you want based on this, commercial or not.
I'm just happy to see people using this code and enjoying it.
I should consider update the article to include all the additions made by all the contributors in the past 2-3 years, and offer an up to date source code to download.
Have a great day,
R. LOPES
Just programmer.
|
|
|
|
|
Please do, I really like your parser! / Anders Bjerin
|
|
|
|
|