import re


input='''R1# show ip interface brief
Interface              IP-Address      OK? Method Status                Protocol
--------------------------------------------------------------------------------
GigabitEthernet0/0     10.1.1.1        YES manual up                    up
GigabitEthernet0/1     172.16.10.1     YES DHCP   up                    down
GigabitEthernet0/2     unassigned      YES unset  administratively down down
GigabitEthernet0/3     192.168.20.1    YES manual down                  down
Loopback0              1.1.1.1         YES manual up                    up
Loopback10             unassigned      YES manual up                    up
Tunnel1                100.1.1.1       YES manual up                    down
Vlan100                10.100.1.1      YES manual down                  down
Serial0/0/0            172.30.1.2      YES manual up                    up
Serial0/0/1            172.30.2.2      YES manual up                    down
Port-channel1          192.168.50.1    YES manual up                    up'''

pattern=r'(\S+)\s+\S+\s+\S+\s+\S+\s+up+\s+\S+'

test=re.findall(pattern,input)
print(test)
#['GigabitEthernet0/0', 'GigabitEthernet0/1', 'Loopback0', 'Loopback10', 'Tunnel1', 'Serial0/0/0', 'Serial0/0/1', 'Port-channel1']




---

import re

## swap local port with remote port & change system name
text= '''user@switch> show lldp neighbors
Local Interface   Parent Interface   Chassis Id          Port info    System Name
xe-3/0/4.0        ae31.0             b0:c6:9a:63:80:40   xe-0/0/0.0   host.jnpr.net
xe-3/0/5.0        ae31.0             b0:c6:9a:63:80:40   xe-0/0/1.0   host.jnpr.net
xe-3/0/6.0        ae31.0             b0:c6:9a:63:80:40   xe-0/0/2.0   host.jnpr.net
xe-3/0/7.0        ae31.0             b0:c6:9a:63:80:40   xe-0/0/3.0   host.jnpr.net
xe-3/0/0.0        ae31.0             b0:c6:9a:63:80:40   xe-0/1/0.0   host.jnpr.net
xe-3/0/1.0        ae31.0             b0:c6:9a:63:80:40   xe-0/1/1.0   host.jnpr.net
xe-3/0/2.0        ae31.0             b0:c6:9a:63:80:40   xe-0/1/2.0   host.jnpr.net'''

s_text=text.split('\n')
#print(s_text)
print(f'Local Interface   Parent Interface   Chassis Id          Port info    System Name')
for x,y in enumerate(s_text,1):
   
    if x > 2 :
        ss=y.split()
        print(f'{ss[3]}\t{ss[1]}\t{ss[2]}\t{ss[0]}\tmohan.com')


#####
outputt
Local Interface Parent Interface Chassis Id Port info System Name xe-0/0/0.0 ae31.0 b0:c6:9a:63:80:40 xe-3/0/4.0 mohan.com xe-0/0/1.0 ae31.0 b0:c6:9a:63:80:40 xe-3/0/5.0 mohan.com xe-0/0/2.0 ae31.0 b0:c6:9a:63:80:40 xe-3/0/6.0 mohan.com xe-0/0/3.0 ae31.0 b0:c6:9a:63:80:40 xe-3/0/7.0 mohan.com xe-0/1/0.0 ae31.0 b0:c6:9a:63:80:40 xe-3/0/0.0 mohan.com xe-0/1/1.0 ae31.0 b0:c6:9a:63:80:40 xe-3/0/1.0 mohan.com xe-0/1/2.0 ae31.0 b0:c6:9a:63:80:40 xe-3/0/2.0 mohan.com


----

 with open('mohan.txt','w') as f:

    f.write('hello world \npython world \nlearning')

with open('mohan.txt','a') as f:
    f.write('\nappending line4')

with open('mohan.txt','r') as f:
    x=f.read()
    print(type(x)) #<class 'str'>
    print(x)
    #output
    '''
     hello world
     python world
     learning
     appending line4'''

with open('mohan.txt','r') as f:
    x=f.readlines()
    print(type(x)) #<class 'list'>
    print(x) #['hello world \n', 'python world \n', 'learning\n', 'appending line4']

---
import re

text = """python1 is a scripting language
python2 hadles data structures list,dict,etc
it has lots of python3 liberiers"""

print(text)

pattern=r'python\d'

print("######### regex match #########")
m=re.match(pattern,text)
print(m)   #<re.Match object; span=(0, 7), match='python1'>
print(type(m)) #<class 're.Match'>
print ('match output:',m.group())  #match output: python1
print ('match span:',m.span())  #match span: (0, 7)
print ('match start:',m.start()) #match start: 0
print ('match end:',m.end()) #match end: 7
print("##############################")


print("######### regex search #########")
s=re.search(pattern,text)
print(s)  #<re.Match object; span=(0, 7), match='python1'>
print(type(s)) #<class 're.Match'>
print ('match output:',s.group())  #match output: python1
print("##############################")

print("######### regex findall #########")
fa=re.findall(pattern,text)
print(fa) #['python1', 'python2', 'python3']
print(len(fa)) #3
print(type(fa))  #<class 'list'>
print ('match output:',fa[0])  #match output: python1
print ('match output:',fa[1])  #match output: python2
print ('match output:',fa[2])  #match output: python3
print("##############################")

print("######### regex finditer #########")
fi=re.finditer(pattern,text)
print(fi)  #<callable_iterator object at 0x000001EDEAEB9030>
print(type(fi)) #<class 'callable_iterator'>
for x in fi:
     print ('match output:',x)
     print ('match output:',x.group())
     '''
#output
      match output: <re.Match object; span=(0, 7), match='python1'>
      match output: python1
      match output: <re.Match object; span=(32, 39), match='python2'>
      match output: python2
      match output: <re.Match object; span=(92, 99), match='python3'>
      match output: python3
     '''
print("##############################")



##Name capturing group

import re text = "My IP is 192.168.100.11" p = r'(?P<oct1>\d{1,3})\.(?P<oct2>\d{1,3})\.(?P<oct3>\d{1,3})\.(?P<oct4>\d{1,3})' match = re.search(p, text) print(match.group("oct1")) # 192 print(match.group("oct2")) # 168 print(match.group("oct3")) # 100 print(match.group("oct4")) # 11


## look ahead & look beyond examples
1. Positive Lookahead (?=...)
Pattern: \d+(?=USD)
Text: 100USD
Regex: \d+ (?=USD)
Matches "100" because it is followed by "USD".
2. Positive Lookbehind (?<=...)
Pattern: (?<=USD)\d+
Text: USD100
Regex: (?<=USD) \d+
Matches "100" because it is preceded by "USD".
3. Negative Lookahead (?!...)
Pattern: \d+(?!USD)
Text: 200EUR
Regex: \d+ (?!USD)
Matches "200" because after digits we don’t see "USD".
4. Negative Lookbehind (?<!...)
Pattern: (?<!USD)\d+
Text: EUR300
Regex: (?<!USD) \d+
Matches "300" because before digits we don’t see "USD".

-----

import re


test= '''
this line has valid ip 192.168.100.11
this line has invalid 256.255.0.1
1.1.1.1
'''
pattern=r'\b((25[0-5]|2[0-4]\d|1\d\d|\d[1-9]?)\.){3}(25[0-5]|2[0-4]\d|1\d\d|\d[1-9]?)\b'
regex = re.compile(pattern)

print("Number of capturing groups:", regex.groups)

validate = re.findall(pattern,test)
print(validate[0])


## output will be
""""
Number of capturing groups: 3
('100.', '100', '11')
Group 1:Matches the whole repeated thing (BUT findall returns only the last repetition):
> '100.'  #((25[0-5]|2[0-4]\d|1\d\d|\d[1-9]?)\.)  > outer (())
Group 2:Matches the final occurrence of the 3 repeated octets
> 100 #(25[0-5]|2[0-4]\d|1\d\d|\d[1-9]?) > inner ()
Group 3:Matches the final octet: '11'
> 11 #(25[0-5]|2[0-4]\d|1\d\d|\d[1-9]?) > 2nd ()
"""
print(validate[0])
#('100.', '100', '11')
print(validate[1])
#('1.', '1', '1')
print(validate[2])
#IndexError: list index out of range  
#due to only 2 matches in the above input test file

#Solution: use below pattern
#escape capturing groups >> ?:
pattern=r'\b(?:(?:25[0-5]|2[0-4]\d|1\d\d|\d[1-9]?)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|\d[1-9]?)\b'



---

#Example-2

## re.multiline example

import re

text = """Hello World
Python is fun
Regex is powerful"""

pattern = r"^\w+"

matches = re.findall(pattern, text)
print(matches) #['Hello']
matches2 = re.findall(pattern, text, re.MULTILINE)
print(matches2) #['Hello', 'Python', 'Regex']


#Example-3


##pattern matching group ##

import re text = "this has ip address 190.168.0.25" match =r'\b(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[0-9]?\d)){3}\b' compiled = re.compile(match) print("Number of capturing groups:", compiled.groups)

test=re.findall(match,text)
for items in test: print(items)
if test: print("valid IP") else: print("invalid ip")

##output
Number of capturing groups: 3 ('190', '.25', '25') valid IP

explanation
Regex: (G1) (G2: (\. (G3) ){3}) Input: 190 . 168 . 0 . 25 Step 1: G1 = 190 Step 2: G2 = .168, G3 = 168 Step 3: G2 = .0, G3 = 0 Step 4: G2 = .25, G3 = 25 <-- last repetition stored


#Example-4

#same example with escape group (?:)
import re

text = "this has ip address 190.168.0.25"
match =r'\b(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[0-9]?\d)){3}\b'

compiled = re.compile(match)
print("Number of capturing groups:", compiled.groups)
test=re.findall(match,text)
for items in test:
    print(items)
if test:
    print("valid IP")
else:
    print("invalid ip")

## output
Number of capturing groups: 0 190.168.0.25 valid IP

#Example-5


## multiple line as input
import re text = """this has ip address 190.168.0.25 & 3.3.4.2 second line 1.1.1.1 and thirs line 33.33.33.33 """ match =r'\b(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[0-9]?\d)){3}\b' compiled = re.compile(match) print("Number of capturing groups:", compiled.groups) test=re.findall(match,text) for items in test: print(items) if test: print("valid IP") else: print("invalid ip")

##output
Number of capturing groups: 0
190.168.0.25
3.3.4.2
1.1.1.1
33.33.33.33
valid IP


#Example-6


### corner case
if input is - 300.3.3.3.3
above regexp match - 3.3.3.3

if you want to exclude that condition use lookahead & look beyond

match = r'(?<!\d)(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}(?!\d)'



#Example-7

## findall vs finditer
import re

text = """Interface              IP-Address      OK? Method Status                Protocol
GigabitEthernet0/0     192.168.1.1     YES manual up                    up
GigabitEthernet0/1     unassigned      YES unset administratively down  down
GigabitEthernet0/2     10.0.0.1        YES manual down                  down
GigabitEthernet0/3     172.16.0.1      YES manual administratively down down
FastEthernet0/4        unassigned      YES unset err-disabled           down
 """
match=r'^(?P<iface>\S+)\s+\S+\s+\S+\s+\S+\s+administratively\s+down'

#compiled = re.compile(match)
#print("Number of capturing groups:", compiled.groups)
test=re.finditer(match,text,re.MULTILINE)
print(f'Finditer result >>> {test}')
test2=re.findall(match,text,re.MULTILINE)
print(f'Findall result >>> {test2}')

##output
Finditer result >>> <callable_iterator object at 0x7e77a17f5e70> Findall result >>> ['GigabitEthernet0/1', 'GigabitEthernet0/3']