Ian
Ian

Reputation: 34539

Matching multiple instances from end of the string

I'm trying to build a regex statement, but am obviously mis-understanding something. I've got a C# StackTrace and want to essentially crop the end of the string until I see the first instance of our namespace. Here is a section of the data to illustrate - with a manually positioned new line to illustrate where I'm trying to match to.

   at MooD.LandscapeExplorer.Controllers.LandscapeController.GetContextInstanceCount(BusinessContext context) in C:\Source\MooDEdge\Code\MooD16\MooD.LandscapeExplorer\Controllers\LandscapeController.cs:line 143
   at System.Linq.Enumerable.<SelectManyIterator>d__1`2.MoveNext()
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at MooD.LandscapeExplorer.Controllers.LandscapeController.ProcessContext(String[] types, String[] instances, QueryFilter[] filters) in C:\Source\MooDEdge\Code\MooD16\MooD.LandscapeExplorer\Controllers\LandscapeController.cs:line 125

   at lambda_method(Closure , ControllerBase , Object[] )
   at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.ActionInvocation.InvokeSynchronousActionMethod()

So I want to keep the first half, and remove the second half from the string. I've used a negative lookahead (I grabbed from another question, I don't really understand how it works), to match on anything other than MooD. Then I've stuck on an end of string $.

(at (?!MooD).*)$

At this point I thought all I needed to do was to add a * to match lots of the previous group, turning the regex into:

(at (?!MooD).*)*$

However this doesn't work as I expected when testing on https://regex101.com/. It still just matches the final instance. I've also tried adding in a new line detection, but that still only matches the final line:

(at (?!MooD).*\n?)*$

Could anyone explain how I can match the rest, and why this solution doesn't work?

Upvotes: 1

Views: 59

Answers (2)

Ron Beyer
Ron Beyer

Reputation: 11273

I know you are trying to do this with regex, but here is a non-regex option:

public static string GetMooDStackTrace(string stackTrace)
{
    var lines = stackTrace.Split('\n');
    int lastLineWithMood = -1;

    for (int i = 0; i < lines.Length; i++)
    {
        if (lines[i].Contains("MooD"))
            lastLineWithMood = i;
    }

    return string.Join("\n", lines, 0, lastLineWithMood + 1);
}

From your input, produces:

at MooD.LandscapeExplorer.Controllers.LandscapeController.GetContextInstanceCount(BusinessContext context) in C:\Source\MooDEdge\Code\MooD16\MooD.LandscapeExplorer\Controllers\LandscapeController.cs:line 143
   at System.Linq.Enumerable.d__1`2.MoveNext()
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at MooD.LandscapeExplorer.Controllers.LandscapeController.ProcessContext(String[] types, String[] instances, QueryFilter[] filters) in C:\Source\MooDEdge\Code\MooD16\MooD.LandscapeExplorer\Controllers\LandscapeController.cs:line 125

Upvotes: 0

anubhava
anubhava

Reputation: 785971

To match bottom part you can use this negative lookahead based regex:

at (?:(?!MooD)[\s\S])*$

(?:(?!MooD)[\s\S])* will match 0 or more of any character while asserting that no character has MooD at next position.

[\s\S] will match any character including newline.

PS: It is safer to use word boundary:

at (?:(?!\bMooD\b)[\s\S])*$

RegEx Demo

Upvotes: 2

Related Questions