What does "?:" mean in a Python regular expression? - python

Below is the Python regular expression. What does the ?: mean in it? What does the expression do overall? How does it match a MAC address such as "00:07:32:12:ac:de:ef"?
re.compile(([\dA-Fa-f]{2}(?:[:-][\dA-Fa-f]{2}){5}), string)

It (?:...) means a set of non-capturing grouping parentheses.
Normally, when you write (...) in a regex, it 'captures' the matched material. When you use the non-capturing version, it doesn't capture.
You can get at the various parts matched by the regex using the methods in the re package after the regex matches against a particular string.
How does this regular expression match MAC address "00:07:32:12:ac:de:ef"?
That's a different question from what you initially asked. However, the regex part is:
([\dA-Fa-f]{2}(?:[:-][\dA-Fa-f]{2}){5})
The outer most pair of parentheses are capturing parentheses; what they surround will be available when you use the regex against a string successfully.
The [\dA-Fa-f]{2} part matches a digit (\d) or the hexadecimal digits A-Fa-f], in a pair {2}, followed by a non-capturing grouping where the matched material is a colon or dash (: or -), followed by another pair of hex digits, with the whole repeated exactly 5 times.
p = re.compile(([\dA-Fa-f]{2}(?:[:-][\dA-Fa-f]{2}){5}))
m = p.match("00:07:32:12:ac:de:ef")
if m:
m.group(1)
The last line should print the string "00:07:32:12:ac:de" because that is the first set of 6 pairs of hex digits (out of the seven pairs in total in the string). In fact, the outer grouping parentheses are redundant and if omitted, m.group(0) would work (it works even with them). If you need to match 7 pairs, then you change the 5 into a 6. If you need to reject them, then you'd put anchors into the regex:
p = re.compile(^([\dA-Fa-f]{2}(?:[:-][\dA-Fa-f]{2}){5})$)
The caret ^ matches the start of string; the dollar $ matches the end of string. With the 5, that would not match your sample string. With 6 in place of 5, it would match your string.

Using ?: as in (?:...) makes the group non-capturing during replace. During find it does'nt make any sense.
Your RegEx means
r"""
( # Match the regular expression below and capture its match into backreference number 1
[\dA-Fa-f] # Match a single character present in the list below
# A single digit 0..9
# A character in the range between “A” and “F”
# A character in the range between “a” and “f”
{2} # Exactly 2 times
(?: # Match the regular expression below
[:-] # Match a single character present in the list below
# The character “:”
# The character “-”
[\dA-Fa-f] # Match a single character present in the list below
# A single digit 0..9
# A character in the range between “A” and “F”
# A character in the range between “a” and “f”
{2} # Exactly 2 times
){5} # Exactly 5 times
)
"""
Hope this helps.

It does not change the search process. But it affects the retrieval of the group after the match has been found.
For example:
Text:
text = 'John Wick'
pattern to find:
regex = re.compile(r'John(?:\sWick)') # here we are looking for 'John' and also for a group (space + Wick). the ?: makes this group unretrievable.
When we print the match - nothing changes:
<re.Match object; span=(0, 9), match='John Wick'>
But if you try to manually address the group with (?:) syntax:
res = regex.finditer(text)
for i in res:
print(i)
print(i.group(1)) # here we are trying to retrieve (?:\sWick) group
it gives us an error:
IndexError: no such group
Also, look:
Python docs:
(?:...)
A non-capturing version of regular parentheses. Matches whatever regular expression is inside the parentheses, but the substring matched by the group cannot be retrieved after performing a match or referenced later in the pattern.
the link to the re page in docs:
https://docs.python.org/3/library/re.html

(?:...) means a non cature group. The group will not be captured.

Related

Regex to find N characters between underscore and period

I have a filename having numerals like test_20200331_2020041612345678.csv.
So I just want to read only first 8 characters from the number between last underscore and .csv using a regex.
For e.g: From the file name test_20200331_2020041612345678.csv --> i want to read only 20200416 using regex.
Regex tried: (?<=_)(\d+)(?=\.)
But it is returning the full number between underscore and period i.e 2020041612345678
Also, when tried quantifier like (?<=_)(\d{8})(?=\.) its not matching with any string
The (?<=_)(\d{8})(?=\.) does not work because the (?=\.) positive lookahead requires the presence of a . char immediately to the right of the current location, i.e. right after the eigth digit, but there are more digits in between.
You may add \d* before \. to match any amount of digits after the required 8 digits, use
(?<=_)\d{8}(?=\d*\.)
Or, with a capturing group, you do not even need lookarounds (just make sure you access Group 1 when a match is obtained):
_(\d{8})\d*\.
See the regex demo
Python demo:
import re
s = "test_20200331_2020041612345678.csv"
m = re.search(r"(?<=_)\d{8}(?=\d*\.)", s)
# m = re.search(r"_(\d{8})\d*\.", s) # capturing group approach
if m:
print(m.group()) # => 20200416
# print(m.group(1)) # capturing group approach

How to get all matching iterations for a capture group

I made this regular expression and use it with re.findall():
SELECT.*{(?:\[([a-zA-Z0-9 ]*)\]\.\[([a-zA-Z0-9 ]*)\]\.\[([a-zA-Z0-9 ]*)\][,]{0,1}){1,}}.*
to match these lists of strings:
["dimSales","Product Title","All"],
["test","Product Title","All"]
in this haystack:
SELECT NON EMPTY Hierarchize({DrilldownLevel({[dimSales].[Product Title].[All],[test].[Product Title].[All]},,,INCLUDE_CALC_MEMBERS)}) DIMENSION PROPERTIES PARENT_UNIQUE_NAME,HIERARCHY_UNIQUE_NAME ON COLUMNS FROM [Model] CELL PROPERTIES VALUE, FORMAT_STRING, LANGUAGE, BACK_COLOR, FORE_COLOR, FONT_FLAGS
my regex only matches the last iteration of the outer capturing group
["test","Product Title","All"]
what do I need to change, so re.findall() returns all iterations. Not just the last iteration of the outer capturing group?
string = "SELECT NON EMPTY Hierarchize({DrilldownLevel({[dimSales].[Product Title].[All],[test].[Product Title].[All]},,,INCLUDE_CALC_MEMBERS)}) DIMENSION PROPERTIES PARENT_UNIQUE_NAME,HIERARCHY_UNIQUE_NAME ON COLUMNS FROM [Model] CELL PROPERTIES VALUE, FORMAT_STRING, LANGUAGE, BACK_COLOR, FORE_COLOR, FONT_FLAGS"
print re.findall(r"(?:SELECT .+\({|,)\[([\w ]+)\]\.\[([\w ]+)\]\.\[([\w ]+)\](?=[^}]*})", string)
Output:
[('dimSales', 'Product Title', 'All'), ('test', 'Product Title', 'All')]
Explanation:
(?:SELECT .+\({|,) # non capture group, match SELECT folowed by 1 or more any character then ({ OR a comma
\[([\w ]+)\] # group 1, 1 or more word character or space inside square brackets
\. # a dot
\[([\w ]+)\] # group 2, 1 or more word character or space inside square brackets
\. # a dot
\[([\w ]+)\] # group 3, 1 or more word character or space inside square brackets
(?=[^}]*}) # positive lookahead, make sure we have after a close curly bracket not preceeded by another curly bracket
What about this regex:
(\[\"[^\"]*\",\"[^\"]*\",\"[^\"]*\"\],\s*\[\"[^\"]*\",\"[^\"]*\",\"[^\"]*\"\])
demo:
https://regex101.com/r/LaddaK/2/
Explanations:
parenthesis () to have your capturing group, can be removed if not necessary
\[\"[^\"]*\",\"[^\"]*\",\"[^\"]*\"\] to match an open bracket literally followed by a double quote, 0 to N non double quote characters ([^\"]*) followed by a double quote and a comma. You might have to surround all commas by \s* if you have want to accept space characters around them.
you repeat another 2 times the pattern \"[^\"]*\" to match the first 3 words surround in brackets (you might have to adapt into \w* depending on your exact constraints on the strings.
you repeat the whole block[\"[^\"]*\",\"[^\"]*\",\"[^\"]*\"\] after a ,\s* to accept the whole pattern made of 2 blocks of brackets.
Notes:
You might want to surround your regex with anchors (^ and $)
I don't know exactly your constraints but if you want to analyse some JSON or parse any other format with infinite nested patterns repeating themselves (ex: fractals) you should not use regex.
EDIT after change of requirements:
import re
inputStr = '[dimSales,Product Title,All], [test,Product Title,All]'
print(re.findall(r'\[(?:[a-zA-Z0-9 ]*)(?:,[a-zA-Z0-9 ]*)*\]', inputStr))
output:
['[dimSales,Product Title,All]', '[test,Product Title,All]']

Check in python if self designed pattern matches

I have a pattern which looks like:
abc*_def(##)
and i want to look if this matches for some strings.
E.x. it matches for:
abc1_def23
abc10_def99
but does not match for:
abc9_def9
So the * stands for a number which can have one or more digits.
The # stands for a number with one digit
I want the value in the parenthesis as result
What would be the easiest and simplest solution for this problem?
Replace the * and # through regex expression and then look if they match?
Like this:
pattern = pattern.replace('*', '[0-9]*')
pattern = pattern.replace('#', '[0-9]')
pattern = '^' + pattern + '$'
Or program it myself?
Based on your requirements, I would go for a regex for the simple reason it's already available and tested, so it's easiest as you were asking.
The only "complicated" thing in your requirements is avoiding after def the same digit you have after abc.
This can be done with a negative backreference. The regex you can use is:
\babc(\d+)_def((?!\1)\d{1,2})\b
\b captures word boundaries; if you enclose your regex between two \b
you will restrict your search to words, i.e. text delimited by space,
punctuations etc
abc captures the string abc
\d+ captures one or more digits; if there is an upper limit to the number of digits you want, it has to be \d{1,MAX} where MAX is your maximum number of digits; anyway \d stands for a digit and + indicates 1 or more repetitions
(\d+) is a group: the use of parenthesis defines \d+ as something you want to "remember" inside your regex; it's somehow similar to defining a variable; in this case, (\d+) is your first group since you defined no other groups before it (i.e. to its left)
_def captures the string _def
(?!\1) is the part where you say "I don't want to repeat the first group after _def. \1 represents the first group, while (?!whatever) is a check that results positive is what follows the current position is NOT (the negation is given by !) whatever you want to negate.
Live demo here.
I had the hardest time getting this to work. The trick was the $
#!python2
import re
yourlist = ['abc1_def23', 'abc10_def99', 'abc9_def9', 'abc955_def9', 'abc_def9', 'abc9_def9288', 'abc49_def9234']
for item in yourlist:
if re.search(r'abc[0-9]+_def[0-9][0-9]$', item):
print item, 'is a match'
You could match your pattern like:
abc\d+_def(\d{2})
abc Match literally
\d+ Match 1 or more digits
_ Match underscore
def - Match literally
( Capturing group (Your 2 digits will be in this group)
\d{2} Match 2 digits
) Close capturing group
Then you could for example use search to check for a match and use .group(1) to get the digits between parenthesis.
Demo Python
You could also add word boundaries:
\babc\d+_def(\d{2})\b

Regular expression not contains a specific patterns python

I'm working with regular expression on python then I've the followings string that I need to parse some like
XCT_GRUPO_INVESTIGACION_F1.sql
XCT_GRUPO_INVESTIGACION_F2.sql
XCT_GRUPO_INVESTIGACION.sql
XCS_GRUPO_INVESTIGACION.sql
The I need to parse all the string that has ??T, but the string not must containt somthing like F1,F34,constrains and others
So I've the following pattern
([a-zA-Z][a-zA-Z][tT]_([a-zA-Z]).*.(sql|SQL)$)
[a-zA-Z][a-zA-Z][tT]_ = check the first and second value could be whatever but I need to be followed by t_ or T_
([a-zA-Z]).* = any value a-z and A-Z any times
(sql|SQL)$ = must be end with sql or SQL
I get something like
ICT_GRUPO_INVESTIGACION_F1.sql
ICT_GRUPO_INVESTIGACION_F2.sql
ICT_GRUPO_INVESTIGACION.sql
But this contains F1,F?,constrains and others
how can I say to the regular expression that in the expression ([a-zA-Z]).* no contains f1 | f? | others_expresion_that_Iwanna
This regular expression should work:
([a-zA-Z][a-zA-Z][tT]_(?:(?!_F[0-9]).)*?\.(sql|SQL))
You may put any number of unwanted combinations here (?!_F[0-9]|other_expression|...)
There are following parts in the regular expression:
[a-zA-Z] #match any letter
[a-zA-Z] #match any letter
[tT]_ #match 't_' or 'T_'
(?: #start non-capturing group
(?!_F[0-9]) #negative lookahead, asserts that what immediately
#follows the current position in the string is not _f[0-9]
. #match any single character
)*? #end group, repeat it multiple times but as few as possible
\. #match period character
(sql|SQL) #match 'sql' or 'SQL'
You could find additional information here, here and here

Match only the string that has strings after last underscore

I am trying to match string with underscores, throughout the string there are underscores but I want to match the strings that that has strings after the last underscore: Let me provide an example:
s = "hello_world"
s1 = "hello_world_foo"
s2 = "hello_world_foo_boo"
In my case I only want to capture s1 and s2.
I started with following, but can't really figure how I would do the match to capture strings that has strings after hello_world's underscore.
rgx = re.compile(ur'(?P<firstpart>\w+)[_]+(?P<secondpart>\w+)$', re.I | re.U)
Try this:
reobj = re.compile("^(?P<firstpart>[a-z]+)_(?P<secondpart>[a-z]+)_(?P<lastpart>.*?)$", re.IGNORECASE)
result = reobj.findall(subject)
Regex Explanation
^(?P<firstpart>[a-z]+)_(?P<secondpart>[a-z]+)_(?P<lastpart>.*?)$
Options: case insensitive
Assert position at the beginning of the string «^»
Match the regular expression below and capture its match into backreference with name “firstpart” «(?P<firstpart>[a-z]+)»
Match a single character in the range between “a” and “z” «[a-z]+»
Between one and unlimited times, as many times as possible, giving back as needed (greedy) «+»
Match the character “_” literally «_»
Match the regular expression below and capture its match into backreference with name “secondpart” «(?P<secondpart>[a-z]+)»
Match a single character in the range between “a” and “z” «[a-z]+»
Between one and unlimited times, as many times as possible, giving back as needed (greedy) «+»
Match the character “_” literally «_»
Match the regular expression below and capture its match into backreference with name “lastpart” «(?P<lastpart>.*?)»
Match any single character that is not a line break character «.*?»
Between zero and unlimited times, as few times as possible, expanding as needed (lazy) «*?»
Assert position at the end of the string (or before the line break at the end of the string, if any) «$»
If I understand what you are asking for (you want to match string with more than one underscore and following text)
rgx = re.compile(ur'(?P<firstpart>\w+)[_]+(?P<secondpart>\w+)_[^_]+$', re.I | re.U)

Categories