Jump to content

Split content into 2 columns with the start of a para


jarvis

Recommended Posts

Hi All,

 

I hope someone can help. I use the below code to split content into 2 columns. Works a treat by finding the middle count of words and splitting. Problem is, sometimes it leaves the start of a line in one column and the rest in the other column.

 

How can I alter the below to do what it does but find the start of a para as well to make it a neater split? God I hope that makes sense! Here's the code

 

		<?php
	$extract = str_replace("\r", "<br />", get_the_content(''));
	function splitMyText($colcontent, $columns) {    

		$bodytext = array("$colcontent");
		$text = implode(",", $bodytext); //prepare bodytext
		$length = strlen($text);
		//determine the length of the text
		$length = ceil($length/$columns);
		//divide length by number of columns
		$words = explode(" ",$text);
		// prepare text for word count and split the body into columns
		$c = count($words);
		$l = 0;

		for ($i=1;$i<=$columns;$i++) {
			$new_string = "";
			$coloutput .= '<div class="column">';

			for ($g=$l;$g<=$c;$g++) {
				if (strlen($new_string) <= $length || $i == $columns)
					$new_string.=$words[$g]." ";
				else {
					$l = $g;
					break;
				}
			}
			$coloutput .= $new_string;
			$coloutput .= "</div>";
		}

				return $coloutput;
	}  
	$columns = 2;
			   
	echo splitMyText($extract, $columns); 
	?>

 

Thanks

Link to comment
Share on other sites

The columns won't ever be equal, as it's unlikely the end of the paragraph will fall at the half-way point -- although in my opinion, if one column has to be longer it would make sense for it to be the first.

 

I'd select the text up-to the middle character, and all after until the next new-line character. You can use regex to simplify this substantially:

 

// First find the half-way point
$length = strlen($str);
$target = ceil($length / 2);

// Use regex to match the characters from the start of the string to the target
// point, and up until the next new-line character. Then also match any subsequent
// characters separately...
if (preg_match('/^(.{' . $target . '}[^\n]*)(.+)$/s', $str, $matches)) //
{
    // Store $matches[1] and $matches[2] into more meanigful variables
    list( , $column1, $column2) = $matches;
}

 

Which you could display with some simple HTML like:

 

<div class="article">
    <div class="column1">
        <?php echo nl2br($column1); ?>
    </div>
    <div class="column2">
        <?php echo nl2br($column2); ?>
    </div>
</div>

 

Some formatting and trimming will be needed to prevent gaps at the start of column2. You should probably add an else statement to that IF, just in-case the regex doesn't work (say for 1 paragraph length articles).

 

Also I only noticed after writing this, that you pass in a dynamic column number to your function.. If you only ever use 2 like your post suggested, then it's not a problem..?

Link to comment
Share on other sites

or you can figure out how many lines you have first, and then split accordingly:

 

1. split sentences into array using \n as delimiter

 

2. check each sentence length, to determine if they will occupy more than one line. Split sentences if needed

 

3. devide lines into columns

 

Hope this helps

Link to comment
Share on other sites

If you don't need more than 2 columns, and want to encapsulate it within a function, you can use this:

 

function columiseText($str)
{
    // First find the half-way point
    $length = strlen($str);
    $target = ceil($length / 2);

    // Use regex to match the characters from the start of the string to the target
    // point, and up until the next new-line character. Then also match any subsequent
    // characters separately...
    if (preg_match('/^(.{' . $target . '}[^\n]*)(.+)$/s', $str, $matches)) //
    {
        // Return matches[1] and matches[2] as an array
        return array($matches[1], $matches[2]);
    }

    return false;
}

 

Some usage examples:

 

list($column1, $column2) = columiseText($article);

// ... or ...

$columns = columiseText($article);
    // $columns[0] contains column 1
    // $columns[1] contains column 2

 

As I said if you want to support a dynamic number of columns, it's perfectly possible to do so with a bit more work on the function.

Link to comment
Share on other sites

  • 1 year later...

You could use array_shift () on $matches, and then just return it. If you only want to retrieve the second group (the .*? part), then just make the first group (the one with $target) a non-capturing one.

 

Example of a non-capturing sub group:

/(?:Find|Test) (.*?)\./

This will only return whatever follows "Find " or "Test ", up until the first period.

Link to comment
Share on other sites

mmm... sorry, I post my code:

 

function columiseText($text, $columns)
{
$length = strlen($text);
$target = ceil($length / $columns);

$i=1;
if (preg_match('/^(.{' . $target . '}[^\n]*)(.+)$/s', $text, $matches))
{ 
while ($i<=$columns)
{
$split[$i] = $matches[$i];
$i++;
}
return $split;
}
return false;
}


$columns=3;
$article='Triskel Services is a reputable private security company successfully operating within the securities sector. We pride ourselves on both the quality of our services and our ethical approach designed to meet all of the requirements for private businesses. Our consultant teams include experts from various complementary backgrounds. All have experience in the private and public sectors. This diversity creates a synergy that allows us to operate efficiently as security consultants supplying security solutions and services to all. For our security operations we deploy highly skilled and experienced security teams, working to strict professional standards. All operations are designed to meet the individual’s requirements, ensuring minimum disruption to the daily routine of our customers. The diverse nationalities and multi-language skills combined with the competencies of our employees mean that we can deploy the most suitable task specific personnel to a variety of countries covering all possible circumstances.';
$cols = columiseText($article, $columns);

echo "<div style='float:left; width:200px; margin-right:20px;'>".$cols[1]."</div>";
echo "<div style='float:left; width:200px; margin-right:20px;'>".$cols[2]."</div>";
echo "<div style='float:left; width:200px; margin-right:20px;'>".$cols[3]."</div>";

Edited by bartolix
Link to comment
Share on other sites

That code did not work at all, so I took the liberty to rewrite it for you.

 

I've based my version on the multibyte string functions, to avoid mangling characters in case you get some text with characters occupying more than 1 byte. I've also approximated the cut-off points to a space, to that no words will be cut up either.

 

The result:

<?php

/**
* Splits a given text into the specified number columns, by the closest space from the cut-off point.
* 
* @param string $text
* @param int $columns
* @param string $encoding
* @return array
*/
function columiseText ($text, $columns, $encoding = 'UTF-8') {
   // Get the length of the article, and approximate cut points.
   $length = mb_strlen ($text, $encoding);
   $target = ceil ($length / $columns);

   // Set starting position and ready array for return values.
   $start = 0;
   $columns = array ();

   // While the string is not fully parsed.
   do {
       // Make sure the cut-off point is within the string.
       $stop = $target + $start - 1;
       if ($stop > $length) {
           // Get the remainder of the string.
           $stop = $length;
       } else {
           // Find the position of the first space before the cut position.
           $stop = mb_strpos ($text, ' ', $stop, $encoding) - $start;
       }

       // Cut the string at the specified point, and save it in the return array.
       $columns[] = trim (mb_substr ($text, $start, $stop, $encoding));

       // Increase the start position for the next column.
       $start += $stop;
   } while ($start < $length);

   // Return the finished columns.
   return $columns;
}

$columns = 3;
$article = 'Triskel Services is a reputable private security company successfully operating within the securities sector. We pride ourselves on both the quality of our services and our ethical approach designed to meet all of the requirements for private businesses. Our consultant teams include experts from various complementary backgrounds. All have experience in the private and public sectors. This diversity creates a synergy that allows us to operate efficiently as security consultants supplying security solutions and services to all. For our security operations we deploy highly skilled and experienced security teams, working to strict professional standards. All operations are designed to meet the individual’s requirements, ensuring minimum disruption to the daily routine of our customers. The diverse nationalities and multi-language skills combined with the competencies of our employees mean that we can deploy the most suitable task specific personnel to a variety of countries covering all possible circumstances.';

$cols = columiseText ($article, $columns);

var_dump ($cols);

 

This provided the following result:

array(3) {
 [0]=>
 string(340) "Triskel Services is a reputable private security company successfully operating within the securities sector. We pride ourselves on both the quality of our services and our ethical approach designed to meet all of the requirements for private businesses. Our consultant teams include experts from various complementary backgrounds. All have"
 [1]=>
 string(345) "experience in the private and public sectors. This diversity creates a synergy that allows us to operate efficiently as security consultants supplying security solutions and services to all. For our security operations we deploy highly skilled and experienced security teams, working to strict professional standards. All operations are designed"
 [2]=>
 string(336) "to meet the individual’s requirements, ensuring minimum disruption to the daily routine of our customers. The diverse nationalities and multi-language skills combined with the competencies of our employees mean that we can deploy the most suitable task specific personnel to a variety of countries covering all possible circumstances."
}

Edited by Christian F.
Link to comment
Share on other sites

This is the results of a test with me running the code above, only with 4 columns:

$ php test.php
array(4) {
 [0]=>
 string(258) "Triskel Services is a reputable private security company successfully operating within the securities sector. We pride ourselves on both the quality of our services and our ethical approach designed to meet all of the requirements for private businesses. Our"
 [1]=>
 string(255) "consultant teams include experts from various complementary backgrounds. All have experience in the private and public sectors. This diversity creates a synergy that allows us to operate efficiently as security consultants supplying security solutions and"
 [2]=>
 string(261) "services to all. For our security operations we deploy highly skilled and experienced security teams, working to strict professional standards. All operations are designed to meet the individual’s requirements, ensuring minimum disruption to the daily routine"
 [3]=>
 string(246) "of our customers. The diverse nationalities and multi-language skills combined with the competencies of our employees mean that we can deploy the most suitable task specific personnel to a variety of countries covering all possible circumstances."
}


Max memory used: 643488
Execution time: 0.0002131462097168

So I somewhat doubt that you executed the exact same code, especially since you said it takes several seconds. If you haven't added something extra to the code, then it's most likely the system which you're running it through.

Thus the questions becomes: How does the code of entire file you're executing look like, and is this the very same file that you request PHP to parse (or is it included by another file)?

Edited by Christian F.
Link to comment
Share on other sites

Glad you got it sorted out, though by editing the text you didn't exactly run the same code. :P

 

As for how to divide at a period, I'll leave that up to you to figure out. The code should be well commented, and quite easy to understand. Doing it yourself will help you understand what the code does, and thus reduce the amount of help you'll need in the future. ;)

 

PS: I made one mistake in the comments, writing "before" instead of "after". I apologize for that.

Link to comment
Share on other sites

Without stating what the problem is, in detail, or posting the changed code I'm afraid no-one can help. Remember that we don't know what you've done or what you're seeing on your screen, so we're depending upon you to be as accurate and complete in your descriptions as possible.

 

That said, there are two things that needs to be done with the code above to make it split properly on periods.

The first thing is that I've included the space (splitting character) at the start of the next line. So to get the period on the proper place, you'll need to increase the stopping position with one character.

The other thing, is that periods are a whole lot less frequent than spaces, which means that the difference between the desired cutting position and the next period can be quite large. To combat that problem, you'll need to search both directions, and choose the one with the smaller difference.

 

In other words, replace the else-block in the above code with this:

			// Find the positions of the first periods before and after the cut position.
		$stop_1 = mb_strpos ($text, '.', $stop, $encoding) - $start + 1;
		$stop_2 = mb_strrpos ($text, '.', ($stop+$start) - $length, $encoding) + 1;

		// Determine which cutting point is closest to desired length.
		if (($stop_1 - $start) - $target > $target - ($stop_2 - $start)) {
			$stop = $stop_2;
		} else {
			$stop = $stop_1;
		}

 

Despite these changes you will notice a whole lot more variance in the column lengths, as opposed to the splitting on the space. Exactly how much depends upon the length of the sentences at the cut-off points.

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.