I understand since Regex is essentially stateless, it's rather difficult to achieve complicated matches without resorting to supplementing application logic, however I'm curious to know if the following is possible.
Match all whitespace, easy enough: \s+
But skip whitespace between certain delimiters, in my case the word <pre>
and </pre>
nostrip
.
Are there any tricks to achieve this? I was thinking along the lines of two separate matches, one for all whitespace, and one for nostrip sections, and somehow negating the latter from the former.<pre>
blocks
"This is some text NOSTRIP this is more text NOSTRIP some more text."
// becomes
"ThisissometextNOSTRIP this is more text NOSTRIPsomemoretext."
The nesting of given tags nostrip sections is irrelevant, and I'm not trying to parse the tree HTML or anything, just tidying a text file, but saving the whitespace in nostrip sections for obvious reasons.<pre>
blocks
(better?)
This is ultimately what I went with. I'm sure it can be optimized in a few places, but it works nicely for now.
public function stripWhitespace($html, Array $skipTags = array('pre')){
foreach($skipTags as &$tag){
$tag = "<{$tag}.*?/{$tag}>";
}
$skipped = array();
$buffer = preg_replace_callback('#(?<tag>' . implode('|', $skipTags) . ')#si',
function($match) use(&$skipped){
$skipped[] = $match['tag'];
return "\x1D" . (count($skipped) - 1) . "\x1D";
}, $html
);
$buffer = preg_replace('#\s+#si', ' ', $buffer);
$buffer = preg_replace('#(?:(?<=>)\s|\s(?=<))#si', '', $buffer);
for($i = count($skipped) - 1; $i >= 0; $i--){
$buffer = str_replace("\x1D{$i}\x1D", $skipped[$i], $buffer);
}
return $buffer;
}