How to pass const string into function - c#

How do I pass a const string into a method to be used in a switch statement?
Context
I currently have a program that will parse a file based on customized markers. The markers are constant for the purposes of the program, but ideally can be changed between instances of running the program. I currently have the markers hard coded. I then use them in several switch statements. That works great. However, while refactoring my code, I have been unable to replicate the behavior, because I can't pass the const string variables into the function. While I could just hard code the declaration in the method, that is not ideal nor practical for obvious reasons.
I have Tried
passing the variables as a ref to the original const variables:
Method(ref string keyIndicator)
passing the variables with the 'in' keyword :
Method(in string keyIndicator)
redeclaring the variables constant based on both of the above:
Method(ref string keyIndicator) and Method(in string keyIndicator) followed by :
const string kIndicator = keyIndicator;
Tangent
No matter what I do, I get an error:
The expression being assigned to 'x' must be constant.
But it is constant.
As a side question, why is it that I can not logically assign a constant to a variable (hear me out), such that on initialization, the const string is set for the life of the program? For example:
On program run, program asks user for their name, and then that value is stored in a constant variable.
const string hi = Console.ReadLine(); <- gives me an error.
TLDR
I need to use variables for my switch statement, which I am already doing by hardcoding a constant variable. However, I am trying to refactor my code, and move these switch statements into methods, which as best I can tell, will not allow me to pass the constant variable. I know I must be missing something, as surely do not hardcode all their switch selections, and surely they use switches in methods. So, help a floundering beginner out?
Due to Requests for Code...
Original Code (roughly, I had to jigger it a bit to emulate what it would look like in the context of my program). I'm more confused than ever, because while trying to trim down the code to place here, it actually DID work. But only in a secondary file. I copied the code back to my program, and it doesn't. >:/.
WORKING test file Created to show what I was trying to do...
internal class Program{
static void Main(string[] args)
{
string fileToParse = "!--------------------Demo Key#--------------------DemoBody#--------------------DemoClinical";
//
//
//!!!!!Set Constants for Deliniators!!!!
//
//
const string divider = "--------------------";
const string keySign = "!";
const string bodySign = "#";
const string clinicalSign = "#";
//
//
//!!!!!Set Constants for Deliniators!!!!
//
//
const string keyIndicator = keySign + divider;
const string bodyIndicator = bodySign + divider;
const string clinicalIndicator = clinicalSign + divider;
//
//
List<String> checkDeliniators = new List<String>();
checkDeliniators.Add(keyIndicator);
checkDeliniators.Add(bodyIndicator);
checkDeliniators.Add(clinicalIndicator);
SetIndexes(ref fileToParse, checkDeliniators);
static void SetIndexes(ref string fileToParse, List<string> checkDeliniators)
{
//Create Lists of indexes (Starts and Stops for Text desired)
foreach (var indicator in checkDeliniators)
{
int index = 0;
while ((fileToParse.IndexOf(indicator, index)) != -1)
{
index = fileToParse.IndexOf(indicator, index); // (Undesired text"!"------Desired text)
index = index + indicator.Length; // (Undesired text!-------""Desired text)
//assigns this index to the appropriate list
switch (indicator)
{
case keyIndicator:
Console.WriteLine("Key start {0}", index);
break;
case bodyIndicator:
Console.WriteLine("body start {0}", index);
break;
case clinicalIndicator:
Console.WriteLine("clinical start {0}", index);
break;
default:
// Do this;
break;
}
//sets END of Text
if ((fileToParse.IndexOf(divider, index)) != -1) //(if there are more instances of divider, move index forward)
{
//indexed moved forward to next divider, and then backed up to desired text END.
index = fileToParse.IndexOf(divider, index); // (!"-"------) (diver used because next Indicator not predictable)
index = index - keySign.Length; // ("!"-------) (Recall end index is not inclusive)
switch (indicator) //refers to prior indicator (end), not next indicator (start)
{
case keyIndicator:
Console.WriteLine("Key end {0}", index);
break;
case bodyIndicator:
Console.WriteLine("body end {0}", index);
break;
case clinicalIndicator:
Console.WriteLine("clinical end {0}", index);
break;
default:
// Do this;
break;
}
index++; // (!"-"-------) Technically shouldn't be necessary, but just incase search for divider in future...
}
else //else, if there are no more instances of divider, leave the index alone, and set the end of text to end of file
{
index = fileToParse.Length;
switch (indicator)
{
case keyIndicator:
Console.WriteLine("Key end {0}", index);
break;
case bodyIndicator:
Console.WriteLine("body end {0}", index);
break;
case clinicalIndicator:
Console.WriteLine("clinical end {0}", index);
break;
default:
// Do that;
break;
}
}
}
}
}
}
}
Runs with output
Key start 21
Key end 29
body start 50
body end 58
clinical start 79
clinical end 91
While this same code :
//
//
//!!!!!Set Constants for Deliniators!!!!
//
//
const string divider = "--------------------";
const string keySign = "!";
const string bodySign = "#";
const string clinicalSign = "#";
//
//
//!!!!!Set Constants for Deliniators!!!!
//
//
const string keyIndicator = keySign + divider;
const string bodyIndicator = bodySign + divider;
const string clinicalIndicator = clinicalSign + divider;
//
//
List<String> checkDeliniators = new List<String>();
checkDeliniators.Add(keyIndicator);
checkDeliniators.Add(bodyIndicator);
checkDeliniators.Add(clinicalIndicator);
List<String> KeyList = new List<String>();
List<String> BodyList = new List<String>();
List<String> ClinicalList = new List<String>();
List<String> GarbageList = new List<String>();
List<int> keyStartIndex = new List<int>();
List<int> bodyStartIndex = new List<int>();
List<int> clinicalStartIndex = new List<int>();
List<int> garbageStartIndex = new List<int>();
List<int> keyEndIndex = new List<int>();
List<int> bodyEndIndex = new List<int>();
List<int> clinicalEndIndex = new List<int>();
List<int> garbageEndIndex = new List<int>();
const string fileSign = "|";
//const string commentSign = "~";
//const string allIndicators = keySign + bodySign + clinicalSign;
string fileStartIndicator = fileSign + divider;
string fileEndIndicator = divider + fileSign;
bool headerSet = SetHeader(ref fileToParse, fileStartIndicator, ref fileHeader);
bool footerSet = SetFooter(ref fileToParse, fileEndIndicator, ref fileFooter);
SetIndexes(ref fileToParse, checkDeliniators);
static void SetIndexes(ref string fileToParse, List<string> checkDeliniators)
{
//Create Lists of indexes (Starts and Stops for Text desired)
foreach (var indicator in checkDeliniators)
{
int index = 0;
while ((fileToParse.IndexOf(indicator, index)) != -1)
{
index = fileToParse.IndexOf(indicator, index); // (Undesired text"!"------Desired text)
index = index + indicator.Length; // (Undesired text!-------""Desired text)
//assigns this index to the appropriate list
switch (indicator)
{
case keyIndicator:
keyStartIndex.Add(index);
break;
case bodyIndicator:
bodyStartIndex.Add(index);
break;
case clinicalIndicator:
clinicalStartIndex.Add(index);
break;
default:
garbageStartIndex.Add(index);
break;
}
//sets END of Text
if ((fileToParse.IndexOf(divider, index)) != -1) //(if there are more instances of divider, move index forward)
{
//indexed moved forward to next divider, and then backed up to desired text END.
index = fileToParse.IndexOf(divider, index); // (!"-"------) (diver used because next Indicator not predictable)
index = index - keySign.Length; // ("!"-------) (Recall end index is not inclusive)
switch (indicator) //refers to prior indicator (end), not next indicator (start)
{
case keyIndicator:
keyEndIndex.Add(index);
break;
case bodyIndicator:
bodyEndIndex.Add(index);
break;
case clinicalIndicator:
clinicalEndIndex.Add(index);
break;
default:
garbageEndIndex.Add(index);
break;
}
index++; // (!"-"-------) Technically shouldn't be necessary, but just incase search for divider in future...
}
else //else, if there are no more instances of divider, leave the index alone, and set the end of text to end of file
{
index = fileToParse.Length;
switch (indicator)
{
case keyIndicator:
keyEndIndex.Add(index);
break;
case bodyIndicator:
bodyEndIndex.Add(index);
break;
case clinicalIndicator:
clinicalEndIndex.Add(index);
break;
default:
garbageEndIndex.Add(index);
break;
}
}
}
}
}
Gives me the error that the indicators (i.e. "keyIndicator") need to be constant.

Only literals or constants can be used in labels of switch statements. Most languages' grammars are specified in that way. If you need to switch on variables, replace your switch statement with an if-else statement.
var x = …;
var y = …;
switch (z) {
case x: doX(); break;
case y: doY(); break;
default: doOther(); break;
}
becomes
var x = …;
var y = …;
if (z == x) {
doX();
} else if (z == y) {
doY();
} else {
doOther();
}

In my above code, There was one that worked, and one that didn't, despite the code being identical.
**
The solution was that one Method was nested within Main() {}. While the other was outside of it.
**
Moving the code into Main() allowed the const variables declared there to pass into the method, and the code functions as intended. I am still unsure how to pass const across namespaces or outside of main....but for the time being that was my problem. So to all those, like myself, who like to use switch statements with non-literal cases....There ya go.
I await the knowledge and further clarification to follow :).

Related

Better regular expression for ReverseStringFormat

I've been using for a while this neat function found here on SO:
private List<string> ReverseStringFormat(string template, string str)
{
string pattern = "^" + Regex.Replace(template, #"\{[0-9]+\}", "(.*?)") + "$";
Regex r = new Regex(pattern);
Match m = r.Match(str);
List<string> ret = new List<string>();
for (int i = 1; i < m.Groups.Count; i++)
ret.Add(m.Groups[i].Value);
return ret;
}
This function is able to process correctly templates like:
My name is {0} and I'm {1} years old
While it fails with patterns like:
My name is {0} and I'm {1:00} years old
I would like to handle this failing scenario and add fixed length parsing.
The function transforms the (first) template as following:
My name is (.*?) and I'm (.*?) years old
I've been trying to write the above regular expression to limit the number of characters captured for the second group without success. This is my (terrible) attempt:
My name is (.*?) and I'm (.{2}) years old
I've been trying to process inputs like the following but the below PATTERN doesn't work:
PATTERN: My name is (.*?) (.{3})(.{5})
INPUT: My name is John 123ABCDE
EXPECTED OUTPUT: John, 123, ABCDE
Every suggestion is highly appreciated
It is highly unlikely that you will be able to measure the length of a captured group within the same Regex replacement.
I would strongly suggest you look at the following state machine implementation.
Please note that this implementation also solves the multiple curly brace escape feature of string.Format.
First you will need a state enum, very much like this one:
public enum State {
Outside,
OutsideAfterCurly,
Inside,
InsideAfterColon
}
Then you will need a nice way to iterate over each character in a string.
The string chars parameter represents your template parameter while the returning IEnumerable<string> represents consecutive parts of the resulting pattern:
public static IEnumerable<string> InnerTransmogrify(string chars) {
State state = State.Outside;
int counter = 0;
foreach (var #char in chars) {
switch (state) {
case State.Outside:
switch (#char) {
case '{':
state = State.OutsideAfterCurly;
break;
default:
yield return #char.ToString();
break;
}
break;
case State.OutsideAfterCurly:
switch (#char) {
case '{':
state = State.Outside;
break;
default:
state = State.Inside;
counter = 0;
yield return "(.";
break;
}
break;
case State.Inside:
switch (#char) {
case '}':
state = State.Outside;
yield return "*?)";
break;
case ':':
state = State.InsideAfterColon;
break;
default:
break;
}
break;
case State.InsideAfterColon:
switch (#char) {
case '}':
state = State.Outside;
yield return "{" + counter + "})";
break;
default:
counter++;
break;
}
break;
}
}
}
You could join the parts like so:
public static string Transmogrify(string chars) {
var parts = InnerTransmogrify(chars);
var result = string.Join("", parts);
return result;
}
And then wrap everything up, like you originally intended:
private List<string> ReverseStringFormat(string template, string str) {
string pattern = <<SOME_PLACE>> .Transmogrify(template);
Regex r = new Regex(pattern);
Match m = r.Match(str);
List<string> ret = new List<string>();
for (int i = 1; i < m.Groups.Count; i++)
ret.Add(m.Groups[i].Value);
return ret;
}
Hope you understand why the Regex language isn't expressive enough (at least as far as my understanding is concerned) for this sort of job.
The only way to solve your problem with regular expressions is using a custom matcher to replace the group capture length.
The code bellow does this in your example:
private static string PatternFromStringFormat(string template)
{
// replaces only elements like {0}
string firstPass = Regex.Replace(template, #"\{[0-9]+\}", "(.*?)");
// replaces elements like {0:000} using a custom matcher
string secondPass = Regex.Replace(firstPass, #"\{[0-9]+\:(?<len>[0-9]+)\}",
(match) =>
{
var len = match.Groups["len"].Value.Length;
return "(.{" + len + "*})";
});
return "^" + secondPass + "$";
}
private static List<string> ReverseStringFormat(string template, string str)
{
string pattern = PatternFromStringFormat(template);
Regex r = new Regex(pattern);
Match m = r.Match(str);
List<string> ret = new List<string>();
for (int i = 1; i < m.Groups.Count; i++)
ret.Add(m.Groups[i].Value);
return ret;
}

convert or figure formula which is contained parentheses

i need to find a way to conert treated formula(just using digits,letters and parentheses)
for example, for this input: '5(2(a)sz)' the output should be :'aaszaaszaaszaaszaasz'
i tried in that way:
string AddChainDeleteBracks(int open, int close, string input)
{
string to="",from="";
//get the local chain multipule the number in input[open-1]
//the number of the times the chain should be multiplied
for (int i = input[open - 1]; i > 0; i--)
{
//the content
for (int m = open + 1; m < close; m++)
{
to = to + input[m];
}
}
//get the chain i want to replace with "to"
for (int j = open - 1; j <= close; j++)
{
from = from + input[j];
}
String output = input.Replace(from, to);
return output;
}
but it doesn't work. Do u have a better idea to solve this?
You could store the opening parenthesis positions along with the number associated with that parenthesis in a stack (Last-in-First-out, e.g. System.Collections.Generic.Stack); then when you encounter the first (that is: next) closing parenthesis, pop the top of the stack: this will give you the beginning and ending position of the substring between the (so far most inner) parentheses you need to repeat. Then replace this portion of the original string (including the repetion number) with the repeated string. Continue until you reach the end of the string.
Things to be aware of:
when you do the replacement, you will need to update your current position so it now points to the end of the repetiotion string in the new (modified) string
depending whether 0 repetion is allowed, you might need to handle an empty repetition -- that is an empty string
when you reach the end of the string, the stack should be empty (all opening parentheses were matched with a closing one)
the stack might become empty in the middle of the string -- if you encounter a closing parentheses, the input string was malformed
there might be a way to escape the opening/cloding parentheses, so they don't count as part of the repetition pattern -- this depends on your requirements
Since the syntax of your expression is recursive, I suggest a recursive approach.
First split the expression into single tokens. I use Regex to do it and remove empty entries.
Example: "5(2(a)sz)" is split into "5", "(", "2", "(", "a", ")", "sz", ")"
Using an Enumerator enables you to get the tokens one by one. tokens.MoveNext() gets the next token. tokens.Current is the current token.
public string ConvertExpression(string expression)
{
IEnumerator<string> tokens = Regex.Split(expression, #"\b")
.Where(s => s != "")
.GetEnumerator();
if (tokens.MoveNext()) {
return Parse(tokens);
}
return "";
}
Here the main job is done in a recursive way
private string Parse(IEnumerator<string> tokens)
{
string s = "";
while (tokens.Current != ")") {
int n;
if (tokens.Current == "(") {
if (tokens.MoveNext()) {
s += Parse(tokens);
if (tokens.Current == ")") {
tokens.MoveNext();
return s;
}
}
} else if (Int32.TryParse(tokens.Current, out n)) {
if (tokens.MoveNext()) {
string subExpr = Parse(tokens);
var sb = new StringBuilder();
for (int i = 0; i < n; i++) {
sb.Append(subExpr);
}
s += sb.ToString();
}
} else {
s += tokens.Current;
if (!tokens.MoveNext())
return s;
}
}
return s;
}
Here is my second answer. My first answer was a quick shot. Here I tried to create a parser by doing the things one by one.
In order to convert an expression, you need to parse it. This means that you have to analyze its syntax. While analyzing its syntax you can produce an output as well.
1 The first thing to do, is to define the syntax of all the valid expressions.
Here I use EBNF to do it. EBNF is simple.
{ and } enclose repetitions (possibly zero).
[ and ] encloses an optional part.
| separates alternatives.
See Extended Backus–Naur Form (EBNF) on Wikpedia for more detailed information on EBNF. (The EBNF variant used here drops the concatenation operator ",").
Our syntax in EBNF
Expression = { Term }.
Term = [ Number ] Factor.
Factor = Text | "(" Expression ")" | Term.
Examples
5(2(a)sz) => aaszaaszaaszaaszaasz
5(2a sz) => aaszaaszaaszaaszaasz
2 3(a 2b)c => abbabbabbabbabbabbc
2 Lexical analysis
Before we analyze the syntax we have to split the whole expression into single lexical tokens (numbers, operators, etc.).
We use an enum to indicate the token type
private enum TokenType
{
None,
LPar,
RPar,
Number,
Text
}
The following fields are used to hold the token information and the Boolean _error which tells whether an error occurred during parsing.
private IEnumerator<Match> _matches;
TokenType _tokenType;
string _text;
int _number;
bool _error;
The method ConvertExpression starts the conversion. It splits the expression into single tokens represented as Regex.Matches.
Those are used by the method GetToken, which in turn converts the Regex.Matches into more useful information. This information is stored in the fields described above.
public string ConvertExpression(string expression)
{
_matches = Regex.Matches(expression, #"\d+|\(|\)|[a-zA-Z]+")
.Cast<Match>()
.GetEnumerator();
_error = false;
return GetToken() ? Expression() : "";
}
private bool GetToken()
{
_number = 0;
_tokenType = TokenType.None;
_text = null;
if (_error || !_matches.MoveNext())
return false;
_text = _matches.Current.Value;
switch (_text[0]) {
case '(':
_tokenType = TokenType.LPar;
break;
case ')':
_tokenType = TokenType.RPar;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
_tokenType = TokenType.Number;
_number = Int32.Parse(_text);
break;
default:
_tokenType = TokenType.Text;
break;
}
return true;
}
3 Syntactic and Semantic Analysis
Now we have everything we need to perform the actual parsing and expression conversion. Each of the methods below analyses one EBNF syntax production and returns the result of the conversion as string.
The conversion of EBNF into C# code is straight forward. A repetition in the syntax is converted to a C# loop statement.
An option is converted to an if statement and alternatives are converted to a switch statement.
// Expression = { Term }.
private string Expression()
{
string s = "";
do {
s += Term();
} while (_tokenType != TokenType.RPar && _tokenType != TokenType.None);
return s;
}
// Term = [ Number ] Factor.
private string Term()
{
int n;
if (_tokenType == TokenType.Number) {
n = _number;
if (!GetToken()) {
_error = true;
return " Error: Factor expected.";
}
string factor = Factor();
if (_error) {
return factor;
}
var sb = new StringBuilder(n * factor.Length);
for (int i = 0; i < n; i++) {
sb.Append(factor);
}
return sb.ToString();
}
return Factor();
}
// Factor = Text | "(" Expression ")" | Term.
private string Factor()
{
switch (_tokenType) {
case TokenType.None:
_error = true;
return " Error: Unexpected end of Expression.";
case TokenType.LPar:
if (GetToken()) {
string s = Expression();
if (_tokenType == TokenType.RPar) {
GetToken();
return s;
} else {
_error = true;
return s + " Error ')' expected.";
}
} else {
_error = true;
return " Error: Unexpected end of Expression.";
}
case TokenType.RPar:
_error = true;
GetToken();
return " Error: Unexpected ')'.";
case TokenType.Text:
string t = _text;
GetToken();
return t;
default:
return Term();
}
}

Pars Text or script into C# code

I want to store combination of method invocations and formulas as text or script into database. for example, i want to store something like this in Database as a string and execute it somewhere in code:
if(Vessel.Weight>200)
{
return Cargo.Weight*Cargo.Tariff*2
}
else
{
Cargo.Weight*Cargo.Tariff
}
invocation:
var cost = executeFormula("CalculateTariff",new {Cargo= GetCargo(), Vessel=GetVessel()});
because these rules will change frequently and i don't want to deploy dll (CLR solution), and i don't want to store these rules as SP and mix business rules with DAL.
Any idea or tool?
If you place all values in a hashtable or a dictionary by changing the . with a _ (Vessel.Weight will become "Vessel_Weight") and simplify the syntax to one line it will be much easier to create a solution. This rule can be written for example as:
result=(Vessel_Weight>200)
?(Cargo_Weight*Cargo_Tariff*2)
:(Cargo_Weight*Cargo_Tariff)
Having rules defined like the above one you can use the following (draft, not optimal ...) code as a guide for a properly coded function that will do the job. I repeat that the following code is not perfect, but bottom line it's more than enough as a proof of concept.
Dictionary<string, dynamic> compute = new Dictionary<string, dynamic>();
compute.Add("Vessel_Weight", 123);
compute.Add("Cargo_Weight", 24);
compute.Add("Cargo_Tariff", 9);
string rule = "result=(Vessel_Weight>200)
?(Cargo_Weight*Cargo_Tariff*2)
:(Cargo_Weight*Cargo_Tariff)";
string process = rule.Replace(" ", "");
foreach (Match level1 in Regex.Matches(process, "\\([^\\)]+\\)"))
{
string parenthesis = level1.Value;
string keepit = parenthesis;
Console.Write("{0} -> ", parenthesis);
// replace all named variable with values from the dictionary
foreach (Match level2 in Regex.Matches(parenthesis, "[a-zA-z0-9_]+"))
{
string variable = level2.Value;
if (Regex.IsMatch(variable, "[a-zA-z_]+"))
{
if (!compute.ContainsKey(variable))
throw new Exception("Variable not found");
parenthesis = parenthesis.Replace(variable, compute[variable].ToString());
}
}
parenthesis = parenthesis.Replace("(", "").Replace(")", "");
Console.Write("{0} -> ", parenthesis);
// do the math
List<double> d = new List<double>();
foreach (Match level3 in Regex.Matches(parenthesis, "[0-9]+(\\.[0-9]+)?"))
{
d.Add(double.Parse(level3.Value));
parenthesis = Regex.Replace(parenthesis, level3.Value, "");
}
double start = d[0];
for (var i = 1; i < d.Count; i++)
{
switch (parenthesis[i - 1])
{
case '+':
start += d[i];
break;
case '-':
start -= d[i];
break;
case '*':
start *= d[i];
break;
case '/':
start /= d[i];
break;
case '=':
start = (start == d[i]) ? 0 : 1;
break;
case '>':
start = (start > d[i]) ? 0 : 1;
break;
case '<':
start = (start < d[i]) ? 0 : 1;
break;
}
}
parenthesis = start.ToString();
Console.WriteLine(parenthesis);
rule = rule.Replace(keepit, parenthesis);
}
Console.WriteLine(rule);
// peek a value in case of a condition
string condition = "[0-9]+(\\.[0-9]+)?\\?[0-9]+(\\.[0-9]+)?:[0-9]+(\\.[0-9]+)?";
if (Regex.IsMatch(rule, condition))
{
MatchCollection m = Regex.Matches(rule, "[0-9]+(\\.[0-9]+)?");
int check = int.Parse(m[0].Value) + 1;
rule = rule.Replace(Regex.Match(rule, condition).Value, m[check].Value);
}
Console.WriteLine(rule);
// final touch
int equal = rule.IndexOf("=");
compute.Add(rule.Substring(0, equal - 1), double.Parse(rule.Substring(equal + 1)));
Now the result is a named item in the dictionary. This way you may process more rules in the sense of intermediate results and have a final rule based on them. The code as is written does not guarantee correct execution order for arithmetic operations, but if you keep your rules simple (and possibly split them if is needed) your will achieve your goal.

Creating a command prompt and then control some string methods to do

I wanted to do my program like a command prompt. I thought that it must perform some string methods.
For example: COM> length "string " must return the length of "string " (the command must be bigger than three characters, that is, len "asdf" is OK). In addition to this I added three methods, "reverse", "ToUpper" and "ToLower" and to escape, "quit", and it is not necessarily adjecent or not (can be any spaces between the command and the string).
I wrote on my way, but I think it is not optimized. So do you have any trick to make it faster and reusable?
(I didn't care about any exception so I know there are some exceptions.)
class Program
{
static void Main(string[] args)
{
string str;
for (; ; )
{
Console.Write("CSD>");
str = Console.ReadLine();
if (str == "quit")
break;
commandControl(str);
}
}
public static void commandControl(string str)
{
string strCom, strString;
int index;
str = str.Trim();
index = str.IndexOf(" ");
strCom = str.Substring(0, index);
str = str.Substring(index);
str = str.Trim();
strString = str.Substring(1, str.Length - 2);
string[] strComArry = { "length", "reverse", "upper", "lower" };
int i;
if (strCom.Length >= 3)
{
for (i = 0; i < strComArry.Length; i++)
if (strCom.Length <= strComArry[i].Length)
if (strCom == strComArry[i].Substring(0, strCom.Length))
break;
switch (i)
{
case 0:
Console.WriteLine(strString.Length);
break;
case 1:
for (int j = strString.Length - 1; j >= 0; --j)
System.Console.Write(strString[j]);
Console.WriteLine();
break;
case 2:
Console.WriteLine(strString.ToUpper());
break;
case 3:
Console.WriteLine(strString.ToLower());
break;
}
}
else
Console.WriteLine("Command must be more than 3 characters !...");
}
}
I'm not going to provide a full working example, but take a look at the snippet below. This idea is to use a dictionary of actions to store what to do with commands. That way, you can add new functionality to the interpreter by adding to the "Methods" dictionary.
The idea is call InitializeFunctions to register all of the functions you want available in the interpreter, and then call command control to interpret one.
(I gave two examples on how to add functions, a lambda syntax and referencing a normal function. Also those functions assume you have already parsed what is a command and what is a parameter, which you have done already in your example.)
static Dictionary<string, Action<string>> Methods = new Dictionary<string, Action<string>>();
public static void InitializeFunctions()
{
Methods.Add("ToLower", a => Console.WriteLine(a.ToLower()));
Methods.Add("ToUpper", ToUpper);
}
static void ToUpper(string str)
{
Console.WriteLine(str.ToUpper());
}
public static void commandControl(string str, string param)
{
if(str.Length < 4)
Console.WriteLine("Command must be more than 3 characters !...");
if (Methods.ContainsKey(str))
Methods[str].Invoke(param);
else
Console.WriteLine("Invalid Command");
}

c# pad left to string

i want to find a efficent way to do :
i have a string like :
'1,2,5,11,33'
i want to pad zero only to the numbers that below 10 (have one digit)
so i want to get
'01,02,05,11,33'
thanks
How much do you really care about efficiency? Personally I'd use:
string padded = string.Join(",", original.Split(',')
.Select(x => x.PadLeft(2, '0')));
(As pointed out in the comments, if you're using .NET 3.5 you'll need a call to ToArray after the Select.)
That's definitely not the most efficient solution, but it's what I would use until I'd proved that it wasn't efficient enough. Here's an alternative...
// Make more general if you want, with parameters for the separator, length etc
public static string PadCommaSeparated(string text)
{
StringBuilder builder = new StringBuilder();
int start = 0;
int nextComma = text.IndexOf(',');
while (nextComma >= 0)
{
int itemLength = nextComma - start;
switch (itemLength)
{
case 0:
builder.Append("00,");
break;
case 1:
builder.Append("0");
goto default;
default:
builder.Append(text, start, itemLength);
builder.Append(",");
break;
}
start = nextComma + 1;
nextComma = text.IndexOf(',', start);
}
// Now deal with the end...
int finalItemLength = text.Length - start;
switch (finalItemLength)
{
case 0:
builder.Append("00");
break;
case 1:
builder.Append("0");
goto default;
default:
builder.Append(text, start, finalItemLength);
break;
}
return builder.ToString();
}
It's horrible code, but I think it will do what you want...
string input= "1,2,3,11,33";
string[] split = string.Split(input);
List<string> outputList = new List<string>();
foreach(var s in split)
{
outputList.Add(s.PadLeft(2, '0'));
}
string output = string.Join(outputList.ToArray(), ',');

Categories