MyQ
MyQ

Reputation: 469

Limited number of break lines in text in PHP

Assume a string is like

Section 1

Section 2 (after 1 line break)


Section 3 (after 2 line breaks)




Section 4 (after 4 line breaks)

Section 5 (after 1 line break)

My intention is to only allow N number of breaks and replace the other ones with SPACE in PHP. For example, if N=3 then the text above would be outputed like:

Section 1

Section 2 (after 1 line break)

Section 3 (after 2 line breaks) Section 4 (after 4 line breaks) Section 5 (after 1 line break)

My code is below but I am looking for a better way:

function limitedBreaks($str = '', $n = 5)
{
    $str = nl2br($str);
    $chars = str_split($str);
    $counter = 0;
    foreach ($chars as $key => $char) {
        if ($char == "<br/>")
            if ($counter > $n) {
                $chars[$key] = ' ';
            } else {
                $counter += 1;
            }
    }

    return implode($chars, ' ');

}

Upvotes: 0

Views: 138

Answers (2)

Markus AO
Markus AO

Reputation: 4889

This really is a job better suited for regex than exploding and iterating.

$str = preg_replace('~\v+~', "\n\n", $str);

Here \v+ matches any number of vertical spaces, substituted with two \n newlines. You will get a standard one line gap between your content (non-linebreak-only) lines. This results in:

Section 1

Section 2 (after 1 line break)

Section 3 (after 2 line breaks)

Section 4 (after 4 line breaks)

Section 5 (after 1 line break)

If you want to only target more than N breaks, use e.g. \v{4,}, assuming EOL is \n, to normalize all instances of 4 or more newlines. If your file uses Windows EOL (\r\n), be aware that you need to double it up, or use (\r\n|\n){4,}, since \r and \n each are one match of \v.

That's the basic idea. Seeing as you want to replace 4+ newlines with horizontal space, merging the lines instead of normalizing line break quantity, you would simply:

$str = preg_replace('~(\r\n|\n){4,}~', " ", $str);

This would give you:

Section 1

Section 2 (after 1 line break)


Section 3 (after 2 line breaks) Section 4 (after 4 line breaks)

Section 5 (after 1 line break)

Here the gap with 4 or more EOLs was substituted with space and merged with the preceding line. The rest of the "acceptably spaced" lines are still in their places.


However it seems that you want to merge all subsequent rows into a single line after any gap of 4+ EOLs. Is that really the requirement? The first example I posted is a fairly standard operation for normalizing content with irregular linebreaks; especially "level items" like section headings!


OP: thanks for explaining your use case, makes sense. This can be regex-ed without loops:

$str = preg_replace_callback('~(\r\n|\n){4,}(?<therest>.+)~s', function($match) {
    return ' ' . preg_replace('~\v+~', ' ', $match['therest']);
}, $str);

Here we capture (as named group therest) all the content that follows four or more linebreaks using preg_replace_callback, and inside the callback we preg_replace all vertical spaces in "the rest" with a single horizontal space. This results in:

Section 1

Section 2 (after 1 line break)


Section 3 (after 2 line breaks) Section 4 (after 4 line breaks) Section 5 (after 1 line break) Section 17 after a hundred bundred line breaks"

For convenience, here's the regex above wrapped in a function:

function fuse_breaks(string $str): string {
    $str = preg_replace_callback('~(\r\n|\n){4,}(?<therest>.+)~s', function($match) {
        return ' ' . preg_replace('~\v+~', ' ', $match['therest']);
    }, $str);
    return $str;
}

// Usage:
$fused_text = fuse_breaks($source_text);

Upvotes: 1

lukas.j
lukas.j

Reputation: 7173

Your example with N=3 shows either 4 line breaks – if the empty lines count –, or 2 line breaks.

To make things clearer this is a function limitedLines, which reduces the text to a specific amount of lines:

$str = "
line 1 
line 2
line 3
line 4
line 5
line 6
";

function limitedLines(string $str = '', int $maxLines = 5): string {
  $maxLines = $maxLines < 1 ? 1 : $maxLines;
  $arr = explode("\n", trim($str));
  while (count($arr) > $maxLines) {
    $last = array_pop($arr);
    $arr[array_key_last($arr)] .= ' ' . trim($last);
  }
  return implode("\n", $arr);
}

$result = limitedLines($str, 3);

print_r($result);

This will print:

line 1 
line 2
line 3 line 4 line 5 line 6

Upvotes: 1

Related Questions