Parsing Command Line Arguments
Provide access to command line arguments via a SortedList
Introduction
At one time or another, most of us have to create a command console application that reads keys and key/value pairs from the command line. Fortunately, there's a Split
function to help with parsing the text command line value. Unfortunately, it doesn't support text qualifiers or assignment operators.
In a previous article, I described how to make a Split Function that Supports Text Qualifiers. In this article, we will add additional support for command assignment operators. Additionally, we will provide access to the values via a SortedList
to increase our ability to control the command line parameters.
Background
A SortedList
is used to make it obvious that our key/value pairs will not be stored in the order in which they are listed in the command line. None of the collections based on the DictionaryBase
object can guarantee that the values will be returned in the order in which they are added. Dictionaries nearly always reorder the data they contain. Using a SortedList
makes this point obvious to beginners. This approach insulates beginners allowing them to make safe assumptions about how the collection will behave.
The SortedList Abstract Class
The following diagram shows the SortedList
base class and the new TextParser
abstract class. The properties and methods with an asterisk next to them are overridden or shadowed by the abstract class. For the sake of this article, we will only focus on the critical routines. If you want to see the completed class, you will need to download the samples.

Sorting the Expression Property
One feature this class will provide is that it will sort the actual text expression. We begin by creating the Sort
routine which will sort the Expression
property. Since all of our values are already sorted in the list this will be a fairly simple task.
C#
private void Sort()
{
string _KeyValue;
m_Expression = string.Empty;
if (this.Count>0)
{
foreach (string _KeyName in GetKeyList())
{
_KeyValue = System.Convert.ToString
(this.GetByIndex(base.IndexOfKey(_KeyName)));
if ((_KeyValue==null) | (_KeyValue.Length==0))
{
m_Expression += this.Delimiter + _KeyName;
}
else
{
m_Expression += this.Delimiter + _KeyName + this.Operator + _KeyValue;
}
}
}
m_Expression = m_Expression.Trim();
}
VB.NET
Private Sub Sort()
Dim _KeyValue As String
m_Expression = String.Empty
For Each _KeyName As String In GetKeyList()
_KeyValue = Item(_KeyName)
If _KeyValue Is Nothing _
OrElse _KeyValue.Length = 0 Then
m_Expression &= Delimiter & _KeyName
Else
m_Expression &= Delimiter & _KeyName & Operator & _KeyValue
End If
Next
m_Expression = m_Expression.Trim
End Sub
Splitting and Parsing the Expression Property
We will create a Parse
routine responsible for transferring the key/value pairs into the base class. It will use the Split
function and extract the key/value pairs from the Expression
property. For additional details about the Split
function, refer to the article, "Split Function that Supports Text Qualifiers".
C#
private void Parse()
{
string[] _Commands = Split(Expression, Delimiter, Qualifier, IgnoreCase);
string _Key;
string _Value;
int _AOLoc;
base.Clear();
if (_Commands!=null)
{
foreach (string _Command in _Commands)
{
if (IgnoreCase)
{
_AOLoc = _Command.ToUpper().IndexOf(Operator.ToUpper());
}
else
{
_AOLoc = _Command.IndexOf(Operator);
}
if (_AOLoc > 0)
{
_Key = _Command.Substring(0, _AOLoc).Trim();
_Value = _Command.Substring(_AOLoc + 1).Trim();
}
else
{
_Key = _Command.Trim();
_Value = null;
}
if (!(base.ContainsKey(_Key)))
base.Add(_Key, _Value);
}
}
}
VB.NET
Private Sub Parse()
Dim _Commands() As String = Split(Expression, Delimiter, Qualifier, IgnoreCase)
Dim _Key As String
Dim _Value As String
Dim _AOLoc As Integer
MyBase.Clear()
If Not _Commands Is Nothing Then
For Each _Command As String In _Commands
If IgnoreCase Then
_AOLoc = _Command.ToUpper.IndexOf(Operator.ToUpper)
Else
_AOLoc = _Command.IndexOf(Operator)
End If
If _AOLoc > 0 Then
_Key = _Command.Substring(0, _AOLoc).Trim
_Value = _Command.Substring(_AOLoc + 1).Trim
Else
_Key = _Command.Trim
_Value = Nothing
End If
'Duplicate keys are ignored suppressing error messages.
If Not MyBase.ContainsKey(_Key) Then
MyBase.Add(_Key, _Value)
End If
Next
End If
End Sub
Finally... The Expression Property Itself
Whenever a value is assigned to the Expression
property, we will need to parse the value and sort the expression.
C#
public string Expression
{
get
{
return m_Expression;
}
set
{
if (value==null) value = string.Empty;
m_Expression = value.Trim();
Parse();
Sort();
}
}
VB.NET
Public Property Expression() As String
Get
Return m_Expression
End Get
Set(ByVal Value As String)
If Value Is Nothing Then Value = String.Empty
m_Expression = Value.Trim
Parse()
Sort()
End Set
End Property
Adding New Key/Value Pairs
Whenever we need to add a new value to the list, we can simply append the key/value pair to the Expression
property and it will do the rest of the work for us.
It is important to verify that the value contains a delimiter so we know how to build the expression. We will rely on the consumer to provide a properly formatted string
value that will support text qualifiers.
C#
public void Add(string key, string value)
{
if (this.ContainsKey(key))
{
throw new ArgumentException("The specified key already exists");
}
else if ((value==null) | (value.Trim().Length==0))
{
Expression += Delimiter + key;
}
else if (((IgnoreCase) & (value.ToUpper().IndexOf(Delimiter.ToUpper()) > 0))
| ((IgnoreCase) & (value.ToUpper().IndexOf(Qualifier.ToUpper()) >= 0))
| (!(IgnoreCase) & (value.IndexOf(Delimiter) > 0))
| (!(IgnoreCase) & (value.IndexOf(Qualifier) >= 0)))
{
Expression += Delimiter + key + Operator + Qualifier + value + Qualifier;
}
else
{
Expression += Delimiter + key + Operator + value;
}
}
VB.NET
Public Shadows Sub Add(ByVal key As String, ByVal value As String)
If Me.ContainsKey(key) Then
Throw New ArgumentException("The specified key already exists")
ElseIf value Is Nothing _
OrElse value.Trim.Length = 0 Then
Expression &= Delimiter & key
ElseIf IgnoreCase AndAlso value.ToUpper.IndexOf(Delimiter.ToUpper) > 0 _
OrElse IgnoreCase AndAlso value.ToUpper.IndexOf(Qualifier.ToUpper) >= 0 _
OrElse Not IgnoreCase AndAlso value.IndexOf(Delimiter) > 0 _
OrElse Not IgnoreCase AndAlso value.IndexOf(Qualifier) >= 0 Then
Expression &= Delimiter & key & Operator & Qualifier & value & Qualifier
Else
Expression &= Delimiter & key & Operator & value
End If
End Sub
Removing Values
Removing key/value pairs has become a straight forward task. All we need to do is remove the key/value from the base class and resort the expression. To Clear
the class is even easier. We clear the values from the base class and reset the Expression
property to an empty string
.
C#
public void Remove(string key)
{
if (this.ContainsKey(key))
{
base.Remove(key);
Sort();
}
}
public override void RemoveAt(int index)
{
if (this.Count>index)
{
base.RemoveAt(index);
Sort();
}
}
public override void Clear()
{
base.Clear();
Expression = string.Empty;
}
VB.NET
Public Shadows Sub Remove(ByVal key As String)
If Me.ContainsKey(key) Then
MyBase.Remove(key)
Sort()
End If
End Sub
Public Overrides Sub RemoveAt(ByVal index As Integer)
If Me.Count > index Then
MyBase.RemoveAt(index)
Sort()
End If
End Sub
Public Overrides Sub Clear()
MyBase.Clear()
Expression = String.Empty
End Sub