views:

2659

answers:

5

I have a large block of text (200+ characters in a String) and need to insert new lines at the next space after 30 characters, to preserve words. Here is what I have now (NOT working):

String rawInfo = front.getItemInfo(name);
String info = "";
int begin = 0;
for(int l=30;(l+30)<rawInfo.length();l+=30) {
    while(rawInfo.charAt(l)!=' ')
        l++;
    info += rawInfo.substring(begin, l) + "\n";
    begin = l+1;
    if((l+30)>=(rawInfo.length()))
        info += rawInfo.substring(begin, rawInfo.length());
}

Thanks for any help

+2  A: 

A better solution: copy the string into a StringBuilder so that you can insert / change characters without a lot of substring mucking. Then use the indexOf() method that takes a starting position to find the index to change.

Edit: teh codez:

public static String breakString(String str, int size)
{
    StringBuilder work = new StringBuilder(str);
    int pos = 0;
    while ((pos = work.indexOf(" ", pos + size)) >= 0)
    {
        work.setCharAt(pos, '\n');
    }
    return work.toString();
}
kdgregory
I think if you don't increment pos inside the while loop you will accidentally replace every space with \n, causing all lines after the first to have one word. You need to bump up pos by 30 after each substitution.
Karl
look more closely at the while expression -- it updates pos
kdgregory
+4  A: 

As suggested by kdgregory, using a StringBuilder would probably be an easier way to work with string manipulation.

Since I wasn't quite sure if the number of characters before the newline is inserted is the word before or after 30 characters, I opted to go for the word after 30 characters, as the implementation is probably easier.

The approach is to find the instance of the " " which occurs at least 30 characters after the current character which is being viewed by using StringBuilder.indexOf. When a space occurs, a \n is inserted by StringBuilder.insert.

(We'll assume that a newline is \n here -- the actual line separator used in the current environment can be retrieved by System.getProperty("line.separator");).

Here's the example:

String s = "A very long string containing " +
    "many many words and characters. " +
    "Newlines will be entered at spaces.";

StringBuilder sb = new StringBuilder(s);

int i = 0;
while ((i = sb.indexOf(" ", i + 30)) != -1) {
    sb.replace(i, i + 1, "\n");
}

System.out.println(sb.toString());

Result:

A very long string containing many
many words and characters. Newlines
will.

I should add that the code above hasn't been tested out, except for the example String that I've shown in the code. It wouldn't be too surprising if it didn't work under certain circumstances.

Edit

The loop in the sample code has been replaced by a while loop rather than a for loop which wasn't very appropriate in this example.

Also, the StringBuilder.insert method was replaced by the StringBuilder.replace method, as Kevin Stich mentioned in the comments that the replace method was used rather than the insert to get the desired behavior.

coobird
This method worked perfectly, just changed sb.insert(i, "\n");to sb.replace(i, i+1, "\n");Thanks!
Kevin Stich
@Kevin Stich: I've updated the sample code -- the for loop wasn't very appropriate in this example, so it was replaced with a while loop.
coobird
+1  A: 

I would iterate over the string instead of the 30. You have to track the 30 though.

psuedocode because this sounds like homework:

charInLine=0
iterateOver each char in rawString
    if(charInLine++ > 30 && currChar==' ')
        charInLine=0
        currChar='\n'
Bill K
+1  A: 

Here's a test-driven solution.

import junit.framework.TestCase;

public class InsertLinebreaksTest extends TestCase {
    public void testEmptyString() throws Exception {
     assertEquals("", insertLinebreaks("", 5));
    }

    public void testShortString() throws Exception {
     assertEquals("abc def", insertLinebreaks("abc def", 5));
    }

    public void testLongString() throws Exception {
     assertEquals("abc\ndef\nghi", insertLinebreaks("abc def ghi", 1));
     assertEquals("abc\ndef\nghi", insertLinebreaks("abc def ghi", 2));
     assertEquals("abc\ndef\nghi", insertLinebreaks("abc def ghi", 3));
     assertEquals("abc def\nghi", insertLinebreaks("abc def ghi", 4));
     assertEquals("abc def\nghi", insertLinebreaks("abc def ghi", 5));
     assertEquals("abc def\nghi", insertLinebreaks("abc def ghi", 6));
     assertEquals("abc def\nghi", insertLinebreaks("abc def ghi", 7));
     assertEquals("abc def ghi", insertLinebreaks("abc def ghi", 8));
    }

    public static String insertLinebreaks(String s, int charsPerLine) {
     char[] chars = s.toCharArray();
     int lastLinebreak = 0;
     boolean wantLinebreak = false;
     StringBuffer sb = new StringBuffer();
     for (int i = 0; i < chars.length; i++) {
      if (wantLinebreak && chars[i] == ' ') {
       sb.append('\n');
       lastLinebreak = i;
       wantLinebreak = false;
      } else {
       sb.append(chars[i]);
      }
      if (i - lastLinebreak + 1 == charsPerLine)
       wantLinebreak = true;
     }
     return sb.toString();
    }
}
Carl Manaster
+1, adding a test to your answer is a great and "civic" idea
dfa
Thanks! I like to write code test-first, and like to promote the practice; I think I'll be writing more answers this way. I appreciate the encouragement.
Carl Manaster
A: 

Maybe I am missing something. Whats wrong with

String s = ... s = s.substring(0, 30) + s.substring(30).replace(' ', '\n');

This replaces all spaces with newlines starting after character 30.

Its slightly less efficient but for 200 characters it really doesn't matter (that is extremely small for data manipulation).

Bruce

Then every word after the 30th character would be in its own line.
Michael Myers