Reputation: 5495
Let's say I have this String
String myText="I think that stackoverflow is a very great website";
If i want to divide it in 2 lines i would have something like
I think that stackoverflow
is a very great website.
So the String will be now ("I think that stackoverflow\nis a very great website"
If I want it to divide in 3 lines it will be like
I think that
stackoverflow is a
very great website
What I've tried was just dividing the text, every line would have total number of words / n (n is the number of lines that i want to divide my text).
But this is a bad thing, i would have a result like
String myText="I me him is veryverylong wordvery longestwordever thisisevenlonger"
And the result would be (if i want to divide it in 2 lines) something like
"i you me is\nveryverylong wordvery longestwordever thisisevenlonger"
What do you guys suggest for me to try?
I've tried the common apache algorithm http://pastebin.com/68zycavf
But my output text will be every word separated by \n ..if i use wrap(text,2)..
Upvotes: 2
Views: 790
Reputation: 41127
As Eran noted in his answer, you want to split at approximately the line length divided by the desired number of lines, but have to adjust for that being in the middle of a word.
I think his solution won't quite always give the best solution though, as it might sometimes be best to split before the word instead of after as he's doing.
A divide-and-conquer approach would be a recursive algorithm roughly as follows:
Let N
be the desired number of lines and LENGTH
be the number of characters in the input string (normalizing to single-spaces first).
If the character at LENGTH/N
is a space, make the first cut there, and recursively call to split the remainder into N-1
lines, otherwise find the spaces at each end of the word containing this character and make trial cuts at both points with recursive calls again tom complete both cuts. Score the results somehow and choose the better.
I have implemented this as follows. For the scoring function, I chose to minimize the maximum length of lines in the split. A more complex scoring function might possibly improve the results, but this seems to work for all your cases.
public class WordWrapper {
public String wrapWords(String input, int lines) {
return splitWords(input.replaceAll("\\s+", " "), lines);
}
private String splitWords(String input, int lines) {
if (lines <= 1) {
return input;
}
int splitPointHigh = findSplit(input, lines, 1);
String splitHigh = input.substring(0, splitPointHigh).trim() + "\n" + splitWords(input.substring(splitPointHigh).trim(), lines - 1);
int splitPointLow = findSplit(input, lines, -1);
String splitLow = input.substring(0, splitPointLow).trim() + "\n" + splitWords(input.substring(splitPointLow).trim(), lines - 1);
if (maxLineLength(splitLow) < maxLineLength(splitHigh))
return splitLow;
else return splitHigh;
}
private int maxLineLength(String split) {
return maxLength(split.split("\n"));
}
private int maxLength(String[] lines) {
int maxLength = 0;
for (String line: lines) {
if (line.length() > maxLength)
maxLength = line.length();
}
return maxLength;
}
private int findSplit(String input, int lines, int dir) {
int result = input.length() / lines;
while (input.charAt(result) != ' ')
result+= dir;
return result;
}
}
I didn't actually bother with the special case of the lucky situation of the simple split landing on a space, and adding special handling for that might make it a little quicker. This code will in that case generate two identical "trial splits" and "choose one".
You might want to make all these methods static of course, and the recursion might give you a stack overflow for large inputs and large line counts.
I make no claim that this is the best algorithm, but it seems to work.
Upvotes: 4
Reputation: 137
This is my solution using the split() function.
public class Textcut {
public static void main(String arg[]) {
String myText="I think that stackoverflow is a very great website";
int n = 2;
String[] textSplit = myText.split(" ");
int wordNumber = textSplit.length;
int cutIndex = wordNumber/n;
int i = cutIndex;
int j = 0;
while(i <= wordNumber) {
for(; j < i; j++) {
System.out.print(textSplit[j] + " ");
}
System.out.println("\n");
i = i+cutIndex;
}
}
}
Upvotes: 1
Reputation: 393781
You can split based on the number of characters divided by n. Then, for each line, you should add the end of the last word (which is the beginning of the next line, if the current line doesn't end with a space and the next line doesn't begin with a space), so that no words are split in the middle.
So if you have :
I me him is veryverylong wordvery longestwordever thisisevenlonger
And you wish to split it to two lines, you get :
I me him is veryverylong wordvery
longestwordever thisisevenlonger
In this case the second line already starts with a space, so we know that no word was split in the middle, and we are done.
If you split it to three lines, you first get :
I me him is veryverylo
ng wordvery longestwor
dever thisisevenlonger
Here some words were split, so you move "ng" to the first line, and then move "dever" to the second line.
I me him is veryverylong
wordvery longestwordever
thisisevenlonger
Upvotes: 2