Wrapping a String After a Number of Characters Word-Wise

1. Overview

In this tutorial, we’ll see how to wrap a sentence automatically after a given number of characters. Hence, our program will return a transformed String with new line breaks.

2. General Algorithm

Let’s consider the following sentence: Baeldung is a popular website that provides in-depth tutorials and articles on various programming and software development topics, primarily focused on Java and related technologies.

We want to insert line returns every n characters maximum, n representing the number of characters. Let’s see the code to do this:

String wrapStringCharacterWise(String input, int n) {      
    StringBuilder stringBuilder = new StringBuilder(input);
    int index = 0;
    while(stringBuilder.length() > index + n) {
        index = stringBuilder.lastIndexOf(" ", index + n);    
        stringBuilder.replace(index, index + 1, "\n");
        index++; 
    }
    return stringBuilder.toString();
}

Let’s take n=20 and understand our example code:

  • we start with finding the latest whitespace before 20 characters: in this case, between the words a and popular
  • then we replace this whitespace with a line return
  • and we start again from the beginning of the next word, popular in our example

We stop the algorithm when the remaining sentence has less than 20 characters. We naturally implement this algorithm via a for loop. Besides, we used a StringBuilder internally for convenience, and parameterized our inputs:

We can write a unit test to confirm that our method returns the expected result for our example:

@Test
void givenStringWithMoreThanNCharacters_whenWrapStringCharacterWise_thenCorrectlyWrapped() {
    String input = "Baeldung is a popular website that provides in-depth tutorials and articles on various programming and software development topics, primarily focused on Java and related technologies.";
    assertEquals("Baeldung is a\npopular website that\nprovides in-depth\ntutorials and\narticles on various\nprogramming and\nsoftware development\ntopics, primarily\nfocused on Java and\nrelated\ntechnologies.", wrapper.wrapStringCharacterWise(input, 20));
}

3. Edge Cases

For now, we’ve written a very naive code. In a real-life use case, we might need to take into account some edge cases. Within this article, we’ll address two of them.

3.1. Words Longer Than the Character Limit

First, what if a word is too large and is impossible to wrap? For simplicity, let’s throw an IllegalArgumentException in this case. At every iteration of our loop, we need to check that there’s indeed a whitespace before the given length:

String wrapStringCharacterWise(String input, int n) {      
    StringBuilder stringBuilder = new StringBuilder(input);
    int index = 0;
    while(stringBuilder.length() > index + n) {
        index = stringBuilder.lastIndexOf(" ", index + n);
        if (index == -1) {
            throw new IllegalArgumentException("impossible to slice " + stringBuilder.substring(0, n));
        }       
        stringBuilder.replace(index, index + 1, "\n");
        index++; 
    }
    return stringBuilder.toString();
}

This time again, we can write a simple JUnit test for validation:

@Test
void givenStringWithATooLongWord_whenWrapStringCharacterWise_thenThrows() {
    String input = "The word straightforward has more than 10 characters";
    assertThrows(IllegalArgumentException.class, () -> wrapper.wrapStringCharacterWise(input, 10));
}

3.2. Original Input With Line Returns

Another edge case is when the input String already has line return characters inside. For the moment, if we add a line return after the word Baeldung in our sentence, it will be wrapped identically. However, it sounds more intuitive to start wrapping after the existing line returns.

For this reason, we’ll search for the last line return at every iteration of our algorithm; if it exists, we move the cursor and skip the wrapping part:

String wrapStringCharacterWise(String input, int n) {      
    StringBuilder stringBuilder = new StringBuilder(input);
    int index = 0;
    while(stringBuilder.length() > index + n) {
        int lastLineReturn = stringBuilder.lastIndexOf("\n", index + n);
        if (lastLineReturn > index) {
            index = lastLineReturn;
        } else {
            index = stringBuilder.lastIndexOf(" ", index + n);
            if (index == -1) {
                throw new IllegalArgumentException("impossible to slice " + stringBuilder.substring(0, n));
            }       
            stringBuilder.replace(index, index + 1, "\n");
            index++;
        }    
    }
    return stringBuilder.toString();
}

Again, we can test our code on our example:

@Test
void givenStringWithLineReturns_whenWrapStringCharacterWise_thenWrappedAccordingly() {
    String input = "Baeldung\nis a popular website that provides in-depth tutorials and articles on various programming and software development topics, primarily focused on Java and related technologies.";
    assertEquals("Baeldung\nis a popular\nwebsite that\nprovides in-depth\ntutorials and\narticles on various\nprogramming and\nsoftware development\ntopics, primarily\nfocused on Java and\nrelated\ntechnologies.", wrapper.wrapStringCharacterWise(input, 20));
}

4. Apache WordUtils wrap() Method

We can use Apache WordUtils wrap() method to implement the required behavior. First, let’s add the latest Apache commons-text dependency:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-text</artifactId>
    <version>1.10.0</version>
</dependency>

The main difference with our code is that wrap() uses the platform-independent System‘s line separator by default:

@Test
void givenStringWithMoreThanNCharacters_whenWrap_thenCorrectlyWrapped() {
    String input = "Baeldung is a popular website that provides in-depth tutorials and articles on various programming and software development topics, primarily focused on Java and related technologies.";
    assertEquals("Baeldung is a" + System.lineSeparator() + "popular website that" + System.lineSeparator() + "provides in-depth" + System.lineSeparator() + "tutorials and" + System.lineSeparator() + "articles on various" + System.lineSeparator() + "programming and" + System.lineSeparator() + "software development" + System.lineSeparator() + "topics, primarily" + System.lineSeparator() + "focused on Java and" + System.lineSeparator() + "related" + System.lineSeparator() + "technologies.", WordUtils.wrap(input, 20));
}

By default, wrap() accepts long words but doesn’t wrap them:

@Test
void givenStringWithATooLongWord_whenWrap_thenLongWordIsNotWrapped() {
    String input = "The word straightforward has more than 10 characters";
    assertEquals("The word" + System.lineSeparator() + "straightforward" + System.lineSeparator() + "has more" + System.lineSeparator() + "than 10" + System.lineSeparator() + "characters", WordUtils.wrap(input, 10));
}

Last but not least, our other edge case is ignored by this library:

@Test
void givenStringWithLineReturns_whenWrap_thenWrappedLikeThereWasNone() {
    String input = "Baeldung" + System.lineSeparator() + "is a popular website that provides in-depth tutorials and articles on various programming and software development topics, primarily focused on Java and related technologies.";
    assertEquals("Baeldung" + System.lineSeparator() + "is a" + System.lineSeparator() + "popular website that" + System.lineSeparator() + "provides in-depth" + System.lineSeparator() + "tutorials and" + System.lineSeparator() + "articles on various" + System.lineSeparator() + "programming and" + System.lineSeparator() + "software development" + System.lineSeparator() + "topics, primarily" + System.lineSeparator() + "focused on Java and" + System.lineSeparator() + "related" + System.lineSeparator() + "technologies.", WordUtils.wrap(input, 20));
}

To conclude, we can have a look at the overloaded signature of the method:

static String wrap(final String str, int wrapLength, String newLineStr, final boolean wrapLongWords, String wrapOn)

We notice the additional parameters:

  • newLineStr: to use a different character for new line insertion
  • wrapLongWords: a boolean to decide whether to wrap long words or not
  • wrapOn: any regular expression can be used instead of whitespaces

5. Conclusion

In this article, we saw an algorithm to wrap a String after a given number of characters. We implemented it and added the support for a couple of edge cases.

Lastly, we realized that Apache WordUtils’ wrap() method is highly configurable and should suffice in most cases. However, if we can’t use the external dependency or need specific behaviours, we can use our own implementation.

As always, the code is available over on GitHub.

       

Commercials Cooperation Advertisements:


(1) IT Teacher IT Freelance

IT電腦補習

立刻註冊及報名電腦補習課程吧!
电子计算机 -教育 -IT 電腦班” ( IT電腦補習 ) 提供一個方便的电子计算机 教育平台, 為大家配對信息技术, 電腦 老師, IT freelance 和 programming expert. 讓大家方便地就能找到合適的電腦補習, 電腦班, 家教, 私人老師.
We are a education and information platform which you can find a IT private tutorial teacher or freelance.
Also we provide different information about information technology, Computer, programming, mobile, Android, apple, game, movie, anime, animation…


(2) ITSec

https://itsec.vip/

www.ITSec.vip

www.Sraa.com.hk

www.ITSec.hk

www.Penetrationtest.hk

www.ITSeceu.uk

Secure Your Computers from Cyber Threats and mitigate risks with professional services to defend Hackers.

ITSec provide IT Security and Compliance Services, including IT Compliance Services, Risk Assessment, IT Audit, Security Assessment and Audit, ISO 27001 Consulting and Certification, GDPR Compliance Services, Privacy Impact Assessment (PIA), Penetration test, Ethical Hacking, Vulnerabilities scan, IT Consulting, Data Privacy Consulting, Data Protection Services, Information Security Consulting, Cyber Security Consulting, Network Security Audit, Security Awareness Training.

Contact us right away.

Email (Prefer using email to contact us):
SalesExecutive@ITSec.vip

Leave a Reply

Your email address will not be published. Required fields are marked *