Using regex to hanging indent a paragraph in Java
This post shows how to hanging indent a long paragraph using regular expression. The method will consider word boundaries, which means it will not break words for the indentation. To illustrate the problem, consider the following example:
There has been an increasing effort in recent years to extract relations between entities from natural language text. In this dissertation, I will focus on various aspects of recognizing biomedical relations between entities reported in scientific articles.
The output should be:
There has been an increasing effort in recent years to extract relations between entities from natural language text. In this dissertation, I will focus on various aspects of recognizing biomedical relations between entities reported in scientific articles.
My method
We need a regular expression to break the paragraph into a sequence of strings with fixed length. Suppose the text width is 80 and the indent is 3, the length of first string is 80. All remainders’ length is 77.
The main process of the algorithm is following
- Get the first 80 characters
- For the remaining strings, replace splitting points with three spaces
To find the splitting points, we use the regular expression (.{1,77})\s+
. This regex searches a substring whose length is less and equal to 77 and whose last char is not a white space. After finding it, we replace the group ($1
) with $1\n
. Therefore, the java code should look like this
String regex = "(.{1,77})\\s+"; String replacement = " $1\n"; text.replaceAll(regex, replacement);
This regex works perfect except for the last line. If the given text doesn’t end with a whitespace, like \n
, the last line will not be handled correctly. Consider the last line as
in scientific articles.
In the last search, the regex cannot find the whitespace at the end of the line, so it will locate the space between “scientific” and “articles”. As the result, we will get
... in scientific articles.
To overcome this problem, I add a fake “\n” at the end of the paragraph. After formatting, I remove it then.
Other part of the code are trivial. Here I attach my source code. I use Apache common libraries to generate indent spaces and assert the validation of indent. For more recent codes, you can check my Github
/** * Format a paragraph to that has all lines but the first indented. * * @param text text to be formatted * @param hangIndent hanging indentation. hangIndent >= 0 * @param width the width of formatted paragraph * @param considerSpace true if only split at white spaces. * @return */ public static String hangIndent(String text, int hangIndent, int width, boolean considerSpace) { Validate.isTrue( hangIndent >= 0, "hangIndent should not be negative: %d", hangIndent); Validate.isTrue(width >= 0, "text width should not be negative: %d", width); Validate.isTrue( hangIndent < width, "hangIndent should not be less than width: " + "hangIndent=%d, width=%d", hangIndent, width); StringBuilder sb = new StringBuilder(text.substring(0, hangIndent)); // Needed to handle last line correctly. // Will be trimmed at last text = text.substring(hangIndent) + "\n"; // hang indent String spaces = org.apache.commons.lang3.StringUtils .repeat(' ', hangIndent); String replacement = spaces + "$1\n"; String regex = "(.{1," + (width - hangIndent) + "})"; if (considerSpace) { regex += "\\s+"; } text = text.replaceAll(regex, replacement); // remove first spaces and last "\n" text = text.substring(hangIndent, text.length() - 1); return sb.append(text).toString(); }
Related work
There are many other ways to implement the hanging indentation function. The simplest appears to be first breaking the paragraph into words, then using a counter to calculate the length of current line. Whenever it will exceed the max length, we add a newline and the indent.
I am not sure which way is more efficient, but absolutely the non-regular-expression way is easier to implement and maintain. So I guess the main point of this post is to learn something “NEW“.