Last week I was writing a service that could be run as a console app. I needed to be able to parse a single string into an array like the shell does for command line apps, and the service does for start parameters. I did a little googling, but found only command line arg classes that put command line args into associative array’s for easy indexing, which isn’t what I was looking for.
The class had basically 2 requirements:
- It had to be able to split the string on spaces (simple right?)
- It had to escape spaces that were quoted (as well as quotes that were escaped).
First I tried to iterate through the string a character at a time, looking for spaces keeping the state of whether we were inside a quote or not, whether the current character was an escaped quote, and so on.
That was a major pain, so I stepped back and rethought my algorithm. this is what I ended up with, which was much simpler, and did exactly what I wanted without a lot of fuss.
So here’s my command line parser:
public class CommandLineParser {
CommandLineParser() { }
public static string[] Parse(string str) {
if(str == null || !(str.Length > 0)) return new string[0];
int idx = str.Trim().IndexOf(" ");
if(idx == -1) return new string[] { str };
int count = str.Length;
ArrayList list = new ArrayList();
while(count > 0) {
if(str[0] == '"') {
int temp = str.IndexOf("\"", 1, str.Length - 1);
while(str[temp - 1] == '\\') {
temp = str.IndexOf("\"", temp + 1, str.Length - temp - 1);
}
idx = temp+1;
}
if(str[0] == '\'') {
int temp = str.IndexOf("\'", 1, str.Length - 1);
while(str[temp - 1] == '\\') {
temp = str.IndexOf("\'", temp + 1, str.Length - temp - 1);
}
idx = temp+1;
}
string s = str.Substring(0, idx);
int left = count - idx;
str = str.Substring(idx, left).Trim();
list.Add(s.Trim('"'));
count = str.Length;
idx = str.IndexOf(" ");
if(idx == -1) {
string add = str.Trim('"', ' ');
if(add.Length > 0) {
list.Add(add);
}
break;
}
}
return (string[])list.ToArray(typeof(string));
}
}C#