Regex to match unescaped characters only

Problem:

Find key-value pairs separated by : but ignore escaped colons, i.e. \:.

Solution:

Negative lookbehinds – see Regex Tutorial – Lookaheads and Lookbehinds. “Positive” means “must match”, “Negative” means “must not match”. Over here, we are “looking behind” the backslash to ensure that a colon is not matched.

Single match in a string:

<?php
// 4 backslashes in PHP = 2 backslashes in pattern string = escaped (literal) backslash in regex
$regex = '/(.*(?<!\\\\)):(.*)/';
$str = 'a:b\:c';

$isMatch = preg_match($regex, $str, $matches);
var_dump($matches);
// array (size=3)
//   0 => string 'a:b\:c' (length=6)
//   1 => string 'a' (length=1)
//   2 => string 'b\:c' (length=4)

Multiple matches in multi-line text enclosed by braces:

<?php
$regex = '/\{([a-z\-_\\\\:]*(?<!\\\\)):([a-z\-_\\\\:]*)\}/i';
$str = <<<'EOD'
{a:b\:c}   {d\:e:f}

{date_format:Y-m-d\TH\:i\:s}

y:z
EOD;

$hasMatch = preg_match_all($regex, $str, $matches, PREG_SET_ORDER);
var_dump($matches);
// array (size=3)
//   0 => 
//     array (size=3)
//       0 => string '{a:b\:c}' (length=8)
//       1 => string 'a' (length=1)
//       2 => string 'b\:c' (length=4)
//   1 => 
//     array (size=3)
//       0 => string '{d\:e:f}' (length=8)
//       1 => string 'd\:e' (length=4)
//       2 => string 'f' (length=1)
//   2 => 
//     array (size=3)
//       0 => string '{date_format:Y-m-d\TH\:i\:s}' (length=28)
//       1 => string 'date_format' (length=11)
//       2 => string 'Y-m-d\TH\:i\:s' (length=14)