I have an application where I have to provide number of parameters in the format Name:Value
I provide the list of parameters through the Command line arguments value under "Debug" section of the project
So, it look something like that: "MyJobName" "0" "#FullFilePath:C:\MyFile.txt" "#FileType:MyFileType" "#FileDate:20200318" "#FileID:MyAppID"
One parameter is FilePath:C:\FileDir\MyFileTxt.txt
So, when the following logic is applied:
for (int i = 2; i <= args.GetLength(0) - 1; i++)
{
L.Add(args[i].Split(':')[0], args[i].Split(':')[1]);
}
My Parameter looks like that: FilePath:C, ignoring the rest of the path.
The final parameter list that I need to pass to the Stored Procedure should have "Name:Value" format
How can I fix that?
Split lets you pass the maximum array length.
See Split Split(Char[], Int32)
Splits a string into a maximum number of substrings based on the characters
in an array.
You also specify the maximum number of substrings to return.
Sample:
var keyValue = args[i].Split(new char[]{ ':' }, 2);
L.Add(keyValue[0], keyValue[1]);
This way only the first : is taken. The other : that come after it are ignored and will be part of the second item in the array.
But I honestly advise you to use a proper parameter parser, because your approach is very easy to break and very very fragile.
https://github.com/commandlineparser/commandline
Have a look at dragonfruit and Systel.CommandLine
Instead of writing your arguments parser yourself.
It’s a way to have type safe arguments in your main method.
Scott Hanselman has a great blog post about it.
The great part being your XML comments are used to generate a help message.
The moment you use Split, you exclude the delimiter from being a valid character without having to add the extra overloads to it. So if you absolutely must use a colon as your delimiter, you can either use the Split with overload as suggested above, or write extra code to address it;below is how I would parse it.
Of course, a much easier alternative (if possible) would be to change your delimiter to something you know it would never use, something like a pipe or a tilde or a backtick (|, ~, ). Then Split would work cleanly.
"#FullFilePath:C:\MyFile.txt" "#FileType:MyFileType" "#FileDate:20200318" "#FileID:MyAppID"
If your parameters always have the format #ParameterName:ParameterValue, your best bet is to parse the command line args like so:
var argumentsList = new Dictionary<string,object>();
for (int i=2; i < args.Length; i++)
{
int colonIndex = args[i].IndexOf(":");
string parameterName = args[i].Substring(0, colonIndex - 1);
string parameterValue = args[i].Substring(colonIndex + 1);
argumentsList[parameterName] = parameterValue;
}
The scope of your question centers around how to get around the colon, so however you choose to store the parameter values is up to you, I just used the dictionary as an example to help wrap up the code.
This will skip FilePath and give you C:\FileDir\MyFileTxt.txt
string.Join(":", args[i].Split(':').Skip(1));
I need to process a large amount of csv data in real time as it is spat out by a TCP port. Here is an example as displayed by Putty:
MSG,3,1920,742,4009C5,14205994,2017/01/29,20:14:27.065,2017/01/29,20:14:27.972,,8000,,,51.26582,-0.33783,,,0,0,0,0
MSG,4,1920,742,4009C5,14205994,2017/01/29,20:14:27.065,2017/01/29,20:14:27.972,,,212.9,242.0,,,0,,,,,
MSG,1,1920,742,4009C5,14205994,2017/01/29,20:14:27.065,2017/01/29,20:14:27.972,BAW469,,,,,,,,,,,
MSG,3,1920,742,4009C5,14205994,2017/01/29,20:14:27.284,2017/01/29,20:14:27.972,,8000,,,51.26559,-0.33835,,,0,0,0,0
MSG,4,1920,742,4009C5,14205994,2017/01/29,20:14:27.284,2017/01/29,20:14:27.972,,,212.9,242.0,,,0,,,,,
I need to put each line of data in string (line) into an array (linedata[]) so that I can read and process certain elements, but linedata = line.Split(','); seems to ignore the many empty elements, with the result that linedata[20], for example, may or may not exist, and if it doesn't I get an error if I try to read it. Even if element 20 in the line contains a value it won't necessarily be the 20th element in the array. And that's no good.
I can work out how to parse line character by character into linedata[], inserting an empty string where appropriate, but surely there must be a better way ? Have I missed something obvious ?
Many Thanks. Perhaps I'd better add that I'm quite new to C#, my past experience is all with Delphi 7. I really miss stringlists.
Edited: sorry, this is now resolved with the help of MSDN's documentation. This code works: lineData = line.Split(separators, StringSplitOptions.None); after setting "string[] separators = { "," };". My big mistake was to follow examples found on tutorial sites which didn't give any clues that the .split method had any options.
https://msdn.microsoft.com/en-us/library/system.stringsplitoptions(v=vs.110).aspx
That link has an example section, look at example 1b specifically. There is an extra parameter to Split called StringSplitOptions which does this.
For Example:
string[] linedata = line.Split(charSeparators, StringSplitOptions.None);
foreach (string line in linedata)
{
Console.Write("<{0}>", line);
}
Console.Write("\n\n");
The way to find this sort of information is to start with the Reference Documentation for the function, and hope it has an option or a link to a similar function.
If you want to also start validating types, handling variants in the format etc... you could move up to a CSV library. If you do not need that functionality, this is the easiest way and efficient for small files.
Some of the overloads for String.Split() take a StringSplitOptions argument, and if you use the RemoveEmptyEntries option, it will...remove the empty entries. So you can specify the None option:
linedata = line.Split(new [] { ',' }, StringSplitOptions.None);
Or better yet, use the overload that doesn't take a StringSplitOptions, which treats it as None by default:
linedata = line.Split(',');
The code in your question indicates that you are doing this, but your description of the problem suggests that you are not.
However, you're probably better off using an actual CSV parser, which would handle things like unescaping and so on.
The StringReader class provides methods for reading lines, characters, or blocks of characters from a string. Hope this could be the clue
string str = #"MSG,3,1920,742,4009C5,14205994,2017/01/29,20:14:27.065,2017/01/29,20:14:27.972,,8000,,,51.26582,-0.33783,,,0,0,0,0
MSG,4,1920,742,4009C5,14205994,2017/01/29,20:14:27.065,2017/01/29,20:14:27.972,,,212.9,242.0,,,0,,,,,
MSG,1,1920,742,4009C5,14205994,2017/01/29,20:14:27.065,2017/01/29,20:14:27.972,BAW469,,,,,,,,,,,
MSG,3,1920,742,4009C5,14205994,2017/01/29,20:14:27.284,2017/01/29,20:14:27.972,,8000,,,51.26559,-0.33835,,,0,0,0,0
MSG,4,1920,742,4009C5,14205994,2017/01/29,20:14:27.284,2017/01/29,20:14:27.972,,,212.9,242.0,,,0,,,,,";
using (StringReader reader = new StringReader(str))
do
{
string[] linedata = reader.ReadLine().Split(',');
} while (reader.Read() != -1);
While you should look into the various ways the String class can help you here, sometimes the quick and dirty "MAKE it fit" option is called for. In this case, that'd be to roll through the strings in advance and ensure you have at least one character between the commas.
public static string FixIt(string s)
{
return s.Replace(",,", ", ,");
}
You should be able to:
var lineData = FixIt(line).Split(',');
Edit: In response to the question below, I'm not sure what you meant, but if you mean doing it without creating a helper method, you can do so easily. The code will be harder to read and troubleshoot if you do it in one line though. My personal rule is, if you have to do it a LOT, it should probably be a method. If you only had to do it once, this is particularly clean. I'd actually do it this way and just wrap it in a method that does all the work for you.
var lineData = line.Replace(",,", ", ,").Split(',');
As a method, it'd be:
public static string[] GiveMeAnArray(string s)
{
return s.Replace(",,", ", ,").Split(',');
}
string rawConnString = Properties.Settings.Default.ConnectionString;
I want to do this with "rawConnString":
rawConnString.Replace("<<DATA_SOURCE>>", server);
rawConnString.Replace("<<INITIAL_CATALOG>>", "tempdb");
Then set the final string to variable finalConnString.
How would I go about doing this?
This is ASP .NET 4.0/C# btw.
string finalString = Properties.Settings.Default.ConnectionString.Replace("<<DATA_SOURCE>>", server).Replace("<<INITIAL_CATALOG>>", "tempdb");
will do it all in one line of code. But it's uglier IMO because you'll have to scroll. The code in your question seems a LOT cleaner and more readable to me.
And doing it in one line of code won't help your performance at all. It should all compile down to the same MSIL either way. I'd leave it as you had it.
Not sure if this is what you're after, but you can chain them:
var finalConnString = rawConnString.Replace("<<DATA_SOURCE>>", server)
.Replace("<<INITIAL_CATALOG>>", "tempdb");
If you're looking to do it with a single method call, I don't think there's anything native to .NET. You can always create an extension method though. Here's a performance-conscious ReplaceMany implementation (signature .ReplaceMany(string[] oldValues, string[] newValues)).
This is frankly trivial; you have 90% of the code you need:
string rawConnString = Properties.Settings.Default.ConnectionString;
string finalConnString = rawConnString
.Replace("<<DATA_SOURCE>>", server)
.Replace("<<INITIAL_CATALOG>>", "tempdb");
To avoid using two back-to-back calls of Replace, you can use regular expressions. However, this is far less readable than the original:
string connString = Regex.Replace(
rawConnString
, "(<<DATA_SOURCE>>)|(<<INITIAL_CATALOG>>)"
, m => m.Groups[1].Success ? server : "tempdb"
);
Link to ideone.
you mean something like this
string tempString=rawConnString.Replace("<<DATA_SOURCE>>", server);
sting finalstring=tempString.Replace("<<INITIAL_CATALOG>>", "tempdb");
First, String.replace does not change the original string: it creates a new string. So you have to assign the return value to something. So the logically simplest thing to do is:
finalstring=rawConnString.Replace("<<DATA_SOURCE>>", server);
finalstring=finalstring.Replace("<<INITIAL_CATALOG>>", "tempdb");
Note that for the second replace, you want to start with the results of the first replace, not the original string.
As String.replace returns a string, and String.replace takes a string, you might find it easier to run them together:
finalstring=rawConnString.Replace("<<DATA_SOURCE>>", server).Replace("<<INITIAL_CATALOG>>", "tempdb");