pass parameter not having to worry about orders exe c# - c#

Trying to pass a parameter without having to think about the order of parameters.
The first line is what I want to be able to do and below is what I am able to do.
What I want where both should give the same result
test.exe -word1 hello -word2 bye
test.exe -word2 bye -word1 hello
What I have
test.exe hello bye
static void Main(string[] args)
{
console.line(args[0])
console.line(args[1])
}

You could use the string input like so instead:
test.exe -word1=hello -word2=bye
test.exe -word2=bye -word1=hello
and then you could do something like this:
static void Main(string[] args)
{
// Single if the param is needed, else SingleOrDefault (to get null if the param. was not found
var word1 = args.Single(c => c.startsWith("-"+nameof(word1))
.Split(new char[] {'='})
.Last();
var word2 = args.Single(c => c.startsWith("-"+nameof(word2))
.Split(new char[] {'='})
.Last();
}
this is pseudo code, i did not run it - just to give you an example.
I wouldn't even bother tho, and just slap in: https://github.com/Tyrrrz/CliFx

You can use CommandLineApplication from https://www.nuget.org/packages/Microsoft.Extensions.CommandLineUtils
then you can configure your command line parameters and other available options and use them explicitly later on.
For example:
var app = new CommandLineApplication()
{
Description = "CLI tool for copying messages from one queue to another",
Name = "copy-queue",
};
var sourceOption = _app.Option("--source|-s", $"The source queue name", CommandOptionType.SingleValue);
var destinationOption = _app.Option("--destination|-d", $"The destination queue name", CommandOptionType.SingleValue);
var profileOption = _app.Option("--profile|-p", $"AWS CLI profile (default: default)", CommandOptionType.SingleValue);
var regionOption = _app.Option("--region|-r", $"AWS region (default: us-east-1)", CommandOptionType.SingleValue);
var batchSizeOption = _app.Option("--batch-size|-b", $"Batch size (default: 10)", CommandOptionType.SingleValue);
_app.HelpOption("--help|-h|-?");
var name = Assembly.GetEntryAssembly().GetName();
_app.VersionOption("--version", name.Name + " " + name.Version);
_app.Invoke = () => ExecuteAsync().Result;
try
{
return _app.Execute(args);
}
catch (CommandParsingException cpe)
{
Console.WriteLine(cpe.Message);
return 1;
}

I assume in your example word1 will have the value "hello", but could have any other value and you are interested into that value. With Array.IndexOf you can look for the index and then fetch the string with the index one higher:
int index1 = Array.IndexOf(args, "-word1");
string word1 = args[index1+1];
int index2 = Array.IndexOf(args, "-word2");
string word2 = args[index2+1];
You might consider to check also for the array boundaries (index1 < args.Length-1) and that index1 and index2 are not -1 and display an appropriate error message if necessary.

Related

Extracting data from plain text string

I am trying to process a report from a system which gives me the following code
000=[GEN] OK {Q=1 M=1 B=002 I=3e5e65656-e5dd-45678-b785-a05656569e}
I need to extract the values between the curly brackets {} and save them in to variables. I assume I will need to do this using regex or similar? I've really no idea where to start!! I'm using c# asp.net 4.
I need the following variables
param1 = 000
param2 = GEN
param3 = OK
param4 = 1 //Q
param5 = 1 //M
param6 = 002 //B
param7 = 3e5e65656-e5dd-45678-b785-a05656569e //I
I will name the params based on what they actually mean. Can anyone please help me here? I have tried to split based on spaces, but I get the other garbage with it!
Thanks for any pointers/help!
If the format is pretty constant, you can use .NET string processing methods to pull out the values, something along the lines of
string line =
"000=[GEN] OK {Q=1 M=1 B=002 I=3e5e65656-e5dd-45678-b785-a05656569e}";
int start = line.IndexOf('{');
int end = line.IndexOf('}');
string variablePart = line.Substring(start + 1, end - start);
string[] variables = variablePart.Split(' ');
foreach (string variable in variables)
{
string[] parts = variable.Split('=');
// parts[0] holds the variable name, parts[1] holds the value
}
Wrote this off the top of my head, so there may be an off-by-one error somewhere. Also, it would be advisable to add error checking e.g. to make sure the input string has both a { and a }.
I would suggest a regular expression for this type of work.
var objRegex = new System.Text.RegularExpressions.Regex(#"^(\d+)=\[([A-Z]+)\] ([A-Z]+) \{Q=(\d+) M=(\d+) B=(\d+) I=([a-z0-9\-]+)\}$");
var objMatch = objRegex.Match("000=[GEN] OK {Q=1 M=1 B=002 I=3e5e65656-e5dd-45678-b785-a05656569e}");
if (objMatch.Success)
{
Console.WriteLine(objMatch.Groups[1].ToString());
Console.WriteLine(objMatch.Groups[2].ToString());
Console.WriteLine(objMatch.Groups[3].ToString());
Console.WriteLine(objMatch.Groups[4].ToString());
Console.WriteLine(objMatch.Groups[5].ToString());
Console.WriteLine(objMatch.Groups[6].ToString());
Console.WriteLine(objMatch.Groups[7].ToString());
}
I've just tested this out and it works well for me.
Use a regular expression.
Quick and dirty attempt:
(?<ID1>[0-9]*)=\[(?<GEN>[a-zA-Z]*)\] OK {Q=(?<Q>[0-9]*) M=(?<M>[0-9]*) B=(?<B>[0-9]*) I=(?<I>[a-zA-Z0-9\-]*)}
This will generate named groups called ID1, GEN, Q, M, B and I.
Check out the MSDN docs for details on using Regular Expressions in C#.
You can use Regex Hero for quick C# regex testing.
You can use String.Split
string[] parts = s.Split(new string[] {"=[", "] ", " {Q=", " M=", " B=", " I=", "}"},
StringSplitOptions.None);
This solution breaks up your report code into segments and stores the desired values into an array.
The regular expression matches one report code segment at a time and stores the appropriate values in the "Parsed Report Code Array".
As your example implied, the first two code segments are treated differently than the ones after that. I made the assumption that it is always the first two segments that are processed differently.
private static string[] ParseReportCode(string reportCode) {
const int FIRST_VALUE_ONLY_SEGMENT = 3;
const int GRP_SEGMENT_NAME = 1;
const int GRP_SEGMENT_VALUE = 2;
Regex reportCodeSegmentPattern = new Regex(#"\s*([^\}\{=\s]+)(?:=\[?([^\s\]\}]+)\]?)?");
Match matchReportCodeSegment = reportCodeSegmentPattern.Match(reportCode);
List<string> parsedCodeSegmentElements = new List<string>();
int segmentCount = 0;
while (matchReportCodeSegment.Success) {
if (++segmentCount < FIRST_VALUE_ONLY_SEGMENT) {
string segmentName = matchReportCodeSegment.Groups[GRP_SEGMENT_NAME].Value;
parsedCodeSegmentElements.Add(segmentName);
}
string segmentValue = matchReportCodeSegment.Groups[GRP_SEGMENT_VALUE].Value;
if (segmentValue.Length > 0) parsedCodeSegmentElements.Add(segmentValue);
matchReportCodeSegment = matchReportCodeSegment.NextMatch();
}
return parsedCodeSegmentElements.ToArray();
}

How to remove a duplicate set of characters in a string

For example a string contains the following (the string is variable):
http://www.google.comhttp://www.google.com
What would be the most efficient way of removing the duplicate url here - e.g. output would be:
http://www.google.com
I assume that input contains only urls.
string input = "http://www.google.comhttp://www.google.com";
// this will get you distinct URLs but without "http://" at the beginning
IEnumerable<string> distinctAddresses = input
.Split(new[] {"http://"}, StringSplitOptions.RemoveEmptyEntries)
.Distinct();
StringBuilder output = new StringBuilder();
foreach (string distinctAddress in distinctAddresses)
{
// when building the output, insert "http://" before each address so
// that it resembles the original
output.Append("http://");
output.Append(distinctAddress);
}
Console.WriteLine(output);
Efficiency has various definitions: code size, total execution time, CPU usage, space usage, time to write the code, etc. If you want to be "efficient", you should know which one of these you're trying for.
I'd do something like this:
string url = "http://www.google.comhttp://www.google.com";
if (url.Length % 2 == 0)
{
string secondHalf = url.Substring(url.Length / 2);
if (url.StartsWith(secondHalf))
{
url = secondHalf;
}
}
Depending on the kinds of duplicates you need to remove, this may or may not work for you.
collect strings into list and use distinct, if your string has http address you can apply regex http:.+?(?=((http:)|($)) with RegexOptions.SingleLine
var distinctList = list.Distinct(StringComparer.CurrentCultureIgnoreCase).ToList();
Given you don't know the length of the string, you don't know if something is double and you don't know what is double:
string yourprimarystring = "http://www.google.comhttp://www.google.com";
int firstCharacter;
string temp;
for(int i = 0; i <= yourprimarystring.length; i++)
{
for(int j = 0; j <= yourprimarystring.length; j++)
{
string search = yourprimarystring.substring(i,j);
firstCharacter = yourprimaryString.IndexOf(search);
if(firstCharacter != -1)
{
temp = yourprimarystring.substring(0,firstCharacter) + yourprimarystring.substring(firstCharacter + j - i,yourprimarystring.length)
yourprimarystring = temp;
}
}
This itterates through all your elements, takes all out from first to last letter and searches for them like this:
ABCDA - searches for A finds A exludes A, thats the problem, you need to specify how long the duplication needs to be if you want to make it variable, but maybe my code helps you.

C# string help?

I have a function in C# that finds the name of a function in a source file such as
function void MyFunc
I'm trying to create a substring that starts after "void " and I need to find the length of the name of the function. There will always be a space or a newline after the function name.
module MyApplication
[EntryPoint]
function void main
write("a string")
endfunction
endmodule
You can use LastIndexOf to find the last space, and grab the part of the string following to get the function name. Then use the Length property to get the length of the code:
var s = "function void MyFunc "; // example string
var s2 = s.Trim(); // remove any extra spaces at the end
var funcName = s2.Substring(s2.LastIndexOf(' ') + 1); // 'MyFunc'
var length = funcName.Length; // 6
Demo: http://www.ideone.com/64IYz
I assume that the function name might have other stuff after it, like a parameter list.
What you want to do is look for the word "void", go past it, find the first non-space character (which is the beginning of the function name), and then go to the next space or end of line.
You can use:
const string LookFor = "void "; // note space at end.
string GetFunctionName(string line)
{
int voidPos = line.IndexOf(LookFor);
if (voidPos == -1)
return null;
int functionStart = voidPos + LookFor.Length;
int spacePos = line.IndexOf(' ', functionStart);
if (spacePos == -1)
spacePos = line.Length;
return line.Substring(functionStart, spacePos - functionStart);
}
That's "crying for using regex". Try this:
Regex regex = new Regex("(function void ){1,1}(?<functionName>^\w*)");
Sprache can do this, but you'd need to write a grammar for the whole file, as it doesn't implement "searching" for a match.
Something along these lines would parse just the function declaration - as noted above, to make your scenario work you need to add rules for modules and so on.
var identifier = (from first in Parse.Letter
from rest in Parse.LetterOrDigit.Many().Text()
select first + rest).Token();
var returnType = Parse.String("void").Or(Parse.String("int")).Token();
var functionKeyword = Parse.String("function").Token();
var endFunctionKeyword = Parse.String("endfunction").Token();
var function = from fk in functionKeyword
from rt in returnType
from functionName in identifier
from body in Parse.AnyChar.Until(endFunctionKeyword)
select functionName;
var name = function.Parse("function void main write(\"a string\") endfunction");
The variable name above will contain the string "main" (unless I've made some typos :))
Sprache is a bit more powerful than regular expressions, but doesn't require any special build-time processing. There are some tutorials on this approach linked from the Sprache homepage.

Count regex replaces (C#)

Is there a way to count the number of replacements a Regex.Replace call makes?
E.g. for Regex.Replace("aaa", "a", "b"); I want to get the number 3 out (result is "bbb"); for Regex.Replace("aaa", "(?<test>aa?)", "${test}b"); I want to get the number 2 out (result is "aabab").
Ways I can think to do this:
Use a MatchEvaluator that increments a captured variable, doing the replacement manually
Get a MatchCollection and iterate it, doing the replacement manually and keeping a count
Search first and get a MatchCollection, get the count from that, then do a separate replace
Methods 1 and 2 require manual parsing of $ replacements, method 3 requires regex matching the string twice. Is there a better way.
Thanks to both Chevex and Guffa. I started looking for a better way to get the results and found that there is a Result method on the Match class that does the substitution. That's the missing piece of the jigsaw. Example code below:
using System.Text.RegularExpressions;
namespace regexrep
{
class Program
{
static int Main(string[] args)
{
string fileText = System.IO.File.ReadAllText(args[0]);
int matchCount = 0;
string newText = Regex.Replace(fileText, args[1],
(match) =>
{
matchCount++;
return match.Result(args[2]);
});
System.IO.File.WriteAllText(args[0], newText);
return matchCount;
}
}
}
With a file test.txt containing aaa, the command line regexrep test.txt "(?<test>aa?)" ${test}b will set %errorlevel% to 2 and change the text to aabab.
You can use a MatchEvaluator that runs for each replacement, that way you can count how many times it occurs:
int cnt = 0;
string result = Regex.Replace("aaa", "a", m => {
cnt++;
return "b";
});
The second case is trickier as you have to produce the same result as the replacement pattern would:
int cnt = 0;
string result = Regex.Replace("aaa", "(?<test>aa?)", m => {
cnt++;
return m.Groups["test"] + "b";
});
This should do it.
int count = 0;
string text = Regex.Replace(text,
#"(((http|ftp|https):\/\/|www\.)[\w\-_]+(\.[\w\-_]+)+([\w\-\.,#?^=%&:/~\+#]*[\w\-\#?^=%&/~\+#])?)", //Example expression. This one captures URLs.
match =>
{
string replacementValue = String.Format("<a href='{0}'>{0}</a>", match.Value);
count++;
return replacementValue;
});
I am not on my dev computer so I can't do it right now, but I am going to experiment later and see if there is a way to do this with lambda expressions instead of declaring the method IncrementCount() just to increment an int.
EDIT modified to use a lambda expression instead of declaring another method.
EDIT2 If you don't know the pattern in advance, you can still get all the groupings (The $ groups you refer to) within the match object as they are included as a GroupCollection. Like so:
int count = 0;
string text = Regex.Replace(text,
#"(((http|ftp|https):\/\/|www\.)[\w\-_]+(\.[\w\-_]+)+([\w\-\.,#?^=%&:/~\+#]*[\w\-\#?^=%&/~\+#])?)", //Example expression. This one captures URLs.
match =>
{
string replacementValue = String.Format("<a href='{0}'>{0}</a>", match.Value);
count++;
foreach (Group g in match.Groups)
{
g.Value; //Do stuff with g.Value
}
return replacementValue;
});

how to parse main arguments?

How can I find this information :
think we started this process :
testFile.exe i- 100 k- "hello" j-"C:\" "D:\Images" f- "true"
Now how can I get main argument when application started so I have :
int i = ... ; //i will be 100
string k = ... ; // k = hello
string[] path = ... ; // = path[0] = "C:\" , path[1] = "D:\Images"
bool f = ... ; // f = true;
regards
The arguments are passed to the Main function that is being called:
static void Main(string[] args)
{
// The args array contain all the arguments being passed:
// args[0] = "i-"
// args[1] = "100"
// args[2] = "k-"
// args[3] = "hello"
// ...
}
Arguments are in the same order as passed in the command line. If you want to use named arguments you may take a look at this post which suggests NDesk.Options and Mono.Options.
You can use Environment.CommandLine or Environment.GetCommandLineArgs()
String[] arguments = Environment.GetCommandLineArgs();
More info on MSDN
As already answered, you can use the string[] args parameter or Environment.GetCommandLineArgs(). Note that for CLickOnce deployed apps you need something else.
You can do your own processing on the string[] or use a library, like this one on CodePlex.
For some tricky details on spaces in filenames and escaping quotes, see this SO question.
You could use NDesk.Options. Here is their documentation.

Categories