0
\$\begingroup\$

I just started watching some regex tutorials on Plural Sight just to learn how to write a proper regex expression. So far so good, it's been alright and I decided to test my knowledge on Hackerrank. Although, the tutorial explains how to use PCRE (Perl Compatible Regular Expressions) and ECMAScript engines but I'm comfortable using PCRE at the moment so hence my code is running on PHP.

A brief overview of the question is:

Task

You have a test string S. Your task is to match the pattern XXxXXxXX Here, x denotes whitespace characters, and X denotes non-white space characters.

Note

This is a regex only challenge. You are not required to write code. You have to fill the regex pattern in the blank (_________).


$handle = fopen ("php://stdin","r");
$Test_String = fgets($handle);
if(preg_match($Regex_Pattern, $Test_String, $output_array)){
    print ("true");
} else {
    print ("false");
}

fclose($handle);
?>

I don't really know much about PHP, all I had to do was fill in the blank so I wrote 2 test cases to see how my regex was doing so far in regex 101 e.g AA AA AA, BB BB BC.

$Regex_Pattern = "/\S{2}\s{1}\S{2}\s{1}\S{2}/"; //Do not delete '/'. Replace __________ with your regex.

Final thoughts: I'm fully aware the example isn't meant to be complicated as it's meant to show how to use \S and \s. I attempted using [\S\s]+\S{2}$ but that wasn't greedy enough as it permits multiple spacing.

Are there more refined ways to do this without using \S & \s and if S &\s can be used differently to achieve the same results?

\$\endgroup\$
  • \$\begingroup\$ I mean you can do something like /(\S{2}\s){2}\S{2}/ or /\S{2}\s\S{2}\s\S{2}/ but it this point you are splitting hairs. There really isn't anything substantive here to review. \$\endgroup\$ – Mike Brant Mar 24 '17 at 23:35
1
\$\begingroup\$

{1} is redundant, it's better to remove it. When {2} follows a specific character, it's is longer to type than the character itself twice, so I would simply write the character twice.

/\S\S\s\S\S\s\S\S/

As Mike Brant pointed out, some duplication could be reduced by grouping:

/(\S\S\s){2}\S\S/

But this is not as clear as the first example, so I would stick with that.

\$\endgroup\$
1
\$\begingroup\$

I'll offer some best practices when it comes to regex pattern authorship then delve into your specific case.

Use this hierarchy for writing the best possible pattern:

  1. Accuracy: This must be your first priority; without accuracy all other improvements are moot.
  2. Efficiency: In most cases, performance improvement often go unnoticed by your end user because the pattern is being run on a relatively small string and gains are measurable in microseconds. However, focusing on efficiency will develop your authorship and prepare you for cases where efficiency will deliver noticeable gains for your users.
  3. Brevity: Once items #1 and #2 are satisfied/optimized, I prefer to refine my patterns to their shortest expression. I never sacrifice #1 or #2 for #3. This may be, for example, changing /[A-Za-z]/ to /[A-Z]/i.
  4. Readability: Generally, I don't make much of an attempt to make regex patterns "more readable". Pattern comprehension is more often dependent on the reader's regex literacy. For example, some pattern authors might use: /\S{3}\s\S{3}/ to match foo bar rather than /\S{3} \S{3}/ because the \s is easier to "see". My personal rule is, readability is the least important consideration.

Now for your case, to differentiate between pattern variations, let's do a 'global' and 'multi-line' search on a block of several input strings like this: (Pattern Demo)

AA AA AA
BBB BB BC
DD DD DD
E EE EE
FF  FF FF
GG GG GGG
HH HH HH

A. /^(?:\S{2}\s?){3}$/gm This pattern has suboptimal accuracy because it may match the trailing white-space character before the end of line. I only list this pattern to show it as a disqualified pattern. (73 steps)

B. /^(?:\S{2}\s){2}\S{2}$/gm (59 steps)

C. /^\S\S\s\S\S\s\S\S$/gm (58 steps)

D. /^\S{2}(?:\s\S{2}){2}$/gm (57 steps)

E. /^\S\S\s\S\S\s\S{2}$/gm (54 steps)

F. /^\S\S\s\S{2}\s\S\S$/gm (54 steps)

G. /^\S{2}\s\S\S\s\S\S$/gm (51 steps)

H. /^\S\S\s\S{2}\s\S{2}$/gm (50 steps)

I. /^\S{2}\s\S\S\s\S{2}$/gm (47 steps)

J. /^\S{2}\s\S{2}\s\S{2}$/gm (43 steps)

K. /^\S{2} \S{2} \S{2}$/gm (42 steps)

You will find from the above patterns that the following statements are true:

  • Capture groups lead to losses in efficiency.
  • Quantifiers increase efficiency. (these are the curly bracketed components)
  • Patterns with the shortest expression to a non-match first are more efficient. (D vs B)
  • Patterns that ask for the minimum characters in a range are more efficient. (K vs J -- but K is literally unsuitable/innaccurate for this case because the test only state whitespace characters, not just spaces.)

p.s. Your attempted pattern: [\S\s]+\S{2}$ uses a character class [\S\s] but this ignores character order and is not the correct approach for what you are trying to match

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.