Can I wrap text to a given width with Guava?

I would like to be able to wrap a long String to a fixed length. Is there a way to do that in Guava?

Apache Commons / Lang has the method WordUtils.wrap(String, length) that does exactly what I need. Does Guava have a simple means to accomplish this?

I know I can do a hard wrap using Splitter.fixedLength(int) , but I would like a soft wrap.


UPDATE: There is now a bounty for this question.

Obviously this functionality isn't available in Guava out of the Box, so the bounty goes to the most concise (or most complete) and Guava-like answer that uses what's there in Guava. No libs except Guava allowed.


我们(Guava)强烈建议您使用ICU4J的BreakIterator类来处理在用户文本中查找断点的机制。


Here's my own answer, for inspiration:

public final class TextWrapper {

    enum Strategy implements WrapStrategy {
        HARD {

            @Override
            public String wrap(final Iterable<String> words, final int width) {
                return Joiner.on('n')
                             .join(Splitter
                                    .fixedLength(width)
                                    .split(
                                        Joiner.on(' ').join(words)));
            }
        },
        SOFT {
            @Override
            public String wrap(final Iterable<String> words, final int width) {
                final StringBuilder sb = new StringBuilder();
                int lineLength = 0;
                final Iterator<String> iterator = words.iterator();
                if (iterator.hasNext()) {
                    sb.append(iterator.next());
                    lineLength=sb.length();
                    while (iterator.hasNext()) {
                        final String word = iterator.next();
                        if(word.length()+1+lineLength>width) {
                            sb.append('n');
                            lineLength=0;
                        } else {
                            lineLength++;
                            sb.append(' ');
                        }
                        sb.append(word);
                        lineLength+=word.length();
                    }
                }
                return sb.toString();
            }
        }
    }

    interface WrapStrategy {
        String wrap(Iterable<String> words, int width);
    }

    public static TextWrapper forWidth(final int i) {
        return new TextWrapper(Strategy.SOFT, CharMatcher.WHITESPACE, i);
    }

    private final WrapStrategy  strategy;

    private final CharMatcher   delimiter;

    private final int           width;

    TextWrapper(final WrapStrategy strategy,
                final CharMatcher delimiter, final int width) {
        this.strategy = strategy;
        this.delimiter = delimiter;
        this.width = width;
    }

    public TextWrapper hard(){
        return new TextWrapper(Strategy.HARD, this.delimiter, this.width);
    }
    public TextWrapper respectExistingBreaks() {
        return new TextWrapper(
            this.strategy, CharMatcher.anyOf(" t"), this.width);
    }

    public String wrap(final String text) {
        return this.strategy.wrap(
            Splitter.on(this.delimiter).split(text), this.width);
    }

}

Sample Usage 1: (hard wrapping at 80 chars)

TextWrapper.forWidth(80)
        .hard()
        .wrap("Lorem ipsum dolor sit amet, consectetur adipiscing elit.n" +
            "Maecenas porttitor risus vitae urna hendrerit ac condimentum " +
            "odio tincidunt.nDonec porttitor felis quis nulla aliquet " +
            "lobortis. Suspendisse mattis sapien ut metus congue tincidunt. " +
            "Quisque gravida, augue sed congue tempor, tortor augue rhoncus " +
            "leo, eget luctus nisl risus id erat. Nunc tempor pretium gravida.");

Output:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas porttitor risu
s vitae urna hendrerit ac condimentum odio tincidunt. Donec porttitor felis quis
 nulla aliquet lobortis. Suspendisse mattis sapien ut metus congue tincidunt. Qu
isque gravida, augue sed congue tempor, tortor augue rhoncus leo, eget luctus ni
sl risus id erat. Nunc tempor pretium gravida.

Sample Usage 2: (soft wrapping at or or before 60 chars, keep existing line breaks)

TextWrapper.forWidth(60)
    .respectExistingBreaks()
    .wrap("Lorem ipsum dolor sit amet, consectetur adipiscing elit.n" +
    "Maecenas porttitor risus vitae urna hendrerit ac condimentum " +
    "odio tincidunt.nDonec porttitor felis quis nulla aliquet " +
    "lobortis. Suspendisse mattis sapien ut metus congue tincidunt. " +
    "Quisque gravida, augue sed congue tempor, tortor augue rhoncus " +
    "leo, eget luctus nisl risus id erat. Nunc tempor pretium gravida.");

Output:

Lorem ipsum dolor sit amet, consectetur adipiscing
elit.
Maecenas porttitor risus vitae urna hendrerit ac
condimentum odio tincidunt.
Donec porttitor felis quis nulla
aliquet lobortis. Suspendisse mattis sapien ut metus congue
tincidunt. Quisque gravida, augue sed congue tempor, tortor
augue rhoncus leo, eget luctus nisl risus id erat. Nunc
tempor pretium gravida.

why use guava to do something more simple without guava?

In fact, the Splitter class allows you to do an hard wrap using fixedLength() method, otherwise you can split a string depending on a separator char or String . If you want to use guava, you can rely on Splitter.on(' ').split(string) , but you have also to join the results replacing ' ' with 'n' depending on maxLength value.

Without using guava, you can also do what you want. A few lines of code, with no dependencies . Basically, you can use the commons-lang approach, simplifying it. This is my wrap method:

public static String wrap(String str, int wrapLength) {
    int offset = 0;
    StringBuilder resultBuilder = new StringBuilder();

    while ((str.length() - offset) > wrapLength) {
        if (str.charAt(offset) == ' ') {
            offset++;
            continue;
        }

        int spaceToWrapAt = str.lastIndexOf(' ', wrapLength + offset);
        // if the next string with length maxLength doesn't contain ' '
        if (spaceToWrapAt < offset) {
            spaceToWrapAt = str.indexOf(' ', wrapLength + offset);
            // if no more ' '
            if (spaceToWrapAt < 0) {
                break;
            }
        }

        resultBuilder.append(str.substring(offset, spaceToWrapAt));
        resultBuilder.append("n");
        offset = spaceToWrapAt + 1;
    }

    resultBuilder.append(str.substring(offset));
    return resultBuilder.toString();
}

Yes, it's very similar to the original commons-lang method, but shorter, easier and based on your needs, I guess. Maybe, this solution is also more efficient than yours, isn't it?

I've tested it with your text, comparing my result with commons-lang result. It seems to work:

public static void main(String[] args) {

    String string = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.n"
            + "Maecenas porttitor risus vitae urna hendrerit ac condimentum "
            + "odio tincidunt.nDonec porttitor felis quis nulla aliquet "
            + "lobortis. Suspendisse mattis sapien ut metus congue tincidunt. "
            + "Quisque gravida, augue sed congue tempor, tortor augue rhoncus "
            + "leo, eget luctus nisl risus id erat. Nunc tempor pretium gravida.";

    for (int maxLength = 2; maxLength < string.length(); maxLength++) {
        String expectedResult = WordUtils.wrap(string, maxLength);
        String actualResult = wrap(string, maxLength);

        if (!expectedResult.equals(actualResult)) {
            System.out.println("expectedResult: n" + expectedResult);
            System.out.println("nactualResult: n" + actualResult);
            throw new RuntimeException(
                    "actualResult is not the same as expectedResult (maxLength:"
                            + maxLength + ")");
        }
    }
}

So, the matter is: do you really want to use guava to do this? What are the benefits related to this choice?

链接地址: http://www.djcxy.com/p/64112.html

上一篇: 在makefile中,声明伪造目标为通配符

下一篇: 我可以用番石榴包装文字到一个给定的宽度吗?