Insert in regex expression - c#

I have following string:
10-5*tan(40)-cos(0)-40*sin(90);
I have extracted the math functions and calculated their values:
tan(40) = 1.42;
cos(0) = 1;
sin(90) = 0;
I want to insert these values back into the expression string as:
10-5*(1.42)-(1)-40*(0);
Please assist

I would use Regex.Replace and then use a custom MatchEvaluator to convert your values and insert these, check:
http://msdn.microsoft.com/en-us/library/cft8645c(v=vs.110).aspx
Which would look something like:
class Program
{
static string ConvertMathFunc(Match m)
{
Console.WriteLine(m.Groups["mathfunc"]);
Console.WriteLine(m.Groups["argument"]);
double arg;
if (!double.TryParse(m.Groups["argument"].Value, out arg))
throw new Exception(String.Format("Math function argument could not be parsed to double", m.Groups["argument"].Value));
switch (m.Groups["mathfunc"].Value)
{
case "tan": return Math.Tan(arg).ToString();
case "cos": return Math.Cos(arg).ToString();
case "sin": return Math.Sin(arg).ToString();
default:
throw new Exception(String.Format("Unknown math function '{0}'", m.Groups["mathfunc"].Value));
}
}
static void Main(string[] args)
{
string input = "10 - 5 * tan(40) - cos(0) - 40 * sin(90);";
Regex pattern = new Regex(#"(?<mathfunc>(tan|cos|sin))\((?<argument>[0-9]+)\)");
string output = pattern.Replace(input, new MatchEvaluator(Program.ConvertMathFunc));
Console.WriteLine(output);
}
}

Related

WriteLine both expression and the resulting value?

I often need to log or write values of expression and also something that gives context to that. So for example:
WriteLine($"_managedHandlePtr:{_managedHandlePtr}");
WriteLine($"metadata.GetNative(_ptr, handle):{metadata.GetNative(_ptr, handle)}");
Is it possible to get the original expression from an interpolated string? Something like the below in concept. It would be nice if I could eliminate the stringly part of this but get the same output:
LogExpression($"{_managedHandlePtr}");
LogExprsssion($"{metadata.GetNative(_ptr, handle)}");
// Writes the non-interpolated string expression, a colon, then the evaluated string
void LogExpression(FormattableString formattableString)
{
Console.WriteLine(${formattableString.GetExpression()}:{formattableString.ToString()});
}
Output:
_managedHandlePtr:213456
metadata.GetNative(_ptr, handle):0xG5DcS4
It would also be fine if it had to include the notation from the original string:
{_managedHandlePtr}:213456
I don't think you can get much more than the types of the arguments of the FormtableString.
static class Program
{
static void Main(string[] args)
{
Number a = new Number(1.0); // 1.0
Percent b = new Percent(2.0); // 2%
double c = a + b;
Console.WriteLine(Format($"{a} + {b} = {c}"));
// Number + Percent = Double : 1 + 2% = 1.02
}
static string Format(this FormattableString expression, CultureInfo info = null)
{
if (info == null)
{
info = CultureInfo.InvariantCulture;
}
object[] names = expression.GetArguments().Select((arg) => arg.GetType().Name).ToArray();
return $"{string.Format(expression.Format, names)} : {expression.ToString(info)}";
}
}

What is the best practice to resolve placeholders in a plain text?

I need to resolve a huge load of placeholders (about 250) in a plain text.
A placeholder is defined as %ThisIsAPlaceholder%, an example would be %EmailSender%.
Now it's gets a bit creepy: the code should handle case insensitive placeholders too. So, %EmailSender%, %EMAILSENDER% and %emailsender% are the same placeholder. I think that's where it gets complicated.
My first approach was the something like:
public string ResolvePlaceholders(string text)
{
var placeholders = new IEnumerable<string>
{
"%EmailSender%",
"%ErrorMessage%",
"%ActiveUser%"
};
var resolvedText = text;
foreach(var placeholder in placeholders)
{
if(!replacedText.Contains(placeholder))
continue;
var value = GetValueByPlaceholder(placeholder);
resolvedText = resolvedText.Replace(placeholder, value);
}
return resolvedText;
}
But.. as you may notice, i can't handle case insesitive placeholders.
Also i check for every placeholder (if it is used in the text). When using > 200 placholders in a text with about 10'000 words i think this solution is not very fast.
How can this be solved in a better way? A solution that supports case insensitive placeholders would be appreciated.
A really basic but efficient replacement scheme for your case would be something like this:
private readonly static Regex regex = new Regex("%(?<name>.+?)%");
private static string Replace(string input, ISet<string> replacements)
{
string result = regex.Replace(input, m => {
string name = m.Groups["name"].Value;
string value;
if (replacements.Contains(name))
{
return GetValueByPlaceholder(name);
}
else
{
return m.Captures[0].Value;
}
});
return result;
}
public static void Main(string[] args)
{
var replacements = new HashSet<string>(StringComparer.CurrentCultureIgnoreCase)
{
"EmailSender", "ErrorMessage", "ActiveUser"
};
string text = "Hello %ACTIVEUSER%, There is a message from %emailsender%. %errorMessage%";
string result = Replace(text, replacements);
Console.WriteLine(result);
}
It will use a regular expression to go through the input text once. Note that we are getting case-insensitive comparisons via the equality comparer passed to the HashSet that we constructed in Main. Any unrecognized items will be ignored. For more general cases, the Replace method could take a dictionary:
private static string Replace(string input, IDictionary<string, string> replacements)
{
string result = regex.Replace(input, m => {
string name = m.Groups["name"].Value;
string value;
if (replacements.TryGetValue(name, out value))
{
return value;
}
else
{
return m.Captures[0].Value;
}
});
return result;
}
A typical recommendation when matching using quantifiers on input from an untrusted source (e.g. users over the internet) is to specify a match timeout for the regular expression. You would have to catch the RegexMatchTimeoutException that is thrown and do something in that case.
Regex solution
private static string ReplaceCaseInsensitive(string input, string search, string replacement)
{
string result = Regex.Replace(
input,
Regex.Escape(search),
replacement.Replace("$","$$"),
RegexOptions.IgnoreCase
);
return result;
}
Non regex solution
public static string Replace(this string str, string old, string #new, StringComparison comparison)
{
#new = #new ?? "";
if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(old) || old.Equals(#new, comparison))
return str;
int foundAt;
while ((foundAt = str.IndexOf(old, 0, StringComparison.CurrentCultureIgnoreCase)) != -1)
str = str.Remove(foundAt, old.Length).Insert(foundAt, #new);
return str;
}
Seems like a duplicate question / answer
String.Replace ignoring case

Split a string and convert one of the index as int

I have the following code:
static void Main(string[] args)
{
string prueba = "Something_2.zip";
int num;
prueba = prueba.Split('.')[0];
if (!prueba.Contains("_"))
{
prueba = prueba + "_";
}
else
{
//The code I want to try
}
}
The idea is that in the else I want to split the string after the _ and convert it to int, I did this like
num = Convert.ToInt16(prueba.Split('_')[1]);
but can I cast the split? for example num = (int)(prueba.Split('_')[1]);
Is it possible to do it that way? Or I have to use the Convert?
You Cannot cast string as integer, so you need to do some conversion:
I suggest you to use Int.TryParse() in this scenario.
Hence the else part will be like the following:
else
{
if(int.TryParse(prueba.Substring(prueba.LastIndexOf('_')),out num))
{
//proceed your code
}
else
{
//Throw error message
}
}
Convert the string to int like this:
var myInt = 0;
if (Int32.TryParse(prueba.Split('_')[1], out myInt))
{
// use myInt here
}
It's a string, so you'll have to parse it. You can use Convert.ToInt32, int.Parse, or int.TryParse to do so, like so:
var numString = prueba.Split('_')[1];
var numByConvert = Convert.ToInt32(numString);
var numByParse = int.Parse(numString);
int numByTryParse;
if(int.TryParse(numString, out numByTryParse))
{/*Success, numByTryParse is populated with the numString's int value.*/}
else
{/*Failure. You can handle the fact that it failed to parse now. numByTryParse will be 0 */}
string prueba = "Something_2.zip";
prueba = prueba.Split('.')[0];
int theValue = 0; // Also default value if no '_' is found
var index = prueba.LastIndexOf('_');
if(index >= 0)
int.TryParse(prueba.Substring(index + 1), out theValue);
theValue.Dump();
You could use a regular expression and avoid all the string splitting logic. If you need an explanation of the regex I've used see https://regex101.com/r/fW9fX5/1
var num = -1; // set to default value that you intend to use when the string doesn't contain an underscore
var fileNamePattern = new Regex(#".*_(?<num>\d+)\..*");
var regexMatch = fileNamePattern.Match("Something_2.zip");
if (regexMatch.Success)
{
int.TryParse(regexMatch.Groups["num"].Value, out num);
}

Comparing string

Is there any method that allow us to return true if string a likes string b formality?
Exam:
"12:2".Like("*:*") = true
or
"what is your name?".Like("*is*name*?")=true
Thanks!
You can use this following function using Regular Expression
Regex.IsMatch("string", "your expression")
Instance following line should return true:
Regex.IsMatch("12:2", "/[0-9]{2,}:[0-9]{1,}/")
Note: This way you have to create exp every time for different format
You can use the following method to check whether a given string matches a DOS like pattern with wildcards (i.e. a pattern that denotes one character with '?' and zero or more characters with '*'):
public static bool IsMatch(string str, string pattern)
{
string regexString = "^" + Regex.Escape(pattern).Replace("\\*", ".*").Replace("\\?", ".") + "$";
Regex regex = new Regex(regexString);
return regex.IsMatch(regexString);
}
You can call it like this:
bool match = IsMatch("what is your name?", "*is*name*?"); // Returns true
You can use the following not-optimized method. The function may does not take into account some cases, but i think it gives you a point to start from.
Another possible solution is to you Regular Expressions
public static bool Like(string pattern, string str)
{
string[] words = pattern.Split('*').Where(w => w.Trim() != string.Empty).ToArray();
List<int> indeces = new List<int>();
for (int i = 0, l = words.Length; i < l; i++)
{
int wordIndex = str.IndexOf(words[i], StringComparison.OrdinalIgnoreCase);
if (wordIndex == -1)
return false;
else
indeces.Add(wordIndex);
}
List<int> sortedIndeces = indeces.ToList();
sortedIndeces.Sort();
for (int i = 0, l = sortedIndeces.Count; i < l; i++)
{
if (sortedIndeces[i] != indeces[i]) return false;
}
return true;
}
Good Luck

How to Regex replace match group item with method result

The input string is something like this:
LineA: 50
LineB: 120
LineA: 12
LineB: 53
I would like to replace the LineB values with a result of MultiplyCalculatorMethod(LineAValue), where LineAValue is the value of the line above LineB and MultiplyCalculatorMethod is my other, complicated C# method.
In semi-code, I would like to do something like this:
int MultiplyCalculatorMethod(int value)
{
return 2 * Math.Max(3,value);
}
string ReplaceValues(string Input)
{
Matches mat = Regex.Match(LineA:input_value\r\nLineB:output_value)
foreach (Match m in mat)
{
m.output_value = MultiplyCalculatorMethod(m.input_value)
}
return m.OutputText;
}
Example:
string Text = "LineA:5\r\nLineB:2\r\nLineA:2\r\nLineB:7";
string Result = ReplaceValues(Text);
//Result = "LineA:5\r\nLineB:10\r\nLineA:2\r\nLineB:6";
I wrote a Regex.Match to match LineA: value\r\nLineB: value and get these values in groups. But when I use Regex.Replace, I can only provide a "static" result that is combining groups from the match, but I can not use C# methods there.
So my questions is how to Regex.Replace where Result is a result of C# method where input is LineA value.
You can use a MatchEvaluator like this:
public static class Program
{
public static void Main()
{
string input = "LineA:5\r\nLineB:2\r\nLineA:2\r\nLineB:7";
string output = Regex.Replace(input, #"LineA:(?<input_value>\d+)\r\nLineB:\d+", new MatchEvaluator(MatchEvaluator));
Console.WriteLine(output);
}
private static string MatchEvaluator(Match m)
{
int inputValue = Convert.ToInt32(m.Groups["input_value"].Value);
int outputValue = MultiplyCalculatorMethod(inputValue);
return string.Format("LineA:{0}\r\nLineB:{1}", inputValue, outputValue);
}
static int MultiplyCalculatorMethod(int value)
{
return 2 * Math.Max(3, value);
}
}
Try using following Replace overload.
public static string Replace( string input, string pattern, MatchEvaluator evaluator);
MatchEvaluator has access to Match contents and can call any other methods to return the replacement string.

Categories