Наткнулся на этот вопрос и принял тот факт, что не было полностью работающего регулярного выражения, как личный вызов. Я считаю, что мне удалось создать регулярное выражение, которое работает для всех входов - при условии, что вы можете использовать атомная группировка / собственнические квантификаторы .
Конечно, я не уверен, есть ли какие-либо ароматы, которые позволяют атомарную группировку, но не обходные, но Вопрос задал вопрос, возможно ли в регулярном выражении указать исключение без обходного пути, и это технически возможно :
\A(?:$|[^f]++|f++(?:[^o]|$)|(?:f++o)*+(?:[^o]|$))*\Z
Пояснение:
\A #Start of string
(?: #Non-capturing group
$ #Consume end-of-line. We're not in foo-mode.
|[^f]++ #Consume every non-'f'. We're not in foo-mode.
|f++(?:[^o]|$) #Enter foo-mode with an 'f'. Consume all 'f's, but only exit foo-mode if 'o' is not the next character. Thus, 'f' is valid but 'fo' is invalid.
|(?:f++o)*+(?:[^o]|$) #Enter foo-mode with an 'f'. Consume all 'f's, followed by a single 'o'. Repeat, since '(f+o)*' by itself cannot contain 'foo'. Only exit foo-mode if 'o' is not the next character following (f+o). Thus, 'fo' is valid but 'foo' is invalid.
)* #Repeat the non-capturing group
\Z #End of string. Note that this regex only works in flavours that can match $\Z
Если по какой-либо причине вы можете использовать атомарную группировку, но не собственнические квантификаторы или обходные пути, вы можете использовать:
\A(?:$|(?>[^f]+)|(?>f+)(?:[^o]|$)|(?>(?:(?>f+)o)*)(?:[^o]|$))*\Z
Однако, как отмечают другие, возможно, более практично просто отрицать совпадение другими способами.