Visual Basic 2012 Voorbeelden
   

visual basic 2012 broncode voorbeelden

Blijf op de hoogte van de recente aanpassingen op vbvoorbeelden!

Microsoft Visual Studio 2012Microsoft Developers Network - Visual BasicMicrosoft .NET Framework

28.9. Regular Expressions - Grouping Constructs

Print Email Deel op Twitter Deel op Facebook

Dit artikel is gepubliceerd op maandag 15 oktober 2012 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.

28.9.1. Backreferencing with Grouping Constructs

Men kan in een pattern naar een capture van een eerder gedefinieerde group verwijzen aan de hand van een "backreference".  Dit kan je bijvoorbeeld doen door een backslash \ te laten volgen door het nummer van de groep.
De grouping constructs (tenzij ze "noncapturing" zijn) worden van links naar rechts opeenlopend genummerd.  Let op : eerst worden van links naar rechts de "unnamed" grouping constructs genummerd, daarna van links naar rechts de "named" grouping constructs.  In pattern (?<group1>abc)(def) zal group1 de derde groep (op index 2) zijn, en (def) de tweede (op index 1).  Het volledige pattern wordt als de eerste groep (op index 0) gezien.
Dit is specifiek voor de .NET regex engine, de ander bekende engines nummeren named en unnamed grouping constructs zonder onderscheid.

Noncapturing groups worden niet door de regex engine onthouden en zijn bijgevolg ook bevorderlijk voor de performantie, maar er kan verderop in de reguliere expressie niet meer naartoe verwezen worden.
Een grouping construct is by default capturing, men maakt ze noncapturing door tussen de haakjes met ?: te starten.
Visual Basic 2012 Broncode - Codevoorbeeld 720
Imports System.Text.RegularExpressions
Class Example1
    Public Shared Sub Main()
        Dim input As String = "<a href=""url"">link</a><b>bold</b>"
        Dim tagPattern As String = "<([a-zA-Z][a-zA-Z0-9]*)\b[^>]*>.*?</\1>"
        '
        Dim match As Match
        For Each match In Regex.Matches(input, tagPattern)
            Console.WriteLine(match.Value) ' "<a href="url">link</a>"
        Next                               ' "<b>bold</b>"
        '
        input = "abcdef"
        match = Regex.Match(input, "(?<group1>abc)(def)")
        Dim group1 As Group = match.Groups.Item(0)
        Console.WriteLine(group1.Value)    ' "abcdef"
        Dim group2 As Group = match.Groups.Item(1)
        Console.WriteLine(group2.Value)    ' "def"
        Dim group3 As Group = match.Groups.Item(2)
        Console.WriteLine(group3.Value)    ' "abc"
        '
        Console.ReadLine()
    End Sub
End Class
Console Application Output
<a href="url">link</a>
<b>bold</b>
abcdef
def
abc
Ook naar de naam van een grouping construct kunnen we verwijzen, hierbij wordt de backslash gevolgd door deze naam.

De GroupCollection opgeleverd door de Groups property van een Match object beschikt over een Items property waar men zowel via de index als via de naam, een groep (Group object) kan ophalen :
Visual Basic 2012 Broncode - Codevoorbeeld 721
Imports System.Text.RegularExpressions
Class Example2
    Public Shared Sub Main()
        Dim input As String = "<a href=""url""><i>link</i></a><b>bold</b>"
        Dim tagPattern As String
        tagPattern = "<(?<name>[a-zA-Z][a-zA-Z0-9]*)\b[^>]*>(?<value>.*?)</\<name>>"
        '
        Dim matches As MatchCollection = Regex.Matches(input, tagPattern)
        For Each match As Match In Regex.Matches(input, tagPattern)
            Console.WriteLine("Tag   : " & match.Value)
            Console.WriteLine("Name  : " & match.Groups.Item("name").Value)
            Console.WriteLine("Value : " & match.Groups.Item("value").Value)
            Console.WriteLine()
        Next
        '
        Console.ReadLine()
    End Sub
End Class
Console Application Output
Tag   : <a href="url">link</a>
Name  : a
Value : link

Tag   : <b>bold</b>
Name  : b
Value : bold
In volgend voorbeeld vindt het pattern op regel (1) vind de tekst "b", de grouping construct voor de "b" is immers optioneel (?).

Ook pattern op regel (2) vind de tekst "b", de grouping construct matcht hier omdat de "a" ook nul keer mag voorkomen (a?), de deelmatch (a?) levert niets op, maar faalt ook niet.  Deze deelmatch (geen karakters) wordt ook na de "b" in de inputstring gevonden, waardoor het volledige pattern matcht met de tekst "b".

De grouping construct in het pattern op regel (4) faalt, maar omdat de backreference optioneel is (\1?), maakt het niet uit dat de grouping construct faalt, hierdoor vind ook dit pattern de tekst "b".
Visual Basic 2012 Broncode - Codevoorbeeld 722
Imports System.Text.RegularExpressions
Class Example3
    Public Shared Sub Main()
        Dim input As String = "b"
        Example3.Print(input, "(a)?b")     ' matches "b"                     (1)
        Example3.Print(input, "(a?)b\1")   ' matches "b"                     (2)
        Example3.Print(input, "(a)?b\1")   ' no matches                      (3)
        Example3.Print(input, "(a)?b\1?")  ' matches "b"                     (4)

        input = "ba"
        Example3.Print(input, "(a)?b\1")   ' no matches                      (5)
        '
        Console.ReadLine()
    End Sub
    Public Shared Sub Print(ByVal input As String, ByVal pattern As String)
        Dim r As Regex = New Regex(pattern)
        Dim m As Match = r.Match(input)
        If m.Success Then
            Do
                Console.WriteLine("""" & input & """ has" & _
                                  " match """ & m.Value & """ " & _
                                  "at index " & m.Index.ToString() & _
                                  " for pattern """ & pattern & """")
                m = m.NextMatch()
            Loop While m.Success
        Else
            Console.WriteLine("""" & input & """ has no match" & _
                              " for pattern """ & pattern & """")
        End If
    End Sub
End Class
Console Application Output
"b" has match "b" at index 0 for pattern "(a)?b"
"b" has match "b" at index 0 for pattern "(a?)b\1"
"b" has no match for pattern "(a)?b\1"
"b" has match "b" at index 0 for pattern "(a)?b\1?"
"ba" has no match for pattern "(a)?b\1"
Je ziet aan pattern (5) hoe beide (de grouping construct en backreference) moeten slagen of falen.  Als de group faalt, en de backreference slaagt, of vice versa zal er geen match zijn.  Uiteraard mogen ze beide enkel falen als het pattern dat toestaat.

28.9.2. Optimalisatie via Atomic Grouping Constructs

Als we in een inputstring op zoek gaan naar de tekst "March 1" of "Mar 1" dan kunnen we het pattern (March|Mar) 1 gebruiken (1)(2)(3).

Bij de inputstring op regel (3) wordt er geen match gevonden.  Noch de alternatieve subexpressie March, noch subexpressie Mar gevolgd door <space>1 werd gevonden.
In dergelijke alternating construct worden de subexpressies van links naar rechts geprobeerd te matchen op de inputstring.  Eerst werd de subexpressie March geprobeerd, daarna Mar.
Op het moment dat de regex engine besefte dat de subexpressie March wel matchste met de tekst "March", maar de daaropvolgende tekst " 2" niet meer matchste met de subexpressie <space>1, had het eigenlijk geen zin meer om de alternatieve subexpressie Mar gevolgde door <space>1 te proberen matchen.  De regex engine heeft reeds geconstateerd dat zich op index 6 van de inputstring geen spatie bevindt, want het matchte op die positie met de "c" uit subexpressie March.
Toch zal de regex engine ook deze tweede optie proberen te matchen.

Om deze performance overhead te vermijden, kunnen we gebruik maken van een "atomic grouping construct", deze gebruikt een "nonbacktracking" ("eager") subexpression.
Indien een alternatief reeds volledig werd gematcht, maar de daaropvolgende subexpressie niet meer matcht met de inputstring, dan zal er geen backtracking worden toegepast (wat normaal gezien wel het geval is), maar de volledige grouping construct falen.

Atomic grouping constructs past men toe door de grouping construct te starten met ?>.

(4) Doordat de subexpressie March volledig matcht met de tekst "March" in de inputstring, maar de daaropvolgende 1 (<space>1) niet matcht, zal de engine niet terugkeren in de inputstring naar index 3, om vanaf daar te proberen te matchen met Mar 1.
Wat hier het gewenste effect is, want Mar 1 zal daar toch nooit matchen.

Indien de subexpressie March niet volledig matcht, wordt natuurlijk wel gepoogd om de tweede subexpressie Mar te matchen.  Hierdoor levert hetzelfde pattern voor de inputstring "on Mar 1st" wel een match op (5).
Visual Basic 2012 Broncode - Codevoorbeeld 723
Imports System.Text.RegularExpressions
Class Example4
    Public Shared Sub Main()
        Dim pattern As String
        '
        pattern = "(March|Mar) 1"
        Example3.Print("on March 1st", pattern)  ' matches "March 1"         (1)
        Example3.Print("on Mar 1", pattern)      ' matches "Mar 1"           (2)
        Example3.Print("on March 2nd", pattern)  ' no matches                (3)
        '
        ' Atomic Grouping Construct :
        pattern = "(?>March|Mar) 1"
        Example3.Print("on March 2nd", pattern)  ' no matches                (4)
        Example3.Print("on Mar 1st", pattern)    ' matches "Mar 1"           (5)
        '
        pattern = "(?>March|Mar)ch"
        Example3.Print("on March 1st", pattern)  ' no matches                (6)
        '
        Console.ReadLine()
    End Sub
End Class
Console Application Output
"on March 1st" has match "March 1" at index 3 for pattern "(March|Mar) 1"
"on Mar 1" has match "Mar 1" at index 3 for pattern "(March|Mar) 1"
"on March 2nd" has no match for pattern "(March|Mar) 1"
"on March 2nd" has no match for pattern "(?>March|Mar) 1"
"on Mar 1st" has match "Mar 1" at index 3 for pattern "(?>March|Mar) 1"
"on March 1st" has no match for pattern "(?>March|Mar)ch"
Let op : Als de atomic group (?>March|Mar) gevolgd wordt door ch, zal dit voor inputstring "on March 1st" geen match opleveren (6).
De tekst "March" matchste immers met alternatief March, maar de daarop volgende " 1" matchste niet met ch, de engine keer hier dan ook niet terug naar index 3 in de inputstring om vanaf daar te zoeken naar Mar gevolgd door ch.

Als we op zoek gaan de keywords "For", "Each", "As", "Integer" en "In" en het pattern \b(?>For|Each|As|In|Integer)\b gebruiken, krijgen we geen match voor keyword Integer (1).
Keren we de volgorde van de subexpressies In en Integer om, dan bekomen we wel het gewenste effect (2).

Hoewel atomic grouping constructs intressant zijn voor de performantie, dient men dus voldoende aandacht te besteden aan de volgorde waarin de alternatieven zijn gedefinieerd.
Visual Basic 2012 Broncode - Codevoorbeeld 724
Imports System.Text.RegularExpressions
Class Example5
    Public Shared Sub Main()
        Dim input As String = "For Each element As Integer In array1"
        Dim pattern As String = "\b(?>For|Each|As|In|Integer)\b"
        Example3.Print(input, pattern)                                     ' (1)
        '
        pattern = "\b(?>For|Each|As|Integer|In)\b"
        Example3.Print(input, pattern)                                     ' (2)
        '
        Console.ReadLine()
    End Sub
End Class
Console Application Output
"For Each element As Integer In array1" has match "For" at index 0 for pattern "
\b(?>For|Each|As|In|Integer)\b"
"For Each element As Integer In array1" has match "Each" at index 4 for pattern
"\b(?>For|Each|As|In|Integer)\b"
"For Each element As Integer In array1" has match "As" at index 17 for pattern "
\b(?>For|Each|As|In|Integer)\b"
"For Each element As Integer In array1" has match "In" at index 28 for pattern "
\b(?>For|Each|As|In|Integer)\b"
"For Each element As Integer In array1" has match "For" at index 0 for pattern "
\b(?>For|Each|As|Integer|In)\b"
"For Each element As Integer In array1" has match "Each" at index 4 for pattern
"\b(?>For|Each|As|Integer|In)\b"
"For Each element As Integer In array1" has match "As" at index 17 for pattern "
\b(?>For|Each|As|Integer|In)\b"
"For Each element As Integer In array1" has match "Integer" at index 20 for patt
ern "\b(?>For|Each|As|Integer|In)\b"
"For Each element As Integer In array1" has match "In" at index 28 for pattern "
\b(?>For|Each|As|Integer|In)\b"

28.9.3. Lookaround - Zero-width Assertions

"Lookaround" assertions maakt het mogelijk om veronderstellingen te maken over wat zich voor ("lookbehind") of na ("lookahead") bepaalde tokens zou bevinden.

28.9.4. Lookahead Assertions

Indien men een veronderstelling wil maken, over wat volgt of wat juist niet volgt na een gevonden match, kan men gebruik maken van een "positive" of "negative lookahead assertions".

Positive lookahead assertion kan via een grouping construct dat start met ?=, negative lookeahead assertions starten met ?!.

Als men elke tekst "the" wil vinden die gevolgd wordt door " brown" kan men de lookahead assertion (?= brown) maken (1).
Visual Basic 2012 Broncode - Codevoorbeeld 725
Imports System.Text.RegularExpressions
Class Example6
    Public Shared Sub Main()
        Dim input As String = "the brown fox in the yellow river"
        Example3.Print(input, "the(?= brown)") ' matches "the" at index 0    (1)
        Example3.Print(input, "the(?! brown)") ' matches "the" at index 17
        '
        Example3.Print(input, "(the(?= brown)|brown fox)")                 ' (2)
        '                                        matches "the" at index 0
        '                                        matches "brown fox" at index 4
        Example3.Print(input, "(the brown|brown fox)")                     ' (3)
        '                                        matches "the brown" at index 0
        Console.ReadLine()
    End Sub
End Class
Console Application Output
"the brown fox in the yellow river" has match "the" at index 0 for pattern "the(
?= brown)"
"the brown fox in the yellow river" has match "the" at index 17 for pattern "the
(?! brown)"
"the brown fox in the yellow river" has match "the" at index 0 for pattern "(the
(?= brown)|brown fox)"
"the brown fox in the yellow river" has match "brown fox" at index 4 for pattern
 "(the(?= brown)|brown fox)"
"the brown fox in the yellow river" has match "the brown" at index 0 for pattern
 "(the brown|brown fox)"
Bemerk de verschillende matches van de patterns op regels (2) en (3).
Pattern op regel (2) vind zowel "the" als "brown fox" terug, wat aangeeft dat de substring <space>brown niet werd geconsumeerd.  Een lookaround is immers een zero-width assertion.
Pattern op regel (3) vindt "the brown" en consumeert deze ook, de cursor van de regex engine staat dan ook na het laatste karakter van deze match.  Verder zoekend van op die positie wordt "brown fox" niet meer gevonden.

Er is een verschil tussen een negative lookahead assertion en het werken met een negative character classes.

Zo zoekt in onderstaand voorbeeld het pattern a[^b] niet naar elk tekst waarin a niet gevolgd wordt door een b, maar zoekt het naar elke tekst waarin karakter a gevolgd wordt door een karakter die niet b is (1).
Het pattern a[^b] vind dan ook enkel de match "aa".

Als men dan toch zou willen zoeken naar elke tekst waarin a niet gevolgd wordt door een b, kan men gebruik maken van de negative loohahead assertion a(?!b) (2).
Deze levert bijvoorbeeld ook een karakter a op het eind van de inputstring op.
Visual Basic 2012 Broncode - Codevoorbeeld 726
Imports System.Text.RegularExpressions
Class Example7
    Public Shared Sub Main()
        Dim input As String = "aa ab a"
        Example3.Print(input, "a[^b]")  ' matches "aa"                     ' (1)
        Example3.Print(input, "a(?!b)") ' matches "a" at index 0           ' (2)
        '                                 matches "a" at index 1
        '                                 matches "a" at index 6
        Console.ReadLine()
    End Sub
End Class
Console Application Output
"aa ab a" has match "aa" at index 0 for pattern "a[^b]"
"aa ab a" has match "a" at index 0 for pattern "a(?!b)"
"aa ab a" has match "a" at index 1 for pattern "a(?!b)"
"aa ab a" has match "a" at index 6 for pattern "a(?!b)"
Bemerkt hoe de negative lookahead assertion a(?!b) zowel de "a" op index 0, als de "a" op index 1 vindt.  Nogmaals, lookaround assertions zijn zero-width assertions en consumeren geen karakters.  Nadat de eerste "a" op index 0 matcht met subexpressie a en de daaropvolgende tekst "a" op index 1 ook matcht met subexpressie (?!b), wordt ondanks dat de cursor van de engine zich bevindt na "a" op index 1, na deze eerste totale match, verder gezocht naar matches vanaf index 1.

Stel dat we zoeken via pattern a(?=b)c in de inputstring "abc ac" dan wordt geen enkele match gevonden (1).
Visual Basic 2012 Broncode - Codevoorbeeld 727
Imports System.Text.RegularExpressions
Class Example8
    Public Shared Sub Main()
        Example3.Print("abc ac", "a(?=b)c") ' no matches                     (1)
        '
        Console.ReadLine()
    End Sub
End Class
Console Application Output
"abc ac" has no match for pattern "a(?=b)c"
Bovenstaand pattern zal nooit matches opleveren gezien verondersteld wordt dat volgende op een tekst "a" een "b" moet staan, maar ook een "c" moet staan.

28.9.5. Lookbehind Assertions

Het is via "lookbehind assertions" ook mogelijk een veronderstelling te maken over hetgeen zich voor een bepaalde subexpressie moet bevinden.

De grouping construct moet in dat geval starten met ?< gevolgd door een = voor positive en een ! voor negative lookbehind assertions.
Visual Basic 2012 Broncode - Codevoorbeeld 728
Imports System.Text.RegularExpressions
Class Example9
    Public Shared Sub Main()
        Example3.Print("ab", "(?<=a)b") ' matches "b"
        Example3.Print("ab", "(?<!a)b") ' no matches
        '
        Console.ReadLine()
    End Sub
End Class
Console Application Output
"ab" has match "b" at index 1 for pattern "(?<=a)b"
"ab" has no match for pattern "(?<!a)b"
In de regex engine van .NET kan elke subexpressie gebruikt worden in een lookaround (lookbehind of lookahead).  Dit kan zelf een grouping construct zijn, bijvoorbeeld (?=(...)) of een character class of een alternation construct of ... .
Dit is in lang niet alle andere engines het geval, tot op moment van schrijven beschikt enkel de JGsoft engine over dezelfde capaciteit.  De meeste andere engines hebben of geen support voor lookbehind, of een beperkte support (waar men beperkt is in de mogelijke subexpressies van een lookbehind).

Belangrijk nog is dat lookaround grouping construct noncapturing groups zijn, men kan bijgevolg geen backreference gebruiken naar de lookaround.  Als je een andere grouping construct gebruikt als subexpressie voor de lookaround, dan is het wel mogelijk om verderop naar deze te verwijzen.  In het pattern (?=(<group1>...))...\{group1}... zal de backreference \{group1} verwijzen naar de captures van group1.

28.9.6. Conditional Alternation Constructs

Het is mogelijk om een conditie te koppelen aan een bepaalde subexpressie, dit om aan te geven dat enkel naar een match van deze subexpressie wordt gezocht indien voldaan is aan deze conditie.

De synthax is (?(condition)then|else), het |else gedeelte is optioneel.
De haakjes rond deze alternation construct, noch deze rond de conditie zelf, maken hiervan capturing groups.  Men kan hier bijvoorbeeld via een backreference niet naartoe verwijzen.

Voor de conditie zijn er 2 mogelijkheden, een "expression test" (1)(2) en een "capture test" (3).

Een expression test is altijd een lookaround grouping construct (zero-width assertion en noncapturing), by default is dit een positive lookahead assertions.  De (condition) is dus by default gelijk aan (?=condition).
Het type van lookaround (positive of negative en lookbehind of lookahead) kan worden aangepast.

Een capture test is een verwijzing naar een capturing group, aan de hand van een naam van een named group of gewoon een nummer van capturing group.  In tegenstelling tot een gewone backreference wordt hier geen backslash gebruikt.

Het then en else gedeelte kan bestaan uit eender welke subexpressie, dit kan bijvoorbeeld een alternation construct zijn, plaats in dat geval haakjes rond de verschillende alternatieven, om verwarring tussen de vertical bar operators te voorkomen.  Bijvoorbeeld (?(condition)then|(else1|else2)).

In onderstaand voorbeeld gaat het pattern op regel (1) op zoek naar woorden van 8 karakters die eindigen op "book" of naar het woord "the".
Pattern op regel (2) gaat op zoek naar "Basic" voorafgegaan door de tekst "Visual " of naar "C#" of "J#".
Pattern (5)?6(?(1)7|8) op regel (3) verwijst in zijn conditie (1) naar de capturing group (5).  Er wordt gezocht naar teksten "567", of teksten die niet starten met "5", wel met "6" en daaropvolgend een "8".
Visual Basic 2012 Broncode - Codevoorbeeld 729
Imports System.Text.RegularExpressions
Class Example10
    Public Shared Sub Main()
        Dim input As String
        Dim pattern As String
        '
        input = "this is the textbook"
        pattern = "(?(\b\w{8}\b)\w{4}book|the)"                            ' (1)
        Example3.Print(input, pattern) ' matches "the" and "textbook"
        '
        input = "in Visual Basic and C#"
        pattern = "((?<=Visual)Basic|(C|J)#)"                             ' (2)
        Example3.Print(input, pattern) ' matches "Basic" and "C#"
        '
        input = "567 67 68 568"
        pattern = "(5)?6(?(1)7|8)"                                         ' (3)
        Example3.Print(input, pattern) ' matches "567", "68" at index 7 and
        '                                "68" at index 11
        '
        Console.ReadLine()
    End Sub
End Class
Console Application Output
"this is the textbook" has match "the" at index 8 for pattern "(?(\b\w{8}\b)\w{4
}book|the)"
"this is the textbook" has match "textbook" at index 12 for pattern "(?(\b\w{8}\
b)\w{4}book|the)"
"in Visual Basic and C#" has match "Basic" at index 10 for pattern "((?<=Visual
)Basic|(C|J)#)"
"in Visual Basic and C#" has match "C#" at index 20 for pattern "((?<=Visual)Ba
sic|(C|J)#)"
"567 67 68 568" has match "567" at index 0 for pattern "(5)?6(?(1)7|8)"
"567 67 68 568" has match "68" at index 7 for pattern "(5)?6(?(1)7|8)"
"567 67 68 568" has match "68" at index 11 for pattern "(5)?6(?(1)7|8)"

Dit artikel is gepubliceerd op maandag 15 oktober 2012 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.