Python Regex 4 letter word with min 1 capital letter

Viewed 473

I am trying to find a regex-solution for finding all words with max 4. letters and at least 1 capital letter:

Examples of positive matches:

A, Ab, AbC, ABC, Abcd, bACD, aBCd, abcD

Examples of negative matches:

abcd

Do you know if this is possible with regex?

I started off with something like this:

match = re.findall(r"[a-zA-Z]{1,4}", string) 

but how do I eliminate the case of only small letters?

EDIT: I would like to find these patterns within a string of words such as this:

"in this sentence, we want to find Abcd and Hi but not abcd and not abcdefgHI"

Result: ["Abcd", "Hi"]

3 Answers

We can use a lookahead to check if atleast one capital exists in the string, then use your existing regex to match any letters ranging from 1-4 in length.

^(?=.*[A-Z])([a-zA-Z]{1,4})$

Output:

import re
test = ['A', 'Ab', 'AbC', 'ABC', 'Abcd', 'bACD', 'aBCd', 'abcD', 'abcd', 'a', 'abcdgehfyF', 'ABccdhdhdhd']
pattern = r'^(?=.*[A-Z])([a-zA-Z]{1,4})$'

for i in test:
    print(i, re.match(pattern, i))

A <re.Match object; span=(0, 1), match='A'>
Ab <re.Match object; span=(0, 2), match='Ab'>
AbC <re.Match object; span=(0, 3), match='AbC'>
ABC <re.Match object; span=(0, 3), match='ABC'>
Abcd <re.Match object; span=(0, 4), match='Abcd'>
bACD <re.Match object; span=(0, 4), match='bACD'>
aBCd <re.Match object; span=(0, 4), match='aBCd'>
abcD <re.Match object; span=(0, 4), match='abcD'>
abcd None
a None
abcdgehfyF None
ABccdhdhdhd None

To match this in a sentence, we can use this pattern

(?<=\s)(?=[^ ]*[A-Z])([a-zA-Z]{1,4})(?=\s)


string = "in this sentence, we want to find Abcd and Hi but not abcd and not abcdefgHI"
re.findall(r'(?<=\s)(?=[^ ]*[A-Z])([a-zA-Z]{1,4})(?=\s)', string)
#['Abcd', 'Hi']

Let us try a simple way that does not involve lookarounds, as it may be instructive.

  1. Consider a regex for matching 1-4 characters where the first MUST be capitalized
  2. Consider a regex for matching 2-4 characters where the second MUST be capitalized
  3. And so on...
  4. Combine the four cases using operator |

Tip: use \b at the start and end to match only full words.

Alternatively, you could use lookarounds to create a more concise regex. For example, using negative lookahead (?!...) as follows:

\b(?![a-z]+\b)[A-Za-z]{1,4}\b

Demonstration: https://pythex.org/?regex=%5Cb(%3F!%5Ba-z%5D%2B%5Cb)%5BA-Za-z%5D%7B1%2C4%7D%5Cb&test_string=in%20this%20sentence%2C%20we%20want%20to%20find%20Abcd%20and%20Hi%20but%20not%20abcd%20and%20not%20abcdefgH

You can assert whitespace boundaries to the left and right, and assert at least a single uppercase char [A-Z].

Then you can match 1-4 times [a-zA-Z].

(?<!\S)(?=[a-zA-Z]*[A-Z])[a-zA-Z]{1,4}(?!\S)

Regex demo

Example

import re

regex = r"(?<!\S)(?=[a-zA-Z]*[A-Z])[a-zA-Z]{1,4}(?!\S)"
s = "Abcd and Hi but not abcd and not abcdefgHI $aBc"
print(re.findall(regex, s))

Output

['Abcd', 'Hi']
Related