Frustrated trying to read a path from an argument in C# - c#

I'm passing /file:c:\myfile.doc and I'm getting back "/file:c:\myfile.doc" instead of "C:\myfile.doc", could someone please advise where I am going wrong?
if (entry.ToUpper().IndexOf("FILE") != -1)
{
//override default log location
MyFileLocation = entry.Split(new char[] {'='})[1];
}

You are splitting on "=" instead of ":"
Try
if (entry.ToUpper().IndexOf("FILE:") == 0)
{
//override default log location
MyFileLocation location = entry.Split(new char[] {':'},2)[1];
}

The easiest way to do this is to just take a substring. Since you are reading this from the command line, the "/file:" portion will always be consistent.
entry.Substring(6);
This will return everything after the "/file:".

Not an answer as I think it's been answered well enough already, but as you stated that you're a beginner I thought that I would point out that:
entry.split(new char[]{':'});
can be:
entry.split(':');
This uses:
split(params char[] separator);
This can be deceiving for new C# programmers as the params keyword means that you can actually pass in 1 to many chars, as in:
entry.split(':','.',' ');

You could also just lop off the 'file:' part. It is clearly defined and will be constant so it isn't THAT bad. Not great, but not horrible.

Here is a good example of a command line argument parser.

The code you've posted would require the argument /file=c:\myfile.doc.
Either use that as the parameter or split on the colon (:) instead of equals (=).

Related

How to include ":" in a filepath if Spit(':') is used in C#

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));

Split string to array when some elements are empty

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(',');
}

Numeric check of string not always working as expected

I have an issue, that I cannot resolve by myself. I am reading string values from a file in CSV-format with default encoding (UTF-8 as far as I know). The thing is, that I use the following method to determine if the strings contains digits only:
private static bool IsDigitsOnly(string str)
{
return str.All(Char.IsDigit);
}
In mostly all cases it´s is working fine, but it returns 'false' when the input string is one of the following:
726849004
704152104
779450251
779459121
346751902
779459111
779459115
779428100
726852040
I tried with another approach by changing the method to this:
str.All(c => c >= '0' && c <= '9');
It does the same in the cases above.
When I debug, I can see, that the string values are correct (no trailing or leading whitespaces, chars or anything)
Can someone help me out here?
Thank you in advance,
Thomas.
I tried to solve this and found that there is some skeptic character at the end of each string except for 3rd and 4th. See my test app screen shot:
This is causing the error. If you see the ASCII value of this character then its 31, listed as Unit Separator. Need to look why it's there.
I know that I am not answering your question but trying to give/get some pointer to solve this interesting problem.
Replace
return str.All(char.IsDigit);
with
return str.All(c =>
char.IsDigit(c));
and set a breakpoint on the second line, then debug to inspect which characters are being passed and hopefully find the one which isn't a digit.

How do I do multiple replaces on a string at the same time?

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");

about string removing in C#

Whats is correct to do?
check if exists, then remove?
var input = "foo #main baa";
if(input.Contains("#main")) {
input = input.Replace("#main", "");
}
or just:
input = input.Replace("#main", "");
Well, this seem a simple question,but I really want know.
Thanks in advance.
The Contains check actually just makes your code slower.
Remove it.
The Contains call needs to loop through the string until it finds #main.
The Replace call then needs to do the same exact loop (it can't remember it from the Contains call).
This is a Shlemiel the Painter's algorithm.
Replace can handle strings with zero or more occurrences of the search string, so you don't need the check.
Just do the replacement - if it's not there, nothing should happen.
Just make the call to Replace(). If the substring isn't found nothing happens and you avoid an additional call to Contains().
I would do this:
input = input.Replace("#main", "").Replace(" "," ");
To remove any double spaces.
Just remove it. The only thing to check is if the string is null or not.

Categories

Resources