Regex to exclude a specific value in key-value pairs with same pattern

Viewed 145

From the text below I would catch only the data after FCST VA CLD +6HR/12HR/18HR:. To do this I built a regex that gives me the data I want but it also gives me the data after OBS VA CLD that I do not want.

Please note that the fields may be reported or not.

I am using https://regex101.com/r/EPxUHG/2:

([\d]+/[\d\w]+)?\s?([\w]+/[\w\d]+)\s((?:[\w]{1}[\d]{4}\s[\w]{1}[\d]{5}\s\-\s)+[\w]{1}[\d]{4}\s[\w]{1}[\d]{5})

The text is

VA ADVISORY
STATUS: EXER
DTG: 20211116/0600Z
VAAC: LONDON
VOLCANO: EYJAFJALLAJOKULL 372020
PSN: N6338 W01938
AREA: ICELAND
SUMMIT ELEV: 1651M
ADVISORY NR: 2021/009
INFO SOURCE: ICELAND MET OFFICE
AVIATION COLOUR CODE: RED
ERUPTION DETAILS: EXERCISE VOLCEX21 - EYJAFJALLAJOKULL ERUPTION
OBS VA DTG: 16/0600Z
OBS VA CLD: SFC/FL200 N4550 W04415 - N6356 W01927 - N7353 E05847 -
N4025 E05735 - N4550 W04415 MOV SE 15KT FL200/350 N5343 W03145 -
N6355 W01932 - N7446 E05944 - N6250 E05925 - N5255 W02531 - N5343
W03145 MOV SE 40KT
FCST VA CLD +6 HR: 16/1200Z SFC/FL200 N4517 W04623 - N6356 W01924 -
N7750 E07729 - N3856 E06031 - N4517 W04623 FL200/350 N4839 W04904 -
N7025 E01017 - N7505 E07954 - N6139 E06549 - N4839 W04904
FCST VA CLD +12 HR: 16/1800Z SFC/FL200 N4010 E03817 - N4900 W04529 -
N7112 W00641 - N7617 E09941 - N4010 E03817 FL200/350 N4919 W05700 -
N7300 E03329 - N7732 E09626 - N5936 E09143 - N4919 W05700
FCST VA CLD +18 HR: 17/0000Z SFC/FL200 N4901 W05420 - N7235 E00056 -
N7558 E09708 - N3934 E05131 - N4901 W05420 FL200/350 N4933 W03842 -
N5436 W04127 - N7433 E03732 - N7935 E11430 - N6503 E10238 - N4933
W03842
RMK: EXERCISE VOLCEX21 - EYJAFJALLAJOKULL ERUPTION CONTINUES,
RECENTLY INCREASING TO APPROX 8KM
NXT ADVISORY: NO LATER THAN 20211116/1200Z

Edit: To be more specific I modified the example and the regex. As you can see I can get all the informations that are written into the fields called FCST VA CLD +6 HR: / FCST VA CLD +12 HR: / FCST VA CLD +18 HR:. Every field (FCST VA CLD +6 HR: / FCST VA CLD +12 HR: / FCST VA CLD +18 HR:) must be a match, and every match must be divided in different groups.

  • First group: it's the issue time (16/1200Z, 16/1800Z, 17/0000Z). There is only one issue time per field.
  • Second group: it's the layer (SFC/FL200, FL200/350). I can't know how many layers there will be reported but at least one layer will be available. If multiple layers are reported they are always referred to the same issue time (per field)
  • Third group: they're coordinates. A bunch of coordinates are always referred to one single layer.
match 1: 16/1200Z SFC/FL200 N4517 W04623 - N6356 W01924 - N7750 E07729 - N3856 E06031 - N4517 W04623 FL200/350 N4839 W04904 -N7025 E01017 - N7505 E07954 - N6139 E06549 - N4839 W04904
            1st group:  16/1200Z
            2nd group:      SFC/FL200
            3rd group:          N4517 W04623 - N6356 W01924 - N7750 E07729 - N3856 E06031 - N4517 W04623
            2nd group:      FL200/350
            3rd group:          N4839 W04904 - N7025 E01017 - N7505 E07954 - N6139 E06549 - N4839 W04904
    
match 2: 16/1800Z SFC/FL200 N4010 E03817 - N4900 W04529 - N7112 W00641 - N7617 E09941 - N4010 E03817 FL200/350 N4919 W05700 - N7300 E03329 - N7732 E09626 - N5936 E09143 - N4919 W05700
    1st group:  16/1800Z
    2nd group:      SFC/FL200
    3rd group:          N4010 E03817 - N4900 W04529 - N7112 W00641 - N7617 E09941 - N4010 E03817
    2nd group:      FL200/350
    3rd group:          N4919 W05700 - N7300 E03329 - N7732 E09626 - N5936 E09143 - N4919 W05700
    
match 3: 17/0000Z SFC/FL200 N4901 W05420 - N7235 E00056 - N7558 E09708 - N3934 E05131 - N4901 W05420 FL200/350 N4933 W03842 - N5436 W04127 - N7433 E03732 - N7935 E11430 - N6503 E10238 - N4933 W03842
    1st group:  17/0000Z
    2nd group:      SFC/FL200
    3rd group:          N4901 W05420 - N7235 E00056 -N7558 E09708 - N3934 E05131 - N4901 W05420
    2nd group:      FL200/350
    3rd group:          N4933 W03842 -N5436 W04127 - N7433 E03732 - N7935 E11430 - N6503 E10238 - N4933 W03842

My modified regex:

([\d]+/[\d\w]+\s)(([\w\d]+/[\w\d]+)\s((?:[\w]{1}[\d]{4}\s[\w]{1}[\d]{5}\s\-\s)+[\w]{1}[\d]{4}\s[\w]{1}[\d]{5}\s))+

The problem, as you can see here: https://regex101.com/r/VZG3IA/1

is that i cannot get the first layer (SFC/FL200) but only the second (FL200/350)

How can I correct my regex to get the data I want? And can it be written in a better way?

the expected output is something like this:

[('16/1200Z', ('SFC/FL200', 'N4517 W04623 - N6356 W01924 - N7750 E07729 - N3856 E06031 - N4517 W04623'), (FL200/350, N4839 W04904 - N7025 E01017 - N7505 E07954 - N6139 E06549 - N4839 W04904))]

[('16/1800Z', ('SFC/FL200', 'N4010 E03817 - N4900 W04529 - N7112 W00641 - N7617 E09941 - N4010 E03817'), (FL200/350, N4919 W05700 - N7300 E03329 - N7732 E09626 - N5936 E09143 - N4919 W05700))]

[('17/0000Z', ('SFC/FL200', 'N4901 W05420 - N7235 E00056 -N7558 E09708 - N3934 E05131 - N4901 W05420'), (FL200/350, N4933 W03842 -N5436 W04127 - N7433 E03732 - N7935 E11430 - N6503 E10238 - N4933 W03842))]

or even better a json file like this (not sure if it's well formatted):
{ "issue time":{
        "16/1200Z": {
            "layer" : {
                "SFC/FL200":{N4517 W04623 - N6356 W01924 - N7750 E07729 - N3856 E06031 - N4517 W04623}
                "FL200/350":{N4839 W04904 - N7025 E01017 - N7505 E07954 - N6139 E06549 - N4839 W04904}
                  }
                }
        }
}

{ "issue time":{
        "16/1800Z": {
            "layer" : {
                "SFC/FL200":{N4010 E03817 - N4900 W04529 - N7112 W00641 - N7617 E09941 - N4010 E03817}
                "FL200/350":{N4919 W05700 - N7300 E03329 - N7732 E09626 - N5936 E09143 - N4919 W05700}
                  }
                }
        }
}

{ "issue time":{
        "17/0000Z": {
            "layer" : {
                "N4901 W05420 - N7235 E00056 -N7558 E09708 - N3934 E05131 - N4901 W05420}
                "N4933 W03842 -N5436 W04127 - N7433 E03732 - N7935 E11430 - N6503 E10238 - N4933 W03842}
                }       
                }
        }
}
1 Answers

You can use

import regex

text = "VA ADVISORY\nSTATUS: EXER\nDTG: 20211116/0600Z\nVAAC: LONDON\nVOLCANO: EYJAFJALLAJOKULL 372020\nPSN: N6338 W01938\nAREA: ICELAND\nSUMMIT ELEV: 1651M\nADVISORY NR: 2021/009\nINFO SOURCE: ICELAND MET OFFICE\nAVIATION COLOUR CODE: RED\nERUPTION DETAILS: EXERCISE VOLCEX21 - EYJAFJALLAJOKULL ERUPTION\nOBS VA DTG: 16/0600Z\nOBS VA CLD: SFC/FL200 N4550 W04415 - N6356 W01927 - N7353 E05847 -\nN4025 E05735 - N4550 W04415 MOV SE 15KT FL200/350 N5343 W03145 -\nN6355 W01932 - N7446 E05944 - N6250 E05925 - N5255 W02531 - N5343\nW03145 MOV SE 40KT\nFCST VA CLD +6 HR: 16/1200Z SFC/FL200 N4517 W04623 - N6356 W01924 -\nN7750 E07729 - N3856 E06031 - N4517 W04623 FL200/350 N4839 W04904 -\nN7025 E01017 - N7505 E07954 - N6139 E06549 - N4839 W04904\nFCST VA CLD +12 HR: 16/1800Z SFC/FL200 N4010 E03817 - N4900 W04529 -\nN7112 W00641 - N7617 E09941 - N4010 E03817 FL200/350 N4919 W05700 -\nN7300 E03329 - N7732 E09626 - N5936 E09143 - N4919 W05700\nFCST VA CLD +18 HR: 17/0000Z SFC/FL200 N4901 W05420 - N7235 E00056 -\nN7558 E09708 - N3934 E05131 - N4901 W05420 FL200/350 N4933 W03842 -\nN5436 W04127 - N7433 E03732 - N7935 E11430 - N6503 E10238 - N4933\nW03842\nRMK: EXERCISE VOLCEX21 - EYJAFJALLAJOKULL ERUPTION CONTINUES,\nRECENTLY INCREASING TO APPROX 8KM\nNXT ADVISORY: NO LATER THAN 20211116/1200Z"

pattern = r'(?P<issuetime>\d+/\w+\s)(?:(?P<layer>\w+/\w+)\s(?P<coordinates>(?:\w\d{4}\s\w\d{5}\s-\s)+\w\d{4}\s\w\d{5}\s))+'

for m in [x.capturesdict() for x in regex.finditer(pattern, text)]:
    print(m)

See the Python demo online. Output:

{'issuetime': ['16/1200Z '], 'layer': ['SFC/FL200', 'FL200/350'], 'coordinates': ['N4517 W04623 - N6356 W01924 -\nN7750 E07729 - N3856 E06031 - N4517 W04623 ', 'N4839 W04904 -\nN7025 E01017 - N7505 E07954 - N6139 E06549 - N4839 W04904\n']}
{'issuetime': ['16/1800Z '], 'layer': ['SFC/FL200', 'FL200/350'], 'coordinates': ['N4010 E03817 - N4900 W04529 -\nN7112 W00641 - N7617 E09941 - N4010 E03817 ', 'N4919 W05700 -\nN7300 E03329 - N7732 E09626 - N5936 E09143 - N4919 W05700\n']}
{'issuetime': ['17/0000Z '], 'layer': ['SFC/FL200', 'FL200/350'], 'coordinates': ['N4901 W05420 - N7235 E00056 -\nN7558 E09708 - N3934 E05131 - N4901 W05420 ', 'N4933 W03842 -\nN5436 W04127 - N7433 E03732 - N7935 E11430 - N6503 E10238 - N4933\nW03842\n']}

The PyPi regex module (installed with pip install regex) can access all captures inside each capture group. The (?P<issuetime>\d+/\w+\s)(?:(?P<layer>\w+/\w+)\s(?P<coordinates>(?:\w\d{4}\s\w\d{5}\s-\s)+\w\d{4}\s\w\d{5}\s))+' regex also has named groups which makes it possible to use a match data object's capturesdict() on each match to get the named captures in a nice list.

Related