# regex to find a pair of adjacent digits with different digits around them

PythonPython 3.xRegex## Python Problem Overview

I'm a beginner to regex and I am trying to make an expression to find if there are two of the same digits next to each other, and the digit behind and in front of the pair is different.

For example,

123456678 should match as there is a double 6,

1234566678 should not match as there is no double with different surrounding numbers. 12334566 should match because there are two 3s.

So far i have this which works only with 1, and as long as the double is not at the start or end of the string, however I can deal with that by adding a letter at the start and end.

```
^.*([^1]11[^1]).*$
```

I know i can use `[0-9]`

instead of the 1s but the problem is having them all be the same digit.

Thank you!

## Python Solutions

## Solution 1 - Python

With regex, it is much more convenient to use a PyPi `regex`

module with the `(*SKIP)(*FAIL)`

based pattern:

```
import regex
rx = r'(\d)\1{2,}(*SKIP)(*F)|(\d)\2'
l = ["123456678", "1234566678"]
for s in l:
print(s, bool(regex.search(rx, s)) )
```

See the Python demo. Output:

```
123456678 True
1234566678 False
```

**Regex details**

`(\d)\1{2,}(*SKIP)(*F)`

- a digit and then two or more occurrences of the same digit`|`

- or`(\d)\2`

- a digit and then the same digit.

The point is to match all chunks of identical 3 or more digits and skip them, and then match a chunk of two identical digits.

See the regex demo.

## Solution 2 - Python

I have divided my answer into four sections.

The first section contains my solution to the problem. Readers interested in nothing else may skip the other sections.

The remaining three sections are concerned with identifying the pairs of equal digits that are preceded by a different digit and are followed by a different digit. The first of the three sections matches them; the other two capture them in a group.

I've included the last section because I wanted to share *The Greatest Regex Trick Ever* with those unfamiliar with it, because I find it so very cool and clever, yet simple. It is documented here. Be forewarned that, to build suspense, the author at that link has included a lengthy preamble before the drum-roll reveal.

**Determine if a string contains two consecutive equal digits that are preceded by a different digit and are followed by a different digit**

You can test the string as follows:

```
import re
r = r'(\d)(?!\1)(\d)\2(?!\2)\d'
arr = ["123456678", "1123455a666788"]
for s in arr:
print(s, bool(re.search(r, s)) )
```

displays

```
123456678 True
1123455a666788 False
```

Run Python code | Start your engine!^{1}

The regex engine performs the following operations.

```
(\d) : match a digit and save to capture group 1 (preceding digit)
(?!\1) : next character cannot equal content of capture group 1
(\d) : match a digit in capture group 2 (first digit of pair)
\2 : match content of capture group 2 (second digit of pair)
(?!\2) : next character cannot equal content of capture group 2
\d : match a digit
```

`(?!\1)`

and `(?!\2)`

are *negative lookaheads*.

**Use Python's regex module to match pairs of consecutive digits that have the desired property**

You can use the following regular expression with Python’s `regex`

module to obtain the matching pairs of digits.

```
r'(\d)(?!\1)\K(\d)\2(?=\d)(?!\2)'
```

The regex engine performs the following operations.

```
(\d) : match a digit and save to capture group 1 (preceding digit)
(?!\1) : next character cannot equal content of capture group 1
\K : forget everything matched so far and reset start of match
(\d) : match a digit in capture group 2 (first digit of pair)
\2 : match content of capture group 2 (second digit of pair)
(?=\d) : next character must be a digit
(?!\2) : next character cannot equal content of capture group 2
```

`(?=\d)`

is a *positive lookahead*. `(?=\d)(?!\2)`

could be replaced with `(?!\2|$|\D)`

.

**Save pairs of consecutive digits that have the desired property to a capture group**

Another way to obtain the matching pairs of digits, which does not require the regex module, is to extract the contents of capture group 2 from matches of the following regular expression.

```
r'(\d)(?!\1)((\d)\3)(?!\3)(?=\d)'
```

The following operations are performed.

```
(\d) : match a digit in capture group 1
(?!\1) : next character does not equal last character
( : begin capture group 2
(\d) : match a digit in capture group 3
\3 : match the content of capture group 3
) : end capture group 2
(?!\3) : next character does not equal last character
(?=\d) : next character is a digit
```

**Use The Greatest Regex Trick Ever to identify pairs of consecutive digits that have the desired property**

We use the following regular expression to match the string.

```
r'(\d)(?=\1)|\d(?=(\d)(?!\2))|\d(?=\d(\d)\3)|\d(?=(\d{2})\d)'
```

When there is a match, we pay no attention to which character was matched, but examine the content of capture group 4 (`(\d{2})`

), as I will explain below.

The first three components of the alternation correspond to the ways that a string of four digits can fail to have the property that the second and third digits are equal, the first and second are unequal and the third and fourth are equal. They are:

```
(\d)(?=\1) : assert first and second digits are equal
\d(?=(\d)(?!\2)) : assert second and third digits are not equal
\d(?=\d(\d)\3) : assert third and fourth digits are equal
```

It follows that if there is a match of a digit and the first three parts of the alternation fail the last part (`\d(?=(\d{2})\d)`

) must succeed, and the capture group it contains (#4) must contain the two equal digits that have the required properties. (The final `\d`

is needed to assert that the pair of digits of interest is followed by a digit.)

If there is a match how do we determine if the last part of the alternation is the one that is matched?

When this regex matches a digit we have no interest in what digit that was. Instead, we look to capture group 4 (`(\d{2})`

). If that group is empty we conclude that one of the first three components of the alternation matched the digit, meaning that the two digits following the matched digit do not have the properties that they are equal and are unequal to the digits that precede and follow them.

If, however, capture group 4 is not empty, it means that none of the first three parts of the alternation matched the digit, so the last part of the alternation must have matched and the two digits following the matched digit, which are held in capture group 4, have the desired properties.

^{1. Move the cursor around for detailed explanations.}

## Solution 3 - Python

Inspired by the answer or Wiktor Stribiżew, another variation of using an alternation with `re`

is to check for the existence of the capturing group which contains a positive match for 2 of the same digits not surrounded by the same digit.

In this case, check for group 3.

```
((\d)\2{2,})|\d(\d)\3(?!\3)\d
```

`(`

Capture**group 1**`(\d)\2{2,}`

Capture**group 2**, match 1 digit and repeat that same digit 2+ times`)`

Close group`|`

Or`\d(\d)`

Match a digit, capture a digit in**group 3**`\3(?!\3)\d`

Match the same digit as in group 3. Match the 4^{th}digit, but is should not be the same as the group 3 digit

For example

```
import re
pattern = r"((\d)\2{2,})|\d(\d)\3(?!\3)\d"
strings = ["123456678", "12334566", "12345654554888", "1221", "1234566678", "1222", "2221", "66", "122", "221", "111"]
for s in strings:
match = re.search(pattern, s)
if match and match.group(3):
print ("Match: " + match.string)
else:
print ("No match: " + s)
```

Output

```
Match: 123456678
Match: 12334566
Match: 12345654554888
Match: 1221
No match: 1234566678
No match: 1222
No match: 2221
No match: 66
No match: 122
No match: 221
No match: 111
```

If for example 2 or 3 digits only is also ok to match, you could check for group 2

```
(\d)\1{2,}|(\d)\2
```

## Solution 4 - Python

You can also use a simple way .

```
import re
l=["123456678",
"1234566678",
"12334566 "]
for i in l:
matches = re.findall(r"((.)\2+)", i)
if any(len(x[0])!=2 for x in matches):
print "{}-->{}".format(i, False)
else:
print "{}-->{}".format(i, True)
```

You can customize this based on you rules.

Output:

```
123456678-->True
1234566678-->False
12334566 -->True
```