How to check number of command line arguments + exception handling - c#

How do I go about checking the number of command line arguments entered and then printing an error if its less than 3.
static void Main(string[] args)
{
string file1 = args[0];
string file2 = args[1];
string file3 = args[2];
So if args is < 3, I need to print an erorr message and stop the program without running the next lines of code so that it doesnt give an error. Im just confused. Any help would be grealty appreciated

args is an array -- you have access to all methods related to arrays. So a simple implementation of your question could look like this:
if(args.Length < 3)
throw new ArgumentException("Must have three command line arguments");
It's almost always a good idea to ensure your variables aren't null before trying to access values on them (so the code could look like if(args == null || args.Length < 3), but per some commentary on this answer, these applications will never give you a null value for args, so it should be fine to omit that in this one specific case.

Related

Visual Studio seems to be throwing an error even when changing the code

This is a really weird one and kinda difficult to explain, so stay with me.
I had some pretty basic c# code which runs based on an inputted string, then that string is used put into a logger and then it does some searches etc with it.. details are somewhat unimportant.
So, it keeps throwing a "Index was outside the bounds of the array." error even though for testing purposes I am manually setting the string array right before it uses it.
args[0] = "{XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}";
logger.Info("guid:" + args[0]); //Errors here
_fetchxml = _myApp.getFetchXml("fileguid", args[0], new Guid(), new Guid());
I even just tried putting a line before the logger which just said var a = "a", and it gave the same error on that line, which makes me think its something with the build?
I have cleaned and rebuild the solution but with no luck, hopefully this makes sense.
An array is immutable (i.e. fixed size), so if the Length of the array is zero, it will throw an exception if you try to add a string and assign it to its first position (which is non-existent).
As suggested in the comments, you can simply confirm if indeed args.Length is 0 by adding an if-block:
if(args.Length > 0)
{
args[0] = "{XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}";
logger.Info("guid:" + args[0]); //No more Errors here?
_fetchxml = _myApp.getFetchXml("fileguid", args[0], new Guid(), new Guid());
}
else
{
logger.Info("guid: --no arguments found--");
// or if _fetchxml is crucial:
throw new ArgumentException("No guid given");
}
Or alternatively, if it's only for testing, you could replace the args array entirely:
args = new[] { "{XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}" };
Or better yet, set the argument in the debug section of the project properties under 'Start options' in 'Command line arguments' (assuming these are indeed command line arguments of course).

C# Console - If no arguments were passed take user input

I have a C#-Console-Program that gets called by another program.
The other Program is supposed to pass an argument to my program.
Now, in case there is no argument passed to the program, I want to make it so that the user inputs an argument into the args string array of the Main function.
I know that you can check for the length of the args string to see whether it has something in it or not like this:
if(args.Length == 0){}
But I dont seem to get it so that the user can input a new value into the console into the args array.
I already tried it like this:
if(args.Length == 0)
{
args[0] = Console.ReadLine();
}
But it only throws an error because there is the Index is out of range.
Is there a way to add an index to the args-array or any other way to handle this case? Or do I need to rewrite my code so that it doesnt take the args-Array directly but instead checks if the array has an argument in it and if not create a new array?
A better approach would be:
String username, password;
if (args.Length >= 1) username = args[0];
else username = Console.ReadLine();
if (args.Length >= 2) password = args[1];
else password = Console.ReadLine();
That way you get meaningful variables instead of a dumb array;
if (args.Length == 0)
{
// args[0] does not exist because it is an empty array.
// assign it with an new array of string instead.
args = new string[] { Console.ReadLine(), };
}

C# How to parse through an inconsistently formatted text file, ignoring unneeded information

A little background. I am new to using C# in a professional setting. My experience is mainly in SQL. I have a file that I need to parse through to pull out certain pieces of information. I can figure out how to parse through each line, but have gotten stuck on searching for specific pieces of information. I am not interested in someone finishing this code for me. Instead, I am interested in pointers on where I can go from here.
Here is an example of the code I have written.
class Program
{
private static Dictionary<string, List<string>> _arrayLists = new Dictionary<string, List<string>>();
static void Main(string[] args)
{
string filePath = "c:\\test.txt";
StreamReader reader = new StreamReader(filePath);
string line;
while (null !=(line = reader.ReadLine()))
{
if (line.ToLower().Contains("disconnected"))
{
// needs to continue on search for Disconnected or Subscribed
}
else
{
if (line.ToLower().Contains("subscribed"))
{
// program needs to continue reading file
// looking for and assigning values to
// dvd, cls, jhd, dxv, hft
// records start at Subscribed and end at ;
}
}
}
}
}
A little bit of explanation of the file. I basically need to pull data existing between the word Subscribed and the first ; i come to. Specifically I need to take the values such as dvd = 234 and assign them to their same variables in the code. Not every record will have the same variables.
Here is an example of the text file that I need to parse through.
test information
annoying information
Subscribed more annoying info
more annoying info
dvd = 234,
cls = 453,
jhd = 567,
more annoying info
more annoying info
dxv = 456,
hft = 876;
more annoying info
test information
annoying information
Subscribed more annoying info
more annoying info
dvd = 234,
cls = 455,
more annoying info
more annoying info
dxv = 456,
hft = 876,
jjd = 768;
more annoying info
test information
annoying information
Disconnected more annoying info
more annoying info
more annoying info
Edit
My apologies on the vague question. I have to learn how to ask better questions.
My thought process was to make sure the program associated all the details between subscribed and the ; as one record. I think the part that I am confused on is in reading the lines. In my head I see the loop reading the line Subscribed, and then going into a method and reading the next line and assigning the value, and so on until it hits the ;. Once that was done I am trying to figure out how to tell the program to exit that method, but to continue reading from the line right after the semi-colon. Perhaps I am over thinking this.
I will take the advice I have been give and see what I can come up with to solve this. Thank you.
From you question as it is now it is not clear what specific problem you are struggling with. I'd suggest you edit your question providing specific challenges you'd like to overcome. currently you problem statement is "have gotten stuck on searching for specific pieces of information". This is as unspecific as it can get.
Having said that I'll try to help you.
First, you will never get into an if like that:
line.ToLower().Contains("Disconnected")
Here you convert all the characters to lower case, and then you are trying to find a substring with capital "D" in it. The expression above will (almost) always evaluate to false.
Secondly, in order for your application to do what you want to do it needs to track the current parsing state. I'm going to ignore the "Disconnected" bit now, as you have not shown what significance it has.
I'll be assuming that you are trying to find everything between Subscribed and first semicolon in the file. I'll also make a couple of other assumption regarding to what can constitute a string, which I won't list here. These can be wrong, but this is my best guess given the information you've provided.
You program will start in a state "looking for subscription". You already set up the read loop, which is good. In this loop you read lines of the file, and you find one that contains word Subscription.
Once you found such line your parser need to move to "parsing subscription" state. In this state, when you read lines you look for lines like jjd = 768, perhaps with a semicolon in the end. You can check if the line match a pattern by using Regular Expressions.
Regular Expressions also can divide match to capturing groups, so that you can extract the name (jjd) and the value (768) separately. Presences or absence of the semicolon could be another RegEx group.
Note that RegEx is not the only way to handle this, but this is the first that comes to mind.
You then keeping matching the lines to your regex and extracting names and values until you come across the semicolon, at which point you switch back to "looking for subscription" state.
You use the current state, to decide how to process the next read line.
You continue until the end of the file.
Generally you want to read up on parsing.
Hope this helps.
As with all code solutions to problems there are many possible ways to achieve what you are looking for. Some will work better then others. Below is one way that could help point you in the right direction.
You can check if the string starts with a keyword or value such as "dvd" (see MSDN String.StartsWith).
If it does then you can split the string into an array of parts (see MSDN String.Split).
You can then get the values of each part from the string array using the index of the value you want.
Do what you need to with the value retrieved.
Continue checking each line for your key business rules (ie. The semicolon that will end the section). Maybe you could check the last character of the string. (see String.EndsWith)
When processing text files containing semi-structured data, state variables can simplify the algorithm. In the code below, a boolean state variable isInRecord is used to track when a line is in a record.
using System;
using System.Collections.Generic;
using System.IO;
namespace ConsoleApplication19
{
public class Program
{
private readonly static String _testData = #"
test information
annoying information
Subscribed more annoying info
more annoying info
dvd = 234,
cls = 453,
jhd = 567,
more annoying info
more annoying info
dxv = 456,
hft = 876;
more annoying info
test information
annoying information
Subscribed more annoying info
more annoying info
dvd = 234,
cls = 455,
more annoying info
more annoying info
dxv = 456,
hft = 876,
jjd = 768;
more annoying info
test information
annoying information
Disconnected more annoying info
more annoying info
more annoying info";
public static void Main(String[] args)
{
/* Create a temporary file containing the test data. */
var testFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Path.GetRandomFileName());
File.WriteAllText(testFile, _testData);
try
{
var p = new Program();
var records = p.GetRecords(testFile);
foreach (var kvp in records)
{
Console.WriteLine("Record #" + kvp.Key);
foreach (var entry in kvp.Value)
{
Console.WriteLine(" " + entry);
}
}
}
finally
{
File.Delete(testFile);
}
}
private Dictionary<String, List<String>> GetRecords(String path)
{
var results = new Dictionary<String, List<String>>();
var recordNumber = 0;
var isInRecord = false;
using (var reader = new StreamReader(path))
{
String line;
while ((line = reader.ReadLine()) != null)
{
line = line.Trim();
if (line.StartsWith("Disconnected"))
{
// needs to continue on search for Disconnected or Subscribed
isInRecord = false;
}
else if (line.StartsWith("Subscribed"))
{
// program needs to continue reading file
// looking for and assigning values to
// dvd, cls, jhd, dxv, hft
// records start at Subscribed and end at ;
isInRecord = true;
recordNumber++;
}
else if (isInRecord)
{
// Check if the line has a general format of "something = something".
var parts = line.Split("=".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
if (parts.Length != 2)
continue;
// Update the relevant dictionary key, or add a new key.
List<String> entries;
if (results.TryGetValue(recordNumber.ToString(), out entries))
entries.Add(line);
else
results.Add(recordNumber.ToString(), new List<String>() { line });
// Determine if the isInRecord state variable should be toggled.
var lastCharacter = line[line.Length - 1];
if (lastCharacter == ';')
isInRecord = false;
}
}
}
return results;
}
}
}

C# Check Array for values, go to position of existing value

I have a (hopefully) simple C# question.
I am parsing arguments in a program where a file will be read from command line, I've allowed for both short and long arguments as input (so for my scenario /f and file are both valid)
The value after either of the above arguments should be the file name to be read.
What I want to do is find this file name in the array based off whichever argument is chosen and copy it in to a string while not leaving any loopholes.
I have functioning code, but I'm not really sure it's "efficient" (and secure).
Code (comments and writes removed):
if ( args.Contains("/f") || args.Contains("file"))
{
int pos = Array.IndexOf(args, "/f");
if (pos == -1)
pos = Array.IndexOf(args, "file");
if (pos > -1)
pos++;
inputFile = (args[pos]);
if (File.Exists(inputFile) == false)
{
Environment.Exit(0);
}
}
Is there a more efficient way to do this, perhaps using some nifty logic in the initial if statement to check which parameter is valid and then do a single check on that parameter?
Using 4 ifs and 2 Array.IndexOf's seems horrible just to support 2 different ways to allow someone to say they want to input a file...
Thanks! And I'm sorry if this seems trivial or is not what SO is meant for. I just don't have any real way to get feedback on my coding practises unfortunately.
Your solution won't scale well. Imagine you have two different arguments with a short and long form. How many conditionals and index checks would that be?
You'd be better off using an existing tool (e.g. Command Line Parser Library) for argument parsing.
One problem I see with the code you provided is that, it will fail if the /f or file is the last argument.
If you don't want to write or use complete argument parsing code, the following code will work slightly better.
var fileArguments = new string[] { "/f", "file" };
int fileArgIndex = Array.FindIndex(args,
arg => fileArguments.Contains(arg.ToLowerInvariant()));
if (fileArgIndex != -1 && fileArgIndex < args.Length - 1)
{
inputFile = args[fileArgIndex + 1];
if (!File.Exists(inputFile))
{
Environment.Exit(0);
}
}
You could write a simple argument parser for your specific need and still have support for "new" scenarios. For example, in your entry method have
// The main entry point for the application.
[STAThread]
static void Main(string[] args)
{
// Parse input args
var parser = new InputArgumentsParser();
parser.Parse(args);
....
}
Where your InputArgumentsParser could be something similar to
public class InputArgumentsParser
{
private const char ArgSeparator = ':';
private Dictionary<string[],Action<string>> ArgAction =
new Dictionary<string[],Action<string>>();
public InputArgumentsParser()
{
// Supported actions to take, based on args
ArgAction.Add(new[] { "/f", "/file" }, (param) =>
Console.WriteLine(#"Received file argument '{0}'", param));
}
/// Parse collection, expected format is "<key>:<value>"
public void Parse(ICollection<string> args)
{
if (args == null || !args.Any())
return;
// Iterate over arguments, extract key/value pairs
foreach (string arg in args)
{
string[] parts = arg.Split(ArgSeparator);
if (parts.Length != 2)
continue;
// Find related action and execute if found
var action = ArgAction.Keys.Where(key =>
key.Contains(parts[0].ToLowerInvariant()))
.Select(key => ArgAction[key]).SingleOrDefault();
if (action != null)
action.Invoke(parts[1]);
else
Console.WriteLine(#"No action for argument '{0}'", arg);
}
}
}
In this case /f:myfile.txt or /file:myfile.txt would spit out to console
Received file argument 'myfile.txt'

starting programs with values

I'm sorry, I can't really google this because I'm not sure how to properly say this in a few words.
But basically I'd like to have something like, when you open your program via dos or via a shortcut looking like this:
"c:\program.exe" value1 value2
that my application would be able to use these values. But also when I don't enter the values, that my application still starts fine.
I hope that made any sense what I'm trying to say here
Any help is appereciated
Those are the args that get passed to your main function:
public static void main (string[] args)
{
// Check to see if at least two args were passed in.
if(args.Length >= 2)
{
Console.WriteLine(args[0]); // value1
Console.WriteLine(args[1]); // value2
}
}
Keep in mind, though, that there's no way to guarantee the order of the args passed in or that they are the values you expect. You should use named arguments and then parse and validate them at the beginning of your application. Your command might look something like:
C:\program.exe /V1 value1 /V2 value2
As for a good list of parsers, I would check out:
.net - Best way to parse command line arguments in C#
Have a peek at the Microsoft tutorial for command line parameters
If a parameter isn't provided then just use some defauls.
public static void Main(string[] args)
{
// The Length property is used to obtain the length of the array.
// Notice that Length is a read-only property:
Console.WriteLine("Number of command line parameters = {0}",
args.Length);
for(int i = 0; i < args.Length; i++)
{
Console.WriteLine("Arg[{0}] = [{1}]", i, args[i]);
}
if(args.length < 2)
{
x = 1;
} else {
{
x = Arg[2];
}
}
When you create a executable you have a Main function that has Main(string[] args), here you can read the params which you used to call the program.
If you want default values, you can make a class variable with a defined value (or use the application properties) and if the program program is called with parameters overwrite them.
Hope it helps you :)
Execute your Program.exe from commandline this way
C:\Program Test1 Test2
To get knowledge on how to this in C# please use the link MSDN

Categories

Resources