|
Try this pls:
Regex isParam = new Regex(@"^^-{1,2}|^/",<br />
RegexOptions.IgnoreCase | RegexOptions.Compiled);<br />
<br />
Regex Spliter = new Regex(@"^-{1,2}|^/|=|:",<br />
RegexOptions.IgnoreCase | RegexOptions.Compiled);<br />
<br />
<br />
[...].br />
<br />
<br />
bool isparam = isParam.IsMatch(Txt);<br />
if (isparam)<br />
Parts = Spliter.Split(Txt, 3);<br />
else<br />
Parts = new string[] { Txt };
This will splitt only if - -- oder / detected.
|
|
|
|
|
I really like your command-line parser - alas, it lets me down in one case:
if I have an argument that expects a file name including a path, such as "C:\temp\*.xml", I cannot seem to get those properly.
When I call my app with an argument "/filespec c:\temp\*.xml", or even when I put the file name / path in double quotes ("), I always get "True" for the "filespec" argument.
It appears that the parser will choke on the ":" in the path and think this is another argument to split up; it should *NOT* do this, especially if the file path/file spec is enclosed in double quotes!
Any chance you could fix this? Also- whenever I download your code, I always seem to get the v1.0 version (Oct 2002) - not the bug fixed version of November. Did you upload the most current version at all?
Thanks again for a great little piece of software!
=============================
Marc Scheuner, Berne, Switzerland
mscheuner - at - gmail.com
May The Source Be With You!
|
|
|
|
|
Hi Marc,
Good points.
For your parsing problem, you found a problem. This could be fixed by tweaking the regex in case one use " or ' characters. The splitter regex has to be enhanced for such cases. But I may be not enough. I didn't have the time right now to look at the problem, because of a heavy schedule. If you are really in need you can give it a try.
That leads us to the second good point you made. I didn't updated the source in the past 2 years but I should do it some day because many people in the comments threads came up with good ideas and fixes. Again I am always postponing this task of puting every contributed bit together to upload an updated and rock solid version. However, I think I can manage at last a moment before mid-september to update the code, the article and give credits to every people who pointed out problems, fixes and enhancements. Please note I use an enhanced version of this class myself (but this doesn't include the fix you need).
I'm sorry not to come right now with a good answer. It's at the very least on my todo list.
Keep coding,
R. LOPES
Just programmer.
|
|
|
|
|
Simply removing the "|:" from the regex seems to fix the problem. That is never a legitimate delimiter that I have heard of anyway.
This also solves the problem above which has ":" in the parameter.
Dave
|
|
|
|
|
the "|:" delimiter is most certainly a valid delimiter, but only in one very specific instance is it ever used that I can think of. That instance being the processing of Optical Character Recognition fields when determining the begining of an ABA Routing numbers and Account numbers fields on checks. The "|:" is used to delimit the begining of a new field either the ABA Routing number or the account number example |:123456789|:1112223334444"' 0001 as you can see from this example the aba routing number is preceded and anteceded by the "|:" character and an OCR system is instructed that data to follow represents a contiguous field from that special delimiter. Hopefuly your needs will not include OCR Check Processing but if it does you may need to put that "|:" delimiter back in
HTH
|
|
|
|
|
that part of the regex does is not to allow "|:" as a delimiter, but is meant allow ":" as a delimiter between the parameter and the value.
in your example, leaving that bit in would cause the same problem with ABA routing numbers as it would for file paths, etc. But, only if you were passing in the Routing number as a value for a parameter.
|
|
|
|
|
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;
}
|
|
|
|
|