Forum Moderators: coopster

Message Too Old, No Replies

Split string into multiple lines by character length at spaces

Manually breaking a sentence into equal parts

         

whoisgregg

10:38 pm on Aug 9, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Hello! I'm trying to take a user entered string and manually break it into equal parts.

For example, the string "obfuscation in the skies to fly kites to space" broken into 2 lines should become

obfuscation in the skies
to fly kites to space

or if broken into 3 lines should become

obfuscation in
the skies to fly
kites to space

I can't simply count the words and split those equally because a long word (obfuscation) cannot count the same as a short word (to).

So, I wrote a script that takes the character length of the string, the expected character length of each line, then loops through an array of positions of spaces in the original string to put the right words in the right line. This method felt right at the time. :)

It works for the sample string above when instructed to break into 4 or 5 lines, but does strange things with other line amounts. (And will surely fail regularly when dealing with user input.)

My fingertips and forehead are bleeding from all the banging against keyboards and walls, so if anyone can take a look at this code or just recommend a better way, I could really use the help.

<?php
$str = "obfuscation in the skies to fly kites to space";
//works for 4 and 5 lines, nothing else
$numlines = 4;
$strarray = explode(' ',$str);
$strposarray = strpos_array($str,' ');
$strlength = strlen($str);
$halfway = floor($strlength / $numlines);
print_r($strarray);
echo '<hr />';
print_r($strposarray);
print '<p>('.$strlength.') Nearest space every '.$halfway.' characters</p>';
for ($n=1; $n<=$numlines; $n++){
echo '<p>'.$n.'';
for ($i=1; $i<(count($strposarray)); $i++){
if ($breakpoint[$n-1]<=($i-1)) {
$line[$n] .= ($strarray[$i-1].' ');
}
if ($n==$numlines){
$line[$n] .=
($strarray[($i+$breakpoint[$n-1]-1)].' ');
} else if ($strposarray[$i]>=($halfway*$n)){
$breakpoint[$n] = $i;
echo ' -> '.$i.'</p>';
break;
}
}
}
echo '<hr />';
print_r($breakpoint);
echo '<hr />';
print_r($line);
function strpos_array($haystack, $needle){
$kill = 0; // Kills while loop when changed
$offset = 0; // Offset for strpos()
$i = 0; // Counter, not iterator
while ($kill === 0) {
$i++;
$result = strpos($haystack, $needle, $offset);
if ($result === FALSE) { // If result is false (no more instances found), kill the while loop
$kill = 1;
} else {
$array[$i] = $result; // Set array
$offset = $result + 1; // Offset is set 1 character after previous occurence
}
}
return $array;
}
?>

whoisgregg

10:48 pm on Aug 9, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I'm such an idiot. I really ought to go read the PHP manual more often...

<?php
$str = "obfuscation in the skies to fly kites to space";
$numlines = 4;
$strlength = strlen($str);
$halfway = floor($strlength / $numlines);
echo wordwrap($str,$halfway,"<br />\n");
?>

whoisgregg

10:59 pm on Aug 9, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



And here's the final function to return either as an array or with a delimiter of choice:

function returnSplit($str,$lines,$delimiter,$array=false){ 
$strlength = strlen($str);
$halfway = floor($strlength / $lines);
if ($array){
$result = explode($delimiter,wordwrap($str,$halfway,$delimiter));
} else {
$result = wordwrap($str,$halfway,$delimiter);
}
return $result;
}

print_r(returnSplit($str,4,"<br />\r",true));

whoisgregg

11:06 pm on Aug 9, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I'm having a bad day, that function still doesn't work. In some cases it returns an extra line. Any ideas?

Returns 4 lines instead of 3:

$str = "in the skies to fly kites to space";
print_r(returnSplit($str,3,"<br />\r",true));

coopster

12:15 am on Aug 12, 2005 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



Hey, it's a one-person thread ;)

I was a bit intrigued when I first seen this thread a couple of days ago. I took a moment to look at it today and from the best I can understand of it ... it won't work the way you think/want/plan.

wordwrap() will always try to split the string based on the width parameter and sentences may have variable length words. This means you cannot specify how many lines are going to be returned, it just won't do what you expect. If you do indeed want to limit the amount of lines though you could split the string into an array and chunk it back together, lopping the leftovers into the last line:

function returnSplit($str, $lines, $delimiter, $array=false) 
{
$strlength = strlen($str);
$wrappoint = floor($strlength/($lines));
$newstring = array_chunk(explode($delimiter, wordwrap($str, $wrappoint, $delimiter)), $lines);
//print "$strlength\n$wrappoint\n"; print_r($newstring);
if (isset($newstring[1])) {
for ($i=1; $i<=count($newstring); $i++) {
$newstring[0][$lines-1] .= ' ' . implode(' ', $newstring[$i]);
unset($newstring[$i]);
}
}
return ($array)? $newstring[0] : implode($delimiter, $newstring[0]);
}

You can uncomment that print line to see some of how the array structure looks and how the functions are working to pull it apart and put it back together. I didn't spend much time here, but maybe somebody has a better solution or more insight? Or maybe there is a different solution altogether ... is there a reason you must have it divided *somewhat* evenly over a predetermined number of lines?

whoisgregg

8:19 pm on Aug 12, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



That function works quite well. There's a few situations that it breaks the lines funny, but at least it always ends up with the right number. :)

Thanks coopster!

is there a reason you must have it divided *somewhat* evenly over a predetermined number of lines?

I'm working on a (quickly becoming massive) GD image generation project and there are a number of instances where variable length user input text is drawn onto multiple lines. Multiple lines of centered text just look better if there's the same amount of text on each line. So, I need to split input to send it to the imagettftext() function for each line.

I suppose I *could* have written it so the user entered text into a textarea and then split on the line breaks, but the decision was made to *not* put that responsibility on the user.

whoisgregg

8:20 pm on Aug 27, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Having problems with long strings being split on many lines. If I want to get 15 lines out of:

"to to fiver 1 four to to four to to to four to tre tre to seventy tre to four tre fiver tre to four fiver sixxer to tre fiver to four sixxer basetenner sixxer eightter fiver tre seventy eightter"

All the lines look great until the last line which ends up with "basetenner sixxer eightter fiver tre seventy eightter"

So, I changed the floor() to a ceil() and it now produces "sixxer eightter fiver tre seventy eightter" as the last line and seems to do better weighting the words evenly, top to bottom. But, having the last line contain such a disparate quantity of the words is not working out. Any ideas?

coopster

6:16 pm on Aug 29, 2005 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



Remember before what I said about variable length of the words? This is what I was referring to. You will probably have to come up with some advanced formula to count words as well as word length and apply a different formula to your logic. Even then, I'm not sure how it is going to flow. By the way, I ran the same function here on that sentence passing '15' and was returned the following (using the existing code as is with floor, not ceil):
to to fiver 

1 four to to

four to to

to four to

tre tre to

seventy tre

to four tre

fiver tre to

four fiver

sixxer to

tre fiver to

four sixxer

basetenner

sixxer

eightter fiver tre seventy eightter

whoisgregg

5:58 pm on Sep 9, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Sorry for the delay in responding... :(

You will probably have to come up with some advanced formula to count words as well as word length and apply a different formula to your logic.

Which I think brings me full circle back to my original "spaghetti code" to solve this problem. Hmm, I think I'll look to managing expectations about this feature. :/