2

I am trying to get my head around the regex module in python. I tried to get my program to match the following pattern from a line of text that the user inputs:

a number between 8-13 "/" a number between 0-15

For example: 8/2, 11/13, 10/9, etc.

The pattern that I came up with was:

upstream = re.compile(r'[8-9|1[0-3][/][0-9|1[0-5]')

However, this regex works with mixed results:

Enter a slot/port : 8/2    
['8/2']                    # This is correct

Enter a slot/port : 1/0    
['1/0']                    # This should print the "else" statement

Enter a slot/port : 8/15
['8/1']                    # The output is incomplete

The problem seems to stem from the forward slash, but I am not sure. I do know that I need some assistance in solving this issue. If anyone can help me solve this, I would greatly appreciate it.

The complete script is below.

import re
pattern = re.compile(r'[8-9|1[0-3][/][0-9|1[0-5]')

upstream = input("Enter a slot/port : ")

if re.search((pattern), upstream):
    print(re.findall(pattern, upstream))
else:
    print("We have a problem")

Thanks in advance :)

python_noob
  • 107
  • 7
  • 1
    Check out [this](https://pythex.org) website which helps test regex patterns for python. – 0x263A Jul 19 '21 at 22:39
  • 1
    You need `(?<!\d)(?:[89]|1[0-3])/(?:[0-9]|1[0-5])(?!\d)` – Wiktor Stribiżew Jul 19 '21 at 22:39
  • Thanks, Wiktor! That works. Now I just have to continue reading the documentation to fully understand what is going on with the ? and !. :) – python_noob Jul 19 '21 at 23:12
  • If you think a '/' might interfere with user input, it would also be reasonable to add two `input()` calls, one for the slot, and then one for the port. Remembering to include a `/` is probably not as important to you as the two numbers. – jorf.brunning Jul 20 '21 at 02:32
  • 2
    Do you have to use regex? It's trivially easy to do this using `str.split()` and then parsing the resulting strings into integers. – Pranav Hosangadi Jul 20 '21 at 21:08
  • You are correct. I had a working module using str.split(), but I needed to make a module that would be able to take multiple entries at once, such as (8/0 9/15 11/12), as well as making sure the module would ignore errors or wrong responses. This is why I wanted to use regex so I could achieve this goad without writing too much additional code. – python_noob Jul 29 '21 at 13:25

2 Answers2

1

Your expression is not well-formed, as you utilized square brackets where round brackets must be. [8-9|1[0-3] and [0-9|1[0-5] both are bad patterns as [8-9 and [0-9 are not closed character classes.

Use

\b(?:[89]|1[0-3])/(?:[0-9]|1[0-5])\b

See regex proof.

EXPLANATION

--------------------------------------------------------------------------------
  \b                       the boundary between a word char (\w) and
                           something that is not a word char
--------------------------------------------------------------------------------
  (?:                      group, but do not capture:
--------------------------------------------------------------------------------
    [89]                     any character of: '8', '9'
--------------------------------------------------------------------------------
   |                        OR
--------------------------------------------------------------------------------
    1                        '1'
--------------------------------------------------------------------------------
    [0-3]                    any character of: '0' to '3'
--------------------------------------------------------------------------------
  )                        end of grouping
--------------------------------------------------------------------------------
  /                        '/'
--------------------------------------------------------------------------------
  (?:                      group, but do not capture:
--------------------------------------------------------------------------------
    [0-9]                    any character of: '0' to '9'
--------------------------------------------------------------------------------
   |                        OR
--------------------------------------------------------------------------------
    1                        '1'
--------------------------------------------------------------------------------
    [0-5]                    any character of: '0' to '5'
--------------------------------------------------------------------------------
  )                        end of grouping
--------------------------------------------------------------------------------
  \b                       the boundary between a word char (\w) and
                           something that is not a word char
Ryszard Czech
  • 18,032
  • 4
  • 24
  • 37
  • 1
    Thank you so much for this great great response! Sorry that I took so long to answer, I wasn't aware that the question was reopened. Using the boundary really made a huge difference, since I wanted to ensure that useless input such as c8/9 would be ignored. I have marked your response as the answer. – python_noob Jul 29 '21 at 13:21
1

The regex you are using requires 1's on both sides of the "/", use the "|" symbol to imply OR statements such that there is a choice of "a or "b", "a|b". This would give you a regex more in line with "[8-9]|1[0-3]" for before the "/" and "[0-9]|1[0-5]" after. So in total, when using "(regex)" to group parts you want to be expressed separately, you could end up with a regex more inline with "([8-9]|1[0-3])/([0-9]|1[0-5])".

Hope this was helpful!

mroussell
  • 17
  • 1
  • 6
  • Yes, it helped me a lot, thanks for responding. My problem was not understanding how to properly group the different subpatterns in order to get the desired result. – python_noob Jul 29 '21 at 13:22