Home > Software design >  Regex and LINQ extract group by group name
Regex and LINQ extract group by group name

Time:12-11

I have a SQL sintax in the form of a string in which there are some parameters written in a standard way ", ". Then i have another string with the parameters values written in a standard way as well: "Parameter1=123; Parameter2=aaa". I need to match the parameters in the first SQL with the values in the second one.

What i have so far:`

Dim BodySQL = "Blablabal WHERE X=<Parameter1> AND Y=<Parameter2>"
Dim vmp As RegularExpressions.MatchCollection = RegularExpressions.Regex.Matches("Parameter1=2555; Parameter2 = 12/02/2021", "([\w ] )=([\w ] )")
Dim vmc As RegularExpressions.MatchCollection = RegularExpressions.Regex.Matches(BodySQL, "(?<=\<). ?(?=\>)")

    For Each vm As RegularExpressions.Match In vmc
        Dim Vl As String = (From m As RegularExpressions.Match In vmp
                            Where m.Groups(1).Value.Trim = vm.Value.ToString).Select(Of String)(Function(f) f.Groups(2).Value).ElementAt(0).Trim

        BodySQL = BodySQL.Replace(vm.Value, Vl)
    Next

` It works for the first parameter, but then i get "System.ArgumentOutOfRangeException: 'Specified argument was out of the range of valid values. Parameter name: index'"

Can i please ask why?

CodePudding user response:

You can extract the keys and values with one regex from the param=value strings, create a dictionary out of them, and then use Regex.Replace to replace the matches with the dictionary values:

Imports System.Text.RegularExpressions
' ...
Dim BodySQL = "Blablabal WHERE X=<Parameter1> AND Y=<Parameter2>"

Dim args As New Dictionary(Of String, String)(StringComparer.InvariantCultureIgnoreCase)
' (StringComparer.InvariantCultureIgnoreCase) makes the dictionary keys case insensitive

For Each match As Match In Regex.Matches("PARAMETER1=2555; Parameter2 = 12/02/2021", "(\S )\s*=\s*([^;]*[^;\s])")
    args.Add(match.Groups(1).Value, match.Groups(2).Value)
Next

Console.WriteLine(Regex.Replace(BodySQL, "<([^<>]*)>",
    Function(match)
        Return If(args.ContainsKey(match.Groups(1).Value), args(match.Groups(1).Value), match.Value)
    End Function))

Output:

Blablabal WHERE X=2555 AND Y=12/02/2021

The (\S )\s*=\s*([^;]*[^;\s]) pattern matches

  • (\S ) - captures into Group 1 any one or more non-whitespace chars (the key value)
  • \s*=\s* - a = char enclosed with zero or more whitespace chars
  • ([^;]*[^;\s]) - Group 2: any zero or more chars other than ; and then one char other than ; and whitespace (the value, it cannot be empty with this pattern. If you want it to be possible to match empty values, you will need to remove [^;\s] and then use Trim() on the match.Groups(2).Value in the code.)

The <([^<>]*)> regex matches

  • < - a < char (do not escape this char in any regex flavor please, it is never a special regex metachar)
  • ([^<>]*) - Group 1: any zero or more chars other than < and >
  • > - a literal > char.

Since the key is in Group 1 and < and > on both ends are consumed, the < and > are removed when replacing with the found value. zero or more chars other than > and < between < and >.

CodePudding user response:

The error is self explanatory. You are trying to access an array or List and specifying an index value that is either negative or larger than the largest index available.

.ElementAt(0) / m.Groups(1) / f.Groups(2)

My guess is that one of them might go out of bounds. Try to debug it with a breakpoint and check the values of your variables.

CodePudding user response:

This is what i did with your code:

Dim vmc = "\<(.*?)\>" 

i changed this regex so that it could also give me the "<>"

Dim BodySQL = "Blablabal WHERE X=<Parameter1> AND Y=<Parameter2>"

Dim args As New Dictionary(Of String, String)
For Each match As Match In Regex.Matches("Parameter1=2555; Parameter2 = 12/02/2021", "(\S )\s*=\s*(\S[^;] )")

i changed the regex expression to exclude the ";"

    args.Add(match.Groups(1).Value, match.Groups(2).Value)
Next
Console.WriteLine(Regex.Replace(BodySQL, vmc,
     Function(match As Match)
                 Return If(args.ContainsKey(match.Groups(1).Value), args(match.Groups(1).Value), match.Value)
             End Function))

And now i have what i needed. Thank you a lot :)

Output:

WHERE X = 2555,Y = 12/02/2021
  • Related