Replace tabs and spaces with a single space as well as carriage returns and newlines with a single newline

Viewed 74440
$string = "My    text       has so    much   whitespace    




Plenty of    spaces  and            tabs";

echo preg_replace("/\s\s+/", " ", $string);

I read the PHP's documentation and followed the preg_replace() tutorial, however this code produces:

My text has so much whitespace Plenty of spaces and tabs

How can I turn it into :

My text has so much whitespace    
Plenty of spaces and tabs
11 Answers

Had the same problem when passing echoed data from PHP to Javascript (formatted as JSON). The string was peppered with useless \r\n and \t characters that are neither required nor displayed on the page.

The solution i ended up using is another way of echoing. That saves a lot of server resources compared to preg_replace (as it is suggested by other people here).


Here the before and after in comparison:

Before:

echo '
<div>

    Example
    Example

</div>
';

Output:

<div>\r\n\r\n\tExample\r\n\tExample\r\n\r\n</div>


After:

echo 
'<div>',

    'Example',
    'Example',

'</div>';

Output:

<div>ExampleExample</div>


(Yes, you can concatenate echo not only with dots, but also with comma.)

This task requires that consecutive spaces and tabs ("horizontal whitespaces" -- \h) be replaced with a single literal space and that consecutive carriage returns and newlines ("verticle whitespaces" -- \v) be replaced with a newline. To ensure that the appropriate newline character sequences are used within your own system, use PHP_EOL.

It makes no sense to match as few as zero of an occurrence (with *) because you would potentially be adding a whitespace character where there was previously no whitespace character. For this reason, patterns for this task should only be using the + (one or more) quantifier.

If there is any chance of any kind of whitespaces occurring at the start or end of the string, then don't bother removing them with regex, just use trim().

In this context, \R would provide the same outcome as \v, but \R goes a bit farther and is more complex (perhaps unnecessarily). This is an informative read: https://www.npopov.com/2011/12/10/PCRE-and-newlines.html#meet-r

Code: (Demo)

$string = "
My    text       has so    much   whitespace    




Plenty of    spaces  and            tabs  ";
var_export(
    preg_replace(
        ['/\h+/', '/\v+/'],
        [' ',     PHP_EOL],
        trim($string)
    )
);

Output:

'My text has so much whitespace 
Plenty of spaces and tabs'

Not sure if this will be useful nor am I absolutely positive it works like it should but it seems to be working for me.

A function that clears multiple spaces and anything else you want or don't want and produces either a single line string or a multi-line string (dependent on passed arguments/options). Can also remove or keep characters for other languages and convert newline tabs to spaces.

/** ¯\_(ツ)_/¯ Hope it's useful to someone. **/
// If $multiLine is null this removes spaces too. <options>'[:emoji:]' with $l = true allows only known emoji.
// <options>'[:print:]' with $l = true allows all utf8 printable chars (including emoji).
// **** TODO: If a unicode emoji or language char is used in $options while $l = false; we get an odd � symbol replacement for any non-matching char. $options char seems to get through, regardless of $l = false ? (bug (?)interesting)
function alphaNumericMagic($value, $options = '', $l = false, $multiLine = false, $tabSpaces = "    ") {
    $utf8Emojis = '';
    $patterns = [];
    $replacements = [];
    if ($l && preg_match("~(\[\:emoji\:\])~", $options)) {
        $utf8Emojis = [
            '\x{1F600}-\x{1F64F}', /* Emoticons */
            '\x{1F9D0}-\x{1F9E6}',
            '\x{1F300}-\x{1F5FF}', /* Misc Characters */ // \x{1F9D0}-\x{1F9E6}
            '\x{1F680}-\x{1F6FF}', /* Transport and Map */
            '\x{1F1E0}-\x{1F1FF}' /* Flags (iOS) */
        ];
        $utf8Emojis = implode('', $utf8Emojis);
    }
    $options = str_replace("[:emoji:]", $utf8Emojis, $options);
    if (!preg_match("~(\[\:graph\:\]|\[\:print\:\]|\[\:punct\:\]|\\\-)~", $options)) {
        $value = str_replace("-", ' ', $value);
    }
    if ($l) {
        $l = 'u';
        $options = $options . '\p{L}\p{N}\p{Pd}';
    } else { $l = ''; }
    if (preg_match("~(\[\:print\:\])~", $options)) {
        $patterns[] = "/[ ]+/m";
        $replacements[] = " ";
    }
    if ($multiLine) {
        $patterns[] = "/(?<!^)(?:[^\r\na-z0-9][\t]+)/m";
        $patterns[] = "/[ ]+(?![a-z0-9$options])|[^a-z0-9$options\s]/im$l";
        $patterns[] = "/\t/m";
        $patterns[] = "/(?<!^)$tabSpaces/m";
        $replacements[] = " ";
        $replacements[] = "";
        $replacements[] = $tabSpaces;
        $replacements[] = " ";
    } else if ($multiLine === null) {
        $patterns[] = "/[\r\n\t]+/m";
        $patterns[] = "/[^a-z0-9$options]/im$l";
        $replacements = "";
    } else {
        $patterns[] = "/[\r\n\t]+/m";
        $patterns[] = "/[ ]+(?![a-z0-9$options\t])|[^a-z0-9$options ]/im$l";
        $replacements[] = " ";
        $replacements[] = "";
    }
    echo "\n";
    print_r($patterns);
    echo "\n";
    echo $l;
    echo "\n";
    return preg_replace($patterns, $replacements, $value);
}

Example usage:

echo header('Content-Type: text/html; charset=utf-8', true);
$string = "fjl!sj\nfl _  sfjs-lkjf\r\n\tskj 婦女與環境健康 fsl \tklkj\thl jhj ⚧ lkj ⸀ skjfl gwo lsjowgtfls s";
echo "<textarea style='width:100%; height:100%;'>";
echo alphaNumericMagic($string, '⚧', true, null);
echo "\n\nAND\n\n";
echo alphaNumericMagic($string, '[:print:]', true, true);
echo "</textarea>";

Results in:

fjlsjflsfjslkjfskj婦女與環境健康fslklkjhljhj⚧lkjskjflgwolsjowgtflss

AND

fjl!sj
fl _ sfjs-lkjf
    skj 婦女與環境健康 fsl klkj hl jhj ⚧ lkj ⸀ skjfl gwo lsjowgtfls s
Related