36

I'm looking for a regex that will match a word only if all its characters are unique, meaning, every character in the word appears only once.

Example:
abcdefg -> will return MATCH
abcdefgbh -> will return NO MATCH (because the letter b repeats more than once)

Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129

3 Answers3

63

Try this, it might work,

^(?:([A-Za-z])(?!.*\1))*$

Explanation

Assert position at the beginning of a line (at beginning of the string or after a line break character) «^»
Match the regular expression below «(?:([A-Z])(?!.*\1))*»
   Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
   Match the regular expression below and capture its match into backreference number 1 «([A-Z])»
      Match a single character in the range between “A” and “Z” «[A-Z]»
   Assert that it is impossible to match the regex below starting at this position (negative lookahead) «(?!.*\1)»
      Match any single character that is not a line break character «.*»
         Between zero and unlimited times, as many times as possible, giving back as needed (greedy) «*»
      Match the same text as most recently matched by capturing group number 1 «\1»
Assert position at the end of a line (at the end of the string or before a line break character) «$»
John Woo
  • 258,903
  • 69
  • 498
  • 492
  • 2
    @JohnWoo I wonder what tool you used to generate the explanation? – turtledove Oct 13 '12 at 07:27
  • 2
    @turtledove I know that [`expresso`](http://www.ultrapico.com/Expresso.htm) generates explanations - but John used something else. – Nir Alfasi Oct 13 '12 at 15:32
  • Hi, can I ask what tool you used for generating the expression? – Amal Murali Jan 20 '14 at 16:21
  • 1
    @turtledove: The explanation looks like the one generated by RegexBuddy (http://www.regexbuddy.com/). – Radu Maris Sep 10 '14 at 14:17
  • @AmalMurali: The explanation looks like the one generated by RegexBuddy (regexbuddy.com). (Onle one user can be notified per comment, that's why I duplicated the comment to make sure both users asking for the tool are notified). – Radu Maris Sep 10 '14 at 14:19
  • @RaduMaris: Too bad I'm on *nix. It seems to be a very useful tool for people who have to work with regexes! – Amal Murali Sep 10 '14 at 14:22
  • @AmalMurali I use it on Ubuntu via Wine for about 4y now, never have big issues, sometimes it's slow especially when you navigate with keys in the regexp, but IMHO it's usable and the price is decent 29 $/€, but agree to bad they don't have a cross-platform version. (AFAIK they still offer a 30 days trial, so see it yourself if works good enough for you) – Radu Maris Sep 10 '14 at 14:31
  • Can I use this in some way to match set of discrete non repeating words (separated by commas) – Jasir Jul 15 '16 at 13:05
  • What is the purpose of the non-capturing group ?: ? – Thanos Dodd Jul 18 '20 at 06:01
  • @Thanos Dodd, the non-capturing group is there so that the last `*` can be applied to the expression as a whole (i.e., to the first character selected by the internal capturing group + the whole negative lookahead expression that backreferences it). This is a clever construct meant to select one character of the string at a time to be matched against the rest of the string. – Anthony Accioly Jan 09 '21 at 22:28
14

You can check whether there are 2 instances of the character in the string:

^.*(.).*\1.*$

(I just simply capture one of the character and check whether it has a copy elsewhere with back reference. The rest of .* are don't-cares).

If the regex above match, then the string has repeating character. If the regex above doesn't match, then all the characters are unique.

The good thing about the regex above is when the regex engine doesn't support look around.

Apparently John Woo's solution is a beautiful way to check for the uniqueness directly. It assert at every character that the string ahead will not contain the current character.

nhahtdh
  • 55,989
  • 15
  • 126
  • 162
3

This one would also provide a full match to any length word with non-repeating letters:

^(?!.*(.).*\1)[a-z]+$

I slightly revised the answer provided by @Bohemian to another question a while ago to get this.

It has also been a while since the question above has been asked but I thought it would be nice to also have this regex pattern here.

Alper
  • 299
  • 2
  • 12