euler
euler

Reputation: 1411

How to abbreviate string at the middle without cutting words

I am using StringUtils.abbreviateMiddle(string, middle, targetLength); to abbreviate string in the middle but using this method cuts off the word. Is there a way such that it will only cut off string at the space nearest to the middle position of the string?

public static String shortenStringMiddle(String string, String middle, int targetLength) {
    targetLength = Math.abs(targetLength);

    if (string != null && string.length() > targetLength) {

        return StringUtils.abbreviateMiddle(string, middle, targetLength);
    }
    else {
        return string;
    }
}

Input if applied to this text:

Disease Control in Fish and Shrimp Aquaculture in Southeast Asia - Diagnosis and Husbandry Techniques: Proceedings of the SEAFDEC-OIE Seminar-Workshop on Disease Control in Fish and Shrimp Aquaculture in Southeast Asia - Diagnosis and Husbandry Techniques, 4-6 December 2001, Iloilo City, Philippines

System.out.println(StringUtils.abbreviateMiddle("Disease Control in Fish and Shrimp Aquaculture in Southeast Asia - Diagnosis and Husbandry Techniques: Proceedings of the SEAFDEC-OIE Seminar-Workshop on Disease Control in Fish and Shrimp Aquaculture in Southeast Asia - Diagnosis and Husbandry Techniques, 4-6 December 2001, Iloilo City, Philippines", " … ", 220));

Output:

Disease Control in Fish and Shrimp Aquaculture in Southeast Asia - Diagnosis and Husbandry Techniques: Proceeaculture in Southeast Asia - Diagnosis and Husbandry Techniques, 4-6 December 2001, Iloilo City, Philippines

The word Proceedings was cut off to Procee and Aquaculture to aculture

My ideal output would be:

Disease Control in Fish and Shrimp Aquaculture in Southeast Asia - Diagnosis and Husbandry Techniques: Proceedings … Aquaculture in Southeast Asia - Diagnosis and Husbandry Techniques, 4-6 December 2001, Iloilo City, Philippines

I've searched here in SO and similar questions only relates to abbreviate string adding an ellipsis at the end.

Upvotes: 7

Views: 3191

Answers (4)

Robby Cornelissen
Robby Cornelissen

Reputation: 97381

Here's another implementation based on a regular expression:

public static String abbreviateMiddle(String input, String middle, int length) {
    if (input != null && input.length() > length) {
        int half = (length - middle.length()) / 2;

        Pattern pattern = Pattern.compile(
                "^(.{" + half + ",}?)" + "\\b.*\\b" + "(.{" + half + ",}?)$");
        Matcher matcher = pattern.matcher(input);

        if (matcher.matches()) {
            return matcher.group(1) + middle + matcher.group(2);
        }
    }

    return input;
}

With your example text, the following call

System.out.println(abbreviateMiddle(exampleText, " ... ", 220));

will produce

Disease Control in Fish and Shrimp Aquaculture in Southeast Asia - Diagnosis and Husbandry Techniques: Proceedings ... Aquaculture in Southeast Asia - Diagnosis and Husbandry Techniques, 4-6 December 2001, Iloilo City, Philippines

Upvotes: 3

M. Shaw
M. Shaw

Reputation: 1742

Try this:

public String abbreviateMiddle(String string, String middle, int targetLength) {
    String[] ssplit = string.split(" ");
    String first = "";
    String second = "";
    for (int i = 0; i < ssplit.length; i++) {
        if (first.length() < targetLength / 2) {
            first += ssplit[i] + " ";
        } else {
            break;
        }
    }
    for (int i = ssplit.length - 1; i >= 0; i--) {
        if (second.length() < targetLength / 2) {
            second = ssplit[i] + " " + second;
        } else {
            break;
        }
    }

    return first + middle + second;
}

If you want the result string to be maximum length but strictly less than targetLength (rather than the above code which returns the minimum string that has a length GREATER than targetLength), use

if (first.length() + ssplit[i].length() < targetLength /2)

and

if (second.length() + ssplit[i].length() < targetLength / 2)

instead.

Upvotes: 1

Robby Cornelissen
Robby Cornelissen

Reputation: 97381

Here's a rough shot at it. Obviously you could implement cleaner fallback methods for cases where the string doesn't contain sufficient spaces etc., but as a base to start from, it should do just fine.

public static String abbreviateMiddle(String input, String middle, int targetLength) {
    if (input == null || input.length() <= targetLength) {
        return input;
    }

    int inputLength = input.length();
    int halfTargetLength = (targetLength - middle.length()) / 2;

    int startLastSpace = input.substring(0, halfTargetLength).lastIndexOf(" ");
    int endFirstSpace = input.indexOf(" ", inputLength - halfTargetLength);

    if (startLastSpace != -1 || endFirstSpace != -1) {
        return input.substring(0, startLastSpace)
                + middle
                + input.substring(endFirstSpace + 1, inputLength);
    }

    return input;
}

With your example text, the following call

System.out.println(abbreviateMiddle(exampleText, " ... ", 240));

will return

Disease Control in Fish and Shrimp Aquaculture in Southeast Asia - Diagnosis and Husbandry Techniques: Proceedings ... Aquaculture in Southeast Asia - Diagnosis and Husbandry Techniques, 4-6 December 2001, Iloilo City, Philippines

Setting the targetLength to 220 will return

Disease Control in Fish and Shrimp Aquaculture in Southeast Asia - Diagnosis and Husbandry Techniques: ... in Southeast Asia - Diagnosis and Husbandry Techniques, 4-6 December 2001, Iloilo City, Philippines

Upvotes: 3

Max von Hippel
Max von Hippel

Reputation: 2970

To elaborate on Vince's comment, you could use indexOf(" ") since a space will represent the end of a word (and thus you will not cut off a word). I would look through, find the number of instances of " " in your input, then estimate how many words to put on either end in order to be at or below your char limit (from the targetLength in your method) and then get the substrings after and below your elected instances of " ". I don't know java so I can't write that code for you though, sorry.

Upvotes: 0

Related Questions