Regular expression to match alphanumeric string - python

If string "x" contains any letter or number, print that string.
How to do that using regular expressions?
The code below is wrong
if re.search('^[A-Z]?[a-z]?[0-9]?', i):
print i

re — Regular expression operations
This question is actually rather tricky. Unfortunately \w includes _ and [a-z] solutions assume a 26-letter alphabet. With the below solution please read the pydoc where it talks about LOCALE and UNICODE.
"[^_\\W]"
Note that since you are only testing for existence, no quantifiers need to be used -- and in fact, using quantifiers that may match 0 times will returns false positives.

You want
if re.search('[A-Za-z0-9]+', i):
print i

I suggest that you check out RegexBuddy. It can explain regexes well.

[A-Z]?[a-z]?[0-9]? matches an optional upper case letter, followed by an optional lower case letter, followed by an optional digit. So, it also matches an empty string. What you're looking for is this: [a-zA-Z0-9] which will match a single digit, lower- or upper case letter.
And if you need to check for letter (and digits) outside of the ascii range, use this if your regex flavour supports it: [\p{L}\p{N}]. Where \p{L} matches any letter and \p{N} any number.

don't need regex.
>>> a="abc123"
>>> if True in map(str.isdigit,list(a)):
... print a
...
abc123
>>> if True in map(str.isalpha,list(a)):
... print a
...
abc123
>>> a="###%$#%#^!"
>>> if True in map(str.isdigit,list(a)):
... print a
...
>>> if True in map(str.isalpha,list(a)):
... print a
...

Related

Regex that checks if string only contains numbers and special characters

I am trying to make an if function that checks if a string contains only a mixture of numbers and any special characters. For example:
Input: "Hello"
>>> False
Input: "$34&#!5^"
>>> True
Input: "Hello34#&%"
>>> False
I'm new to Regex and I'm not sure how to write the Regex for this. I know checking for special characters is r'^[_\W]+$' , and isdigit() can be used to check numbers only, but how do I combine both? I'm confused on how to combine [0-9] and the other symbols to write a Regex for this.
You can use ^[\W0-9_]+$ to match all non-word characters, _, and 0-9.

Python Three Letter Acronyms

I am trying to check if a certain string contains an acronym using regex.
my current regex:
re.search(r'\b[A-Z]{3}', string)
currently it outputs true to USA, NYCs, and NSFW but it should not say true on NSFW because it is a four letter acronym, not three.
How can I readjust the regex to make it not accept NSFW, but still accept NYCs
EDIT: it should also accept NYC,
A negative lookahead assertion: (?!pattern)
re.search(r'\b[A-Z]{3}(?![A-Z])',string)
This requires the triple capital pattern to never be followed by another capital letter, while it doesn't imply other restrictions, like the pattern necessarily be followed by something.
Think "Not followed by P" vs "Followed by not P"
Try:
filter(re.compile(r'\b[A-Z]{3}(?![A-Z])').search, ['.ANS', 'ANSs', 'AANS', 'ANS.'])
>>> import re
>>> rexp = r'(?:\b)([A-Z]{3})(?:$|[^A-Z])'
>>> re.search(rexp, 'USA').groups()
('USA',)
>>> re.search(rexp, 'NSFW') is None
True
>>> re.search(rexp, 'aUSA') is None
True
>>> re.search(rexp, 'NSF,').groups()
('NSF',)
You can use the ? to mean a character is optional, {0,1} would be equivalent.
You can put whatever characters you want to match inside the square brackets [ ] it will match any one of those 0 or 1 times so NYC. or WINs or FOO, will match.
Add the $ to the end to specify no more characters after the match are allowed
re.search(r'\b[A-Z]{3}[s,.]?$', string)

Find ISBN with regex in Python

If have a text (actually lots of texts), where somewhere is one ISBN inside, and I have to find it.
I know: my ISBN-13 will start with "978" followed by 10 digits.
I don't kow: how many '-' (minus) there are and if they are at the correct place.
My code will only find me the ISBN without any Minus:
regex=r'978[0-9]{10}'
pattern = re.compile(regex, re.UNICODE)
for match in pattern.findall(mytext):
print(match)
But how can I find ISBN like these:
978-123-456-789-0
978-1234-567890
9781234567890
etc...
Is this possible with one regex-pattern?
Thanks!
This matches 10 digits and allows one optional hyphen before each:
regex = r'978(?:-?\d){10}'
Since you can't have 2 consecutive hyphens, and it must end with a digit:
r'978(-?\d){10}'
... allowing for a hyphen right after then 978, mandating a digit after every hyphen (does not end in a hyphen), and allowing for consecutive digits by making each hyphen optional.
I would add \b before the 978 and after then {10}, to make sure the ISBN's are well separated from surrounding text.
Also, I would add ?: right after the opening parenthesis, to make those non-capturing (slightly better performance, and also more expressive), making it:
r'\b978(?:-?\d){10}\b'
What about adding the - char in the pattern for the regex? This way, it will look for any combination of (number or -)x10 times.
regex=r'978[0-9\-]{10}'
Although it may be better to use
regex=r'978[0-9\-]+'
because otherwise if we use {10} and some - are found, not all the digits will be found.
Test
>>> import re
>>> regex=r'978[0-9\-]+'
>>> pattern = re.compile(regex, re.UNICODE)
>>> mytext="978-123-456-789-0"
>>> for match in pattern.findall(mytext):
... print(match)
...
978-123-456-789-0
>>> mytext="978-1234-567890"
>>> for match in pattern.findall(mytext):
... print(match)
...
978-1234-567890
>>> mytext="9781234567890"
>>> for match in pattern.findall(mytext):
... print(match)
...
9781234567890
>>>
You can try to match every digits and - characters. In that case you can't know how many characters find however:
regex=r'978[\d\-]+\d'
pattern = re.compile(regex, re.UNICODE)
for match in pattern.findall(mytext):
print(match)
If your ISBN is stucked between other digits or hyphens, you'll have some problems, but if it's clearly seperated, no worries :)
EDIT: According to the first comment, you can add an extra \d at the end of the regex (I've updated my code just below) because you know that an ISBN ends with a digit.
The simplest way should be
regex=r'978[-0-9]{10,15}'
which will accept them.
If someone is still looking : ISBN Detail and Contraints
Easy one regex = r'^(978-?|979-?)?\d(-?\d){9}$'
Strong one isbnRegex = r'^(978-?|979-?)?\d{1,5}-?\d{1,7}-?\d{1,6}-?\d{1,3}$' and include length check of 10 and 13 after removing hypen (Note : Also add substring check for length = 13 ie. only for 978 or 979, Some edge case still need to be checked)

using reg exp to check if test string is of a fixed format

I want to make sure using regex that a string is of the format- "999.999-A9-Won" and without any white spaces or tabs or newline characters.
There may be 2 or 3 numbers in the range 0 - 9.
Followed by a period '.'
Again followed by 2 or 3 numbers in the range 0 - 9
Followed by a hyphen, character 'A' and a number between 0 - 9 .
This can be followed by anything.
Example: 87.98-A8-abcdef
The code I have come up until now is:
testString = "87.98-A1-help"
regCompiled = re.compile('^[0-9][0-9][.][0-9][0-9][-A][0-9][-]*');
checkMatch = re.match(regCompiled, testString);
if checkMatch:
print ("FOUND")
else:
print("Not Found")
This doesn't seem to work. I'm not sure what I'm missing and also the problem here is I'm not checking for white spaces, tabs and new line characters and also hard-coded the number for integers before and after decimal.
With {m,n} you can specify the number of times a pattern can repeat, and the \d character class matches all digits. The \S character class matches anything that is not whitespace. Using these your regular expression can be simplified to:
re.compile(r'\d{2,3}\.\d{2,3}-A\d-\S*\Z')
Note also the \Z anchor, making the \S* expression match all the way to the end of the string. No whitespace (newlines, tabs, etc.) are allowed here. If you combine this with the .match() method you assure that all characters in your tested string conform to the pattern, nothing more, nothing less. See search() vs. match() for more information on .match().
A small demonstration:
>>> import re
>>> pattern = re.compile(r'\d{2,3}\.\d{2,3}-A\d-\S*\Z')
>>> pattern.match('87.98-A1-help')
<_sre.SRE_Match object at 0x1026905e0>
>>> pattern.match('123.45-A6-no whitespace allowed')
>>> pattern.match('123.45-A6-everything_else_is_allowed')
<_sre.SRE_Match object at 0x1026905e0>
Let's look at your regular expression. If you want:
"2 or 3 numbers in the range 0 - 9"
then you can't start your regular expression with '^[0-9][0-9][.] because that will only match strings with exactly two integers at the beginning. A second issue with your regex is at the end: [0-9][-]* - if you wish to match anything at the end of the string then you need to finish your regular expression with .* instead. Edit: see Martijn Pieters's answer regarding the whitespace in the regular expressions.
Here is an updated regular expression:
testString = "87.98-A1-help"
regCompiled = re.compile('^[0-9]{2,3}\.[0-9]{2,3}-A[0-9]-.*');
checkMatch = re.match(regCompiled, testString);
if checkMatch:
print ("FOUND")
else:
print("Not Found")
Not everything needs to be enclosed inside [ and ], in particular when you know the character(s) that you wish to match (such as the part -A). Furthermore:
the notation {m,n} means: match at least m times and at most n times, and
to explicitly match a dot, you need to escape it: that's why there is \. in the regular expression above.

Regular expression for repeating sequence

I'd like to match three-character sequences of letters (only letters 'a', 'b', 'c' are allowed) separated by comma (last group is not ended with comma).
Examples:
abc,bca,cbb
ccc,abc,aab,baa
bcb
I have written following regular expression:
re.match('([abc][abc][abc],)+', "abc,defx,df")
However it doesn't work correctly, because for above example:
>>> print bool(re.match('([abc][abc][abc],)+', "abc,defx,df")) # defx in second group
True
>>> print bool(re.match('([abc][abc][abc],)+', "axc,defx,df")) # 'x' in first group
False
It seems only to check first group of three letters but it ignores the rest. How to write this regular expression correctly?
Try following regex:
^[abc]{3}(,[abc]{3})*$
^...$ from the start till the end of the string
[...] one of the given character
...{3} three time of the phrase before
(...)* 0 till n times of the characters in the brackets
What you're asking it to find with your regex is "at least one triple of letters a, b, c" - that's what "+" gives you. Whatever follows after that doesn't really matter to the regex. You might want to include "$", which means "end of the line", to be sure that the line must all consist of allowed triples. However in the current form your regex would also demand that the last triple ends in a comma, so you should explicitly code that it's not so.
Try this:
re.match('([abc][abc][abc],)*([abc][abc][abc])$'
This finds any number of allowed triples followed by a comma (maybe zero), then a triple without a comma, then the end of the line.
Edit: including the "^" (start of string) symbol is not necessary, because the match method already checks for a match only at the beginning of the string.
The obligatory "you don't need a regex" solution:
all(letter in 'abc,' for letter in data) and all(len(item) == 3 for item in data.split(','))
You need to iterate over sequence of found values.
data_string = "abc,bca,df"
imatch = re.finditer(r'(?P<value>[abc]{3})(,|$)', data_string)
for match in imatch:
print match.group('value')
So the regex to check if the string matches pattern will be
data_string = "abc,bca,df"
match = re.match(r'^([abc]{3}(,|$))+', data_string)
if match:
print "data string is correct"
Your result is not surprising since the regular expression
([abc][abc][abc],)+
tries to match a string containing three characters of [abc] followed by a comma one ore more times anywhere in the string. So the most important part is to make sure that there is nothing more in the string - as scessor suggests with adding ^ (start of string) and $ (end of string) to the regular expression.
An alternative without using regex (albeit a brute force way):
>>> def matcher(x):
total = ["".join(p) for p in itertools.product(('a','b','c'),repeat=3)]
for i in x.split(','):
if i not in total:
return False
return True
>>> matcher("abc,bca,aaa")
True
>>> matcher("abc,bca,xyz")
False
>>> matcher("abc,aaa,bb")
False
If your aim is to validate a string as being composed of triplet of letters a,b,and c:
for ss in ("abc,bbc,abb,baa,bbb",
"acc",
"abc,bbc,abb,bXa,bbb",
"abc,bbc,ab,baa,bbb"):
print ss,' ',bool(re.match('([abc]{3},?)+\Z',ss))
result
abc,bbc,abb,baa,bbb True
acc True
abc,bbc,abb,bXa,bbb False
abc,bbc,ab,baa,bbb False
\Z means: the end of the string. Its presence obliges the match to be until the very end of the string
By the way, I like the form of Sonya too, in a way it is clearer:
bool(re.match('([abc]{3},)*[abc]{3}\Z',ss))
To just repeat a sequence of patterns, you need to use a non-capturing group, a (?:...) like contruct, and apply a quantifier right after the closing parenthesis. The question mark and the colon after the opening parenthesis are the syntax that creates a non-capturing group (SO post).
For example:
(?:abc)+ matches strings like abc, abcabc, abcabcabc, etc.
(?:\d+\.){3} matches strings like 1.12.2., 000.00000.0., etc.
Here, you can use
^[abc]{3}(?:,[abc]{3})*$
^^
Note that using a capturing group is fraught with unwelcome effects in a lot of Python regex methods. See a classical issue described at re.findall behaves weird post, for example, where re.findall and all other regex methods using this function behind the scenes only return captured substrings if there is a capturing group in the pattern.
In Pandas, it is also important to use non-capturing groups when you just need to group a pattern sequence: Series.str.contains will complain that this pattern has match groups. To actually get the groups, use str.extract. and
the Series.str.extract, Series.str.extractall and Series.str.findall will behave as re.findall.

Categories

Resources