Assuming that we consider 2222 to contain 222 twice (from character 1 to 3, and from character 2 to 4): We define some building blocks: A = "no 1's, no 222". B = "no 1's, exactly one 222", C = "1 followed by A, but not followed by 23", and D = "1 followed by B, but not followed by 23". Then the regular expression is
A C* | A C* D C* | B C* = (A | A C* D | B) C*
We take another building block X = ((2 | 22)? 3)* which is any number of 3s, each preceeded by one, two or no 2's.
A consists of any number of 3s preceeded by no, one or two 2s, followed by no, one or two 2s. A = X (2 | 22)?
B is quite similar, but it ends either in 222, or in 2223 followed by A: B = X 222 (3 A)?
C starts with 1. If there are any 3's, then the first one is not preceded by exactly one 2, otherwise it is very similar to A: C = 1 ((22)? 3 X)? (2 | 22)?.
D starts with 1, followed by B, but again if there are any 3's then the first one is not preceded by exactly one 2. D = 1 ((22)? 3 X)? 222 (3 A)?.
To get the usual regular expression, substitute everything, which will make the expression rather large and unreadable.
(((2 | 22)? 3)* (2 | 22)? | ((2 | 22)? 3)* (2 | 22)? (1 ((22)? 3 ((2 | 22)? 3)*)? (2 | 22)?)* 1 ((22)? 3 ((2 | 22)? 3)*)? 222 (3 ((2 | 22)? 3)* (2 | 22)?)? | ((2 | 22)? 3)* 222 (3 ((2 | 22)? 3)* (2 | 22)?)?) (1 ((22)? 3 ((2 | 22)? 3)*)? (2 | 22)?)*
Same thing can be achieved with a state machine with 10 accepting states and one non-accepting state.
grep '^[123]*$' | grep -v 123 | grep -v '222.*222'
is much easier to understand than a single regular expression. $\endgroup$