Regex- Validation (validate string with not more than 1 space in between)

Edit after the updated question:

You could first assert the allowed format of only max a single space in between, and then match 5-255 characters followed by optional spaces.

^ *+(?=[\w-]++(?> ?[\w-]+)*+ *$)[\w -]{4,254}[\w-] *$

The pattern matches:

  • ^ Start of string
  • *+ Match optional spaces using a possessive quantifier (do not backtrack)
  • (?= Positive lookahead, assert that on the right is
    • [\w-]++ Match 1+ word chars or -
    • (?> ?[\w-]+)*+ Repeat matching an optional space followed by 1+ occurrences of \w or - in an atomic group
    • *$ Match optional spaces till end of string
  • ) Close lookahead
  • [\w -]{4,254}[\w-] Match 4 to 254 occurrences of either \w or -
  • * Match optional spaces
  • $ End of string

Regex demo

Example code

$strings = [
    "abc bb",
    "abc d",
    "b b v",
    "There is a very good question",
    "The thing might not go your way",
    "abcd",
    "   bbv",
    "abc  bb",
    "abc   bb",
    "abc    bb",
    "There is  a   very good question",
    "The thing  might  not   go   your way"
];

$pattern = "/^ *+(?=[\w-]++(?> ?[\w-]+)*+ *$)[\w -]{4,254}[\w-] *$/";

foreach ($strings as $s) {
    if (preg_match($pattern, $s)) {
        echo "Match for: [$s]" . PHP_EOL;
    } else {
        echo "No match for: [$s]" . PHP_EOL;
    }
}

Output

Match for: [abc bb]
Match for: [abc d]
Match for: [b b v]
Match for: [There is a very good question]
Match for: [The thing might not go your way]
No match for: [abcd]
No match for: [   bbv]
No match for: [abc  bb]
No match for: [abc   bb]
No match for: [abc    bb]
No match for: [There is  a   very good question]
No match for: [The thing  might  not   go   your way]

Edited answer, after edited question. You could try:

^ *+(?!.+?  \S)[\w -]{4,254}[\w-] *$

See an online demo


  • ^ *+ - Start-line anchor followed by 0+ (Possessive, not giving back) spaces;
  • (?!.+? \S) - A negative lookahead to prevent any line to have two space characters inbetween non-whitespace chars;
  • [\w -]{4,254}[\w-] - Match any word-character, hyphen or space 4-254 times before the last hyphen/word-character;
  • *$ - 0+ (Greedy) spaces before end-line anchor.

Okay, I'll offer a pattern that will ignore leading and trailing spaces, obey the salvageable string length, and ensure that multiple spaces do not exist in the salvageable string.

But first, let's simply your length requirement by reducing 255 to 10 ...just for the sake of unit testing. In my answer, assume that I am validating for a string length between 5 and 10 characters (instead of 5 and 255).

My suggested pattern will allow leading spaces, match the first word-or-dash character, then match any character 3 to 8, then the last word-or-dash character, then allow trailing spaces. 1+3+1 gives the minimum 5 characters and 1+8+1 gives the maximum 10 characters. For the asked question, use 1+3+1 and 1+253+1 to sum up to the desired length limit: {5,255}.

^ *             #match start of string then zero or more allowable/uncountable spaces
[\w-]           #match first word-or-dash character
(?=             #lookahead
   .{3,8}       #match to any character 3 to 8 times to enforce length requirements
   [\w-]        #match last word-or-dash character
    *$          #match allowable/uncountable trailing spaces then end of string
) ?             #close lookahead, then match zero or one space
(?>[\w-]+ ?)+   #repeatedly match one or more word-or-dash sequences followed by zero or one space
 *$             #match zero or more spaces until the end of the string

Code: (Demo)

$tests = [
    '123 56',
    ' 1234',
    '1234 ',
    '123 5',
    '   1234',
    '    1234    ',
    ' 1 3 5 7 9 ',
    '  1 3 5 7 9   ',
    '   12 45 78 01',
    '   12 45 78 0 ',
    '  1234567890  ',
    ' 12345678901 ',
    '1 3 5 7 9  ',
    '1 3 5 7 9 1',
    '1 3 5 7 9 ',
    '1 3  6 8',
];

foreach ($tests as $test) {
    printf(
        "'%s' => %s or %s\n",
        $test,
        preg_match('/^ *[\w-](?=.{3,8}[\w-] *$) ?(?>[\w-]+ ?)+ *$/', $test) ? 'pass' : 'fail',
        preg_match('/^(?=.{5,10}$)(?>[\w-]+ ?)+$/', trim($test)) ? 'pass' : 'fail'
    );
}

Output:

'123 56' => pass or pass
' 1234' => fail or fail
'1234 ' => fail or fail
'123 5' => pass or pass
'   1234' => fail or fail
'    1234    ' => fail or fail
' 1 3 5 7 9 ' => pass or pass
'  1 3 5 7 9   ' => pass or pass
'   12 45 78 01' => fail or fail
'   12 45 78 0 ' => pass or pass
'  1234567890  ' => pass or pass
' 12345678901 ' => fail or fail
'1 3 5 7 9  ' => pass or pass
'1 3 5 7 9 1' => fail or fail
'1 3 5 7 9 ' => pass or pass
'1 3  6 8' => fail or fail