Parse string with tag that position might reverse sometimes

Viewed 47

I'm trying to parse a string from log communicating with network which will be like

2019 Jun 30 15:40:17.561 NETWORK_MESSAGE
Direction = UE_TO_NETWORK
From: <1106994972>
To: <3626301680>

and here is my code:

import re
log = '2019 Jun 30 15:40:17.561 NETWORK_MESSAGE\r\nDirection = UE_TO_NETWORK\r\nFrom: <1106994972>\r\nTo: <3626301680>\r\n'
PATTERN = re.compile(
    '(?P<time>\d{2}:\d{2}:\d{2}.\d{3}).*'  # Time
    'Direction = (?P<Direction>\S+).*'     # Direction
    'From: <(?P<From>\S+)>.*'              # from
    'To: <(?P<To>\S+)>',                   # to
    re.DOTALL)
results = PATTERN.search(log)
print(results.group('From'))

However, I just found sometimes there will be reversed position between "From" and "To", just like the following.

2019 Jun 30 15:40:16.548 NETWORK_MESSAGE
Direction = NETWORK_TO_UE
To: <3626301680>
From: <1106994972>

Is it possible I can solve this with only one pattern?

2 Answers

Here is a solution that uses (From|To) to match either From or To and then explicitly checks which of the two places matched From:

import re
log1 = '2019 Jun 30 15:40:17.561 NETWORK_MESSAGE\r\nDirection = UE_TO_NETWORK\r\nFrom: <1106994972>\r\nTo: <3626301680>\r\n'
log2 = '2019 Jun 30 15:40:17.561 NETWORK_MESSAGE\r\nDirection = UE_TO_NETWORK\r\nTo: <3626301680>\r\nFrom: <1106994972>\r\n'


PATTERN = re.compile(
    '(?P<time>\d{2}:\d{2}:\d{2}.\d{3}).*'  # Time
    'Direction = (?P<Direction>\S+).*'     # Direction
    '(?P<tag1>From|To): <(?P<val1>\S+)>.*' # from or to
    '(?P<tag2>From|To): <(?P<val2>\S+)>',  # from or to
    re.DOTALL)
for log in [log1, log2]:
    results = PATTERN.search(log)
    if results.group('tag1') == 'From':
        print(results.group('val1'))
    elif results.group('tag2') == 'From':
        print(results.group('val2'))

This matches your line but does not make sure there is exactly on From and one To. I also considered this pattern

PATTERN = re.compile(
    '(?P<time>\d{2}:\d{2}:\d{2}.\d{3}).*'  # Time
    'Direction = (?P<Direction>\S+).*'     # Direction
    '(?P<FromTo>(?P<tag1>From|To): <(?P<val1>\S+)>.*){2}', # from or to
    re.DOTALL)

but this will only capture the last match in From and To (according to the docs "If a group is contained in a part of the pattern that matched multiple times, the last match is returned."). So if the two fields appear in the wrong order then you will not be able to get the value for From.

If things get more complicated you may have more readable code by using more than one pattern.

log1 = "2019 Jun 30 15:40:17.561 NETWORK_MESSAGE\r\nDirection = UE_TO_NETWORK\r\nFrom: <1106994972>\r\nTo: <3626301680>\r\n"
log2 = "2019 Jun 30 15:40:16.548 NETWORK_MESSAGE\r\nDirection = NETWORK_TO_UE\r\nTo: <3626301680>\r\nFrom: <1106994972>\r\n"

PATTERN = re.compile(
    '(?P<time>\d{2}:\d{2}:\d{2}.\d{3}).*'  # Time
    'Direction = (?P<Direction>\S+).*'     # Direction
    '(From|To): <(?P<X>\S+)>.*'             
    '(To|From): <(?P<Y>\S+)>',                 
    re.DOTALL)

print(re.findall(PATTERN, log1))
print(re.findall(PATTERN, log2))
Related