简体   繁体   中英

Regular expression for exact one character occurrence at any place of the string

I am writing a Java code that finds a way out from any maze and I need a class that checks generated mazes. Only . , # , S , X characters are allowed.

  1. . and # - one and more occurrences at any place of the string are allowed

  2. S and X - one and not more than one occurrence at any place of the string is allowed.

^[#//.]+$ - regex for the first condition, But I cannot implement the second one.

The maze input looks like this:

.......S..#.
.....###....
..X.........

. - empty space, # - wall, S - start, X - exit

You can use negative lookahead groups , written like (?!...) , to accomplish this, like so:

^(?!.*S.*S)(?!.*X.*X)[SX.#]+$

Demo

This accepts any set of characters from your set ( S , X , . , # ) from the start of the string using the ^ and [SX.#]+ . But it rejects any string containing 2 S s ( (?!.*S.*S) ) or 2 X s ( (?!.*X.*X) ).

Note that this actually checks both of your conditions. You don't really need 2 regexes here. Based on your example maze, though, it looks like your input can span multiple lines. In that case, you need to add \\n inside the final character class.

If the maze string consist of all the lines, you could use a single lookahead asserting only a single S, then match only one X (or the other way around)

^(?=[^S]*S[^S]*$)[.S#\r\n]*X[.S#\r\n]*$

In Java

String regex = "^(?=[^S]*S[^S]*$)[.S#\\r\\n]*X[.S#\\r\\n]*$";

Explanation

  • ^ Start of string
  • (?= Positive lookahead, assert what is on the right is a single occurrence of S
    • [^S]*S Match 0+ times any char without S , match S
    • [^S]*$ Match 0+ times any char without S , end of string
  • ) Close lookahead
  • [.S#\\r\\n]*X Match all accepted chars including a newline without X , then match X
  • [.S#\\r\\n]* Match all accepted chars including a newline without X
  • $ End of string

Regex demo

You could also capture the characters, that are allowed just once and as soon as one is found, check by a lookahead if there is another one ahead. Probably no need to scan the full string several times.

^(?:[.#]*([SX])(?!.*?\1))*[.#]*$

Here is a demo at regex101

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM