1

I have created a program which takes a string (e.g "[2*4]x + [3/2]x"), isolates all the instances where there is text within square brackets and places them within an array 'matches'. It then strips off the square brackets and by some function (i am using the library flee) takes each string (e.g 2*4) and evaluates it before storing it in an array 'answers'. I now need a way to replace the items within square brackets in the original string with the items in the 'answers' array but I am not sure how to do this

public string result(string solution,int num1, int num2, int num3,int num4)
{ 
    Regex regex = new Regex(@"\[.*?\]");
    MatchCollection matches = regex.Matches(solution);
    int count = matches.Count;
    int [] answers = new int [10];
    for (int i = 0; i <= count; i++)
    {
        string match = matches[i].Value;
        match = match.Replace("[", "");
        match = match.Replace("]", "");
        Console.WriteLine(match);
        ExpressionOptions options = new ExpressionOptions();
        options.Imports.AddType(typeof(System.Math));
        ExpressionOwner owner = new ExpressionOwner();
        owner.a = num1;
        owner.b = num2;
        owner.c = num3;
        owner.d = num4;

        Expression expressionmethod = new Expression(match, owner, options);

        try
        {
            ExpressionEvaluator<int> evaluator = (ExpressionEvaluator<int>)expressionmethod.Evaluator;
            int result = evaluator();
            answers[i] = result;
        }
        catch
        {
            ExpressionEvaluator<double> evaluator = (ExpressionEvaluator<double>)expressionmethod.Evaluator;
            double result = evaluator();
            answers[i] = Convert.ToInt32(result);
        }
    }
}
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563

2 Answers2

3

You may use Regex.Replace with a callback method as the replacement argument where you may do whatever you need with the match value and put it back into the resulting string after modifications. Capture all text between square brackets so as to avoid extra manipulation with the match value.

Here is the code:

public string ReplaceCallback(Match m) 
{
    string match = m.Groups[1].Value;
    Console.WriteLine(match);
    ExpressionOptions options = new ExpressionOptions();
    options.Imports.AddType(typeof(System.Math));
    ExpressionOwner owner = new ExpressionOwner();
    owner.a = num1;
    owner.b = num2;
    owner.c = num3;
    owner.d = num4;

    Expression expressionmethod = new Expression(match, owner, options);

    try
    {
        ExpressionEvaluator<int> evaluator = (ExpressionEvaluator<int>)expressionmethod.Evaluator;
        int result = evaluator();
        return result.ToString();
    }
    catch
    {
        ExpressionEvaluator<double> evaluator = (ExpressionEvaluator<double>)expressionmethod.Evaluator;
        double result = evaluator();
        return result.ToString();
    }
}

public string result(string solution,int num1, int num2, int num3,int num4)
{
    return Regex.Replace(solution, @"\[(.*?)]", ReplaceCallback);
}

The \[(.*?)] regex matches [, then matches and captures any 0+ chars other than a newline as few as possible, and then matches a ] char. So, the text between [...] is inside match.Groups[1].Value that is further modified inside the callback method.

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • I'm sorry I'm not familiar with the use of callback methods and a quick google has just left me more confused, could you explain how they work? – Grace Veryan Carling Mar 22 '18 at 09:43
  • See [MSDN](https://learn.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.regex.replace?view=netframework-4.7.1#System_Text_RegularExpressions_Regex_Replace_System_String_System_String_System_Text_RegularExpressions_MatchEvaluator_). The point is that the `ReplaceCallback` method accepts the match object that was found with the regex, and it returns the modified string back that will be used as a replacement by the `Regex.Replace`. – Wiktor Stribiżew Mar 22 '18 at 09:45
  • Definitely a better answer than mine, but it's probably a little more advanced. Not that this is a bad thing, just thinking it might be hard for some to follow, and I'd always recommend using an answer you can understand even if it's not the best one- you've got to maintain the code you write, after all. – Flynn1179 Mar 22 '18 at 09:50
  • 1
    @Flynn1179 Using a callback with Regex.Replace is the most natural solution here since the string is only processed once, there is no need using regex twice and worry about positions where modified data is inserted. It is not "advanced", it is just what OP needs. – Wiktor Stribiżew Mar 22 '18 at 09:55
  • Honestly, I agree with you, but the concept of callback methods or function pointers is something that a beginner might struggle to follow. I wouldn't consider it particularly advanced myself but I've been coding for 30 years. I definitely think it's worth understanding and using this answer over mine; I never knew `Regex.Replace` could do this before, so I learned something too! – Flynn1179 Mar 22 '18 at 10:55
-1

As well as getting the matches, use regex.Split with the same pattern to get the text between the matches.

Then it's just a case of interpolating them with your results to build your result string.

Right after MatchCollection matches = regex.Matches(solution); add the line:

string[] otherStuff = regex.Split(solution);
Debug.Assert(otherStuff.Length == matches.Count + 1); // Optional obviously. Regex can be weird though, I'd check it just in case.

Then you can just do

StringBuilder finalResult = new StringBuilder();
finalResult.Append(otherStuff[0]);
for (int i = 0; i < count; i++)
{
    finalResult.Append(answers[i]);
    finalResult.Append(otherStuff[i+1]);
}

and finalResult should give you what you need.

Flynn1179
  • 11,925
  • 6
  • 38
  • 74