[VBSCRIPT] via regexp controleren op positie x in een string

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • Yucon
  • Registratie: December 2000
  • Laatst online: 10:05
Probleemstelling: ik moet in een string checken of bepaalde posities bepaalde waarden hebben, in diverse combinaties. Bijvoorbeeld:

pos 15 = A
pos 17 = C of P
pos 21 <> F

Deze waardes zijn steeds weer wisselend. Wat er gebeurt is dat ik orderregels moet inlezen en aan de hand van iets als hierboven staat moet matchen met een artikelbestand. Elk artikel heeft een string en elke orderregel heeft een formule zoals hierboven. Dit klinkt omslachtig maar de onderliggende reden is dat bovenstaande uit een keuzelijstje volgt en de check dus nodig is om tot een lijst van artikelnrs te komen. Ofwel, elke orderregel moet tegen een hele lijst artikelen gecheckt worden om te bepalen welke artikelen nodig zijn om die orderregel te vervullen.

M'n programmeerkennis is ondertussen nogal roestig heb ik gemerkt. Ik heb vroeger wel eens wat met regexp gedaan en mogelijk is dat geschikt, maar als ik de diverse tutorials doorlees zie ik eigenlijk geen duidelijke manier om specifiek op een bepaalde positie te checken. Nu kan ik door al die strings gaan loopen en conditie voor conditie met stringfuncties te checken maar het lijkt me veel eleganter om per te checken artikel 1 regexp op te bouwen en die te gebruiken. Dan heb je met 1 call immers een ja of nee.

Kunnen jullie wat richting geven wat een handige aanpak is?

- zou ik dit wel met regexp moeten doen?
- zo ja, dan neig ik ernaar om bij bv pos 17 uit te komen door aan te geven dat pos 0-16 een vaste lengte van 16 met willekeurige inhoud moeten zijn, en vervolgens 17 te checken. Is dat de aanpak of is er iets slimmers?

Acties:
  • 0 Henk 'm!

  • Yucon
  • Registratie: December 2000
  • Laatst online: 10:05
Ik heb het gevonden. Ik had me niet gerealiseerd dat je de string door moet wandelen in plaats van gericht op een positie moet checken, maar met iets als [A-Z0-9 ]{14}A{1}[A-Z0-9 ]{2}[CP][A-Z0-9 ]{4}[^F]{1} werkt het.

[ Voor 12% gewijzigd door Yucon op 01-08-2017 09:59 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Eventueel kun je ook met mid() het zoveelste karakter van een string opzoeken:

code:
1
if mid("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 20, 1) = "T" then print "yes!"

[ Voor 9% gewijzigd door Verwijderd op 01-08-2017 10:30 ]


Acties:
  • 0 Henk 'm!

  • Stoelpoot
  • Registratie: September 2012
  • Niet online
Het lijkt mij logischer dit inderdaad niet met Regex te doen. Als ik al kijk naar die ene regex krijg ik hoofdpijn, en zodra die volgende week aangepast moet worden jij ook.

Inderdaad is het netjes om per artikel gewoon 1 call uit te voeren. Maar die logica kan je ook best in een andere functie uitvoeren. CheckString(str) van ik bovendien netter dan Regexp(str, "Eldritch Incantations");

Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 06-10 13:59
Yucon schreef op dinsdag 1 augustus 2017 @ 08:45:
- zou ik dit wel met regexp moeten doen?
Nee. Je oplossing laat dit wel zien; die regex is praktisch onleesbaar.

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Gomez12
  • Registratie: Maart 2001
  • Laatst online: 17-10-2023
Als je het al met regex zou willen doen (wat imho compleet verkeerd is, vraag gewoon het character op positie x op) dan zou ik alsnog voor de volgende imho leesbaardere regex gaan :
.{14}A{1}.{2}[CP]{1}.{4}[^F]{1}

Alleen de pest van die regex is dat je er eigenlijk direct boven 3 regels commentaar kan gaan zetten om uit te leggen wat je wilt bereiken.
Terwijl als je die 3 regels gewoon in code verwerkt met een mid van josko dan is het gewoon zelfdocumenterende code.

Acties:
  • 0 Henk 'm!

  • Yucon
  • Registratie: December 2000
  • Laatst online: 10:05
Verwijderd schreef op dinsdag 1 augustus 2017 @ 10:29:
Eventueel kun je ook met mid() het zoveelste karakter van een string opzoeken:

code:
1
if mid("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 20, 1) = "T" then print "yes!"
Het vervelende is dat elke orderregel een andere formule zal hebben. En ook een wisselend aantal elementen. Als je dit met stringfuncties moet doen lijkt het resultaat me nog steeds slecht leesbaar, en dat is de reden dat ik naar alternatieven zocht.

Eigenlijk ben ik met beide oplossingen niet gelukkig.. maar het is nu eenmaal een nogal onhandig formaat dat verwerkt moet worden. Vooral die meerdere opties van de 'of' maken het rommelig.

dus artikel 1 heeft
pos 15 = A
pos 17 = C of P
pos 21 <> F


artikel 2 heeft
pos 21 = E
pos 22 = A
pos 43 <> F

artikel 3 heeft
pos 10 = A, C, D of F
pos 12 = E
pos 13 <> 7
pos 18 = Z
pos 21 <> 3

de echte notitie is als |10ACDF&12E^137&18Z^213. De orderregels hebben een dimensieveld als "KLJ 45 KK 23 6 JH23 6 34 7 34 UUBB 23L JBKHBHBJH"

Denken jullie echt dat via stringfuncties makkelijker wordt? Dat wordt namelijk dynamisch loopen vanwege de wisselende lengte van de of's, het wisselend aantal conditionele eisen en het feit dat elk afzonderlijke eis een kan betekenen dat een artikel afgebroken wordt. De match moet immers 100% zijn.

regexp wordt natuurlijk ook niet leesbaar maar dat is in zekere zin nog een directe vertaling van "|10ACDF&12E^137&18Z^213" waardoor het met wat gepuzzel te herleiden is. Die kan ik dus laten genereren.

Acties:
  • 0 Henk 'm!

Verwijderd

Hoeveel 'smaken' van die notities zijn er? Handjevol of heel veel?

Die notitie-string, is dat iets wat gebruikt blijft? Zo nee dan kun je zelf een alternatief maken in VB. Zo ja, dan kun je hier inderdaad beter iets mee doen. Er is vast eenvoudig een function te schrijven welke zo'n notitie-string kan verifieren.

- Zijn de posities in die notitiestring altijd >= 10 en <= 99 ?
- Wat betekent de | aan het begin? Kan hij ook beginnen met een ^ ? Zoja, hoe ziet zo'n string eruit?

Acties:
  • 0 Henk 'm!

  • Yucon
  • Registratie: December 2000
  • Laatst online: 10:05
Verwijderd schreef op dinsdag 1 augustus 2017 @ 11:34:
Hoeveel 'smaken' van die notities zijn er? Handjevol of heel veel?

Die notitie-string, is dat iets wat gebruikt blijft? Zo nee dan kun je zelf een alternatief maken in VB. Zo ja, dan kun je hier inderdaad beter iets mee doen. Er is vast eenvoudig een function te schrijven welke zo'n notitie-string kan verifieren.

- Zijn de posities in die notitiestring altijd >= 10 en <= 99 ?
- Wat betekent de | aan het begin? Kan hij ook beginnen met een ^ ? Zoja, hoe ziet zo'n string eruit?
Het aantal theoretische formule-notities is bijna oneindig, maar in de praktijk zijn er een paar honderd werkelijke combinaties. Die blijft in gebruik.. aan het formaat kan ik niets veranderen.

De positievelden zijn inderdaad altijd 2 posities lang
die | betekent dat de waardereeks die erna komt als een of gelezen moet worden. Het kan inderdaad ook beginnen met & of ^. Voorbeeld: |23ABC wil dus zeggen dat positie 23 een A, B of C moet zijn.

Wat ik nu sowieso al doe is door middel van stringfuncties de 'segmenten' (operator, positie en conditiewaarde) splitten en per segment in die drie elementen uit elkaar trekken. Met stringfuncties denk ik dat ik 1 boolvariabele moet gebruiken die aangeeft of een van de checks gefaald heeft. Dan wordt het geheel immers verworpen. Ik ga even zoeken naar de 'of'. In sql heb je iets als woord in ('aap', 'noot', 'mies') maar dat bestaat in vb niet geloof ik.

edit: volgens mij moet ik het splitten. Alles met stringfuncties, en alleen die 'of' met een simpele regex

[ Voor 5% gewijzigd door Yucon op 01-08-2017 11:48 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Kun je niet andersom werken? Dus uit het begin van de code een productnummer extraheren, en dan aan de hand van dat product alle mogelijke varianten langsgaan om zo een 'nieuw' ordernummer te genereren en dan te kijken of die hetzelfde is als het ordernummer dat je zoekt?

Acties:
  • 0 Henk 'm!

  • Yucon
  • Registratie: December 2000
  • Laatst online: 10:05
Verwijderd schreef op dinsdag 1 augustus 2017 @ 12:25:
Kun je niet andersom werken? Dus uit het begin van de code een productnummer extraheren, en dan aan de hand van dat product alle mogelijke varianten langsgaan om zo een 'nieuw' ordernummer te genereren en dan te kijken of die hetzelfde is als het ordernummer dat je zoekt?
Helaas, want ik ken de betekenis van die lange string per orderregel ook niet. Er staat ook veel info in die niet voor intern gebruik van de betreffende klant is.

Maar het is ondertussen gelukt! Ik heb het uiteindelijk met stringfuncties gedaan en dan een simpele regexp voor de of-varianten.

Bedankt allemaal!

[ Voor 12% gewijzigd door Yucon op 01-08-2017 12:53 ]


Acties:
  • +1 Henk 'm!

Verwijderd

Zoiets zou kunnen werken:

Visual Basic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
Sub TestProgramma()
    Dim goed As Boolean
    goed = ControleerOrderNummer("KLJ 45 KK 23 6 JH23 6 34 7 34 UUBB 23L JBKHBHBJH", "|10ACDF&12E^137&18Z^213")
    Debug.Print IIf(goed, "HIJ VOLDOET!", "JAMMER, DAN")

    goed = ControleerOrderNummer("KLJ 45 KKD2E66 JHZ3 6 34 7 34 UUBB 23L JBKHBHBJH", "|10ACDF&12E^137&18Z^213")
    Debug.Print IIf(goed, "HIJ VOLDOET!", "JAMMER, DAN")

End Sub

Function ControleerOrderNummer(orderNummer As String, patroon As String) As Boolean
    
    Dim myRegExp As New RegExp
    Dim myMatches As MatchCollection
    Dim myMatch As match
    
    myRegExp.Global = True
    myRegExp.Pattern = "([|&^])([0-9]{2})([^|&^]+)"
    Set myMatches = myRegExp.Execute(patroon)
    For Each myMatch In myMatches
        Debug.Print "WE GAAN CONTROLEREN: " & myMatch.Value
        If Not ControleerOrderNummerDeel(orderNummer, myMatch.SubMatches(0), CInt(myMatch.SubMatches(1)), myMatch.SubMatches(2)) Then
            ControleerOrderNummer = False
            Exit Function
        End If
    Next
    
    ControleerOrderNummer = True

End Function

Function ControleerOrderNummerDeel(orderNummer As String, oper As String, positie As Integer, karakters As String)
    Dim idx As Integer
    Dim OrderKarakter As String: OrderKarakter = Mid(orderNummer, positie, 1)
    
    If OrderKarakter = "" Then Exit Function
    
    Select Case oper
        Case "^":
            If InStr(karakters, OrderKarakter) Then
                Debug.Print "HELAAS: '" & OrderKarakter & "' zit in '" & karakters & "'"
                Exit Function
            End If
            
        Case "&", "|":
            If InStr(karakters, OrderKarakter) Then
            Else
                Debug.Print "HELAAS: '" & OrderKarakter & "' zit niet in '" & karakters & "'"
                Exit Function
            End If
            
        Case Default:
            Debug.Print "HELAAS: Onbekende oper: " & oper
            Exit Function
            
    End Select
    
    ControleerOrderNummerDeel = True
End Function

Acties:
  • 0 Henk 'm!

  • Gomez12
  • Registratie: Maart 2001
  • Laatst online: 17-10-2023
Yucon schreef op dinsdag 1 augustus 2017 @ 11:27:
[...]
Het vervelende is dat elke orderregel een andere formule zal hebben. En ook een wisselend aantal elementen. Als je dit met stringfuncties moet doen lijkt het resultaat me nog steeds slecht leesbaar, en dat is de reden dat ik naar alternatieven zocht.
Dan zou ik het stiekem toch wel met regexen doen (of zelf een parser schrijven als ik heel veel tijd over had) alleen dan niet met de verkorte schrijfwijze maar met de uitgebreide schrijfwijze voor het grotere geheel.
Dan kan je een redelijk overzichtelijke library opbouwen als ze onder elkaar staan.

..............A..[CP]....^F
Yucon schreef op dinsdag 1 augustus 2017 @ 12:52:
[...]
Helaas, want ik ken de betekenis van die lange string per orderregel ook niet. Er staat ook veel info in die niet voor intern gebruik van de betreffende klant is.
Tja en hier ging het in het begin dus al fout. Ik vermoed zo maar dat het gewoon een codering is en dat je die gewoon in deeltjes moet parsen en niet als 1 geheel.

Acties:
  • 0 Henk 'm!

Verwijderd

Kun je niet de definities maken met iets als onderstaand, en dan dat object doorlopen?

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var codes = {
    product1: [
        { pos: 15, match: [ 'A' ] },
        { pos: 17, match: [ 'C', 'P' ] },
        { pos: 21, nomatch: [ 'F' ] },
    ],
    product2: [
//etcetera
    ]
}
function getProduct(orderno) {
    codes.forEach(function(code, productid)) {
        code.forEach(function(check)) {
            // check of de volgende match of nomatch van toepassing is
        }
        // als alle matches okay waren, dan return productid;
    }
    return null;
}

Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 13-09 00:05
Yucon schreef op dinsdag 1 augustus 2017 @ 11:27:
pos 10 = A, C, D of F
pos 12 = E
pos 13 <> 7
pos 18 = Z
pos 21 <> 3

de echte notitie is als |10ACDF&12E^137&18Z^213.
Met andere woorden, jullie hebben een Domain Specific Language (DSL) uitgevonden, maar geen implementatie gemaakt. En nu wil je een cross-compiler van jullie DSL naar een regex. Ow.

En om het helemaal makkelijk te maken moet je uitvinden dat "137" geparsed moet worden als "karakter '7' op plek 13". Het is niet eens 1 getal.

Gezien de kwaliteit van de DSL zou ik niet proberen om deze te compileren naar een regex, dat is nooit robuust te krijgen. Bouw een top-level parser voor de individuele condities (begin nieuw token op ^ en &), bouw drie parsers voor tokens die beginnen met |, & en ^, en combineer de resultaten van elke sub-expressie die je op die manier krijgt.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Acties:
  • 0 Henk 'm!

  • Yucon
  • Registratie: December 2000
  • Laatst online: 10:05
MSalters schreef op dinsdag 1 augustus 2017 @ 16:44:
[...]

Met andere woorden, jullie hebben een Domain Specific Language (DSL) uitgevonden, maar geen implementatie gemaakt. En nu wil je een cross-compiler van jullie DSL naar een regex. Ow.

En om het helemaal makkelijk te maken moet je uitvinden dat "137" geparsed moet worden als "karakter '7' op plek 13". Het is niet eens 1 getal.

Gezien de kwaliteit van de DSL zou ik niet proberen om deze te compileren naar een regex, dat is nooit robuust te krijgen. Bouw een top-level parser voor de individuele condities (begin nieuw token op ^ en &), bouw drie parsers voor tokens die beginnen met |, & en ^, en combineer de resultaten van elke sub-expressie die je op die manier krijgt.
"we" hebben helemaal niets uitgevonden. Dit is een opgelegd formaat van enkele decennia oud.

Acties:
  • 0 Henk 'm!

Verwijderd

oh, ik zie net dat je VBScript wil hebben en mijn voorbeeldje was VBA 8)7

Acties:
  • +1 Henk 'm!

  • Yucon
  • Registratie: December 2000
  • Laatst online: 10:05
Verwijderd schreef op dinsdag 1 augustus 2017 @ 18:14:
oh, ik zie net dat je VBScript wil hebben en mijn voorbeeldje was VBA 8)7
Ik had hem gezien maar moest m'n hoofd nog even leegmaken voor ik het door ging lezen. Van dit soort dingen wordt je aardig gaar als je er te lang naar staart. Het is ondertussen gelukt, morgen ga ik m'n code wat opschonen en eens naast andere voorbeelden in dit topic zetten om te zien wat handig is. In ieder geval heel erg bedank voor de moeite!

[ Voor 7% gewijzigd door Yucon op 01-08-2017 19:59 ]

Pagina: 1