Как получить первые n символов строки без проверки размера или выхода за пределы?

163

Как мне получить первые nсимволы строки в Java, не выполнив сначала проверку размера (inline допускается) или не рискуя IndexOutOfBoundsException?

antony.trupe
источник
1
если вы не поймаете исключение, я не знаю, как вы планируете обрабатывать случай, когда длина символа больше, чем длина строки.
Мэтт Бём
2
Зачем? Каково ваше нежелание проверять длину или ловить исключение?
paxdiablo
1
Из любопытства, почему вы хотите избежать проверки размера. Это не С.
Том Хотин -
Я хотел выразить желание избежать блока if / else, а не отвращение к фактической проверке длины.
antony.trupe
потенциал Дубликат: stackoverflow.com/questions/8499698/...
Мульки

Ответы:

347

Вот изящное решение:

String upToNCharacters = s.substring(0, Math.min(s.length(), n));

Мнение: хотя это решение «аккуратное», я думаю, что оно на самом деле менее читабельно, чем решение, которое использует if/ elseочевидным образом. Если читатель не видел этот трюк, он должен подумать, чтобы понять код. ИМО, смысл кода более очевиден в if/ elseверсии. Более чистое / читабельное решение смотрите в ответе @ paxdiablo.

Стивен С
источник
1
+1. Еще лучше, если это обернуто в функцию с именем safe_substring или substring_safe, как ответ paxdiablo, так что использование легче читать / намереваться более очевидно.
ToolmakerSteve
Я не согласен с тем, что вы говорите. Если это обернуто в функцию, не имеет значения, что находится внутри функции , и любая «аккуратность» определенно перевешивается отсутствием ясности. Смысл этого решения в том, что оно «аккуратно» для случая, когда вы не хотите создавать функцию-оболочку.
Стивен С.
88

Не изобретай велосипед ...:

org.apache.commons.lang.StringUtils.substring(String s, int start, int len)

Javadoc говорит:

StringUtils.substring(null, *, *)    = null
StringUtils.substring("", * ,  *)    = "";
StringUtils.substring("abc", 0, 2)   = "ab"
StringUtils.substring("abc", 2, 0)   = ""
StringUtils.substring("abc", 2, 4)   = "c"
StringUtils.substring("abc", 4, 6)   = ""
StringUtils.substring("abc", 2, 2)   = ""
StringUtils.substring("abc", -2, -1) = "b"
StringUtils.substring("abc", -4, 2)  = "ab"

Таким образом:

StringUtils.substring("abc", 0, 4) = "abc"
Nickkk
источник
1
Он не отвечает на вопрос, но, тем не менее, он все же обеспечивает решение. Если ОП может понять, я думаю, что это лучшее решение.
аулла
5
Также может быть полезно указать, что StringUtils.substring(yourString, 0, n)это не то же самое, что yourString.substring(0, n). Первый из StringUtils, а последний использует String.substring(что дает исключение, если конечный индекс превышает длину строки).
ToolmakerSteve
Точно так же как FYI, если вы посмотрите в источнике для этого метода, он обрабатывает случай, когда конец больше, чем длина, изменяя конец на длину:if (end > str.length()) { end = str.length();}
bholl
1
Последний параметр StringUtils.substring(String s, int start, int len)не длинен, это конечный индекс.
gorootde
StringUtils.substring ("abc", 0, 4) = "abc", работает для меня. Спасибо !
Akash5288
42

У Apache Commons Lang есть StringUtils.leftметод для этого.

String upToNCharacters = StringUtils.left(s, n);
Скули
источник
Разве это не должно быть лучшим решением? Почему многие не голосуют за это?
До Уил
3
Может быть, потому что другие люди не имеют того же мнения, что и вы? :-)
Стивен С
этот ответ пришел намного позже, чем первоначальная дата вопроса.
Мульки
@DoWill: потому что добавление (другой) сторонней библиотеки в вашу исполняемую среду не всегда стоит.
LarsH
12

Есть класс вопросов по SO, который иногда имеет смысл не совсем идеальный, этот опасно близок :-)

Возможно, вы могли бы объяснить свое отвращение к использованию одного из двух методов, которые вы исключили.

Если это просто потому, что вы не хотите перетекать свой код ifзаявлениями или кодом перехвата исключений, одним из решений является использование вспомогательной функции, которая позаботится об этом за вас, что-то вроде:

static String substring_safe (String s, int start, int len) { ... }

который заранее проверит длину и будет действовать соответственно (вернет меньшую строку или пробел с пробелами).

Тогда вам не нужно беспокоиться об этом в вашем коде, просто позвоните:

String s2 = substring_safe (s, 10, 7);

вместо того:

String s2 = s.substring (10,7);

Это сработает в том случае, если вы, кажется, беспокоитесь (основываясь на ваших комментариях к другим ответам), не нарушая поток кода при выполнении большого количества работ по построению строк.

paxdiablo
источник
1
Вы должны прочитать комментарий более внимательно, @antony, особенно смайлик, и не стоит так ценить тех, кто пытается помочь. Я просто заявлял, что вы не дали никакого объяснения тому, почему вы должны избегать этих двух методов. И это настоящий ответ, использующий вспомогательную функцию, поэтому его нет в комментарии.
paxdiablo
1
+1: Это НАМНОГО лучший подход, чем принятый, учитывая желание ОП не загромождать код. (или посмотрите решение Никка о включении библиотеки, которая уже имеет функцию, которая ведет себя так, как нужно.)
ToolmakerSteve
12
String upToNCharacters = String.format("%."+ n +"s", str);

Ужасно, если nэто переменная (поэтому вы должны создать строку формата), но довольно ясно, если константа:

String upToNCharacters = String.format("%.10s", str);

документы

13ren
источник
Интересная альтернатива, хотя я не могу себе представить ее использование, учитывая более традиционные подходы, которые были даны четыре года назад.
ToolmakerSteve
Лучший ответ, потому что входная строка читается только один раз, поэтому нет необходимости хранить ее в переменной, что позволяет аккуратно встраивать ее.
Профитроли
3

Используйте метод подстроки следующим образом:

int n = 8;
String s = "Hello, World!";
System.out.println(s.substring(0,n);

Если n больше длины строки, это вызовет исключение, как указал один из комментариев. Одно простое решение - обернуть все это в условие if(s.length()<n)в вашем elseпредложении, вы можете выбрать, хотите ли вы просто напечатать / вернуть всю строку или обработать ее другим способом.

Мэтт Бём
источник
1
это рискует получить
исключение
Кстати, если вы планируете программировать на Java, постарайтесь запомнить большинство методов API для String ( java.sun.com/j2se/1.5.0/docs/api/java/lang/String.html ).
Мэтт Бём
Я уже исключил подстроку, по крайней мере, сам по себе, как не ответ.
antony.trupe
Вы должны либо проверить размер, либо поймать исключение. Могу я спросить, почему выполнение любого из них не сработает в вашей ситуации?
Мэтт Бём
3
Как это ответ на вопрос? Вопрос заключался в том, чтобы не проверять размер в первую очередь и не вызывать исключение, которое необходимо перехватить.
ToolmakerSteve
3

Если вам повезло в разработке с Kotlin,
вы можете использовать takeдля достижения своей цели.

val someString = "hello"

someString.take(10) // result is "hello"
someString.take(4) // result is "hell" )))
Лев Droidcoder
источник
0

ApacheCommons удивил меня, StringUtils.abbreviate(String str, int maxWidth)добавляет "..." нет возможности изменить постфикс. WordUtils.abbreviate(String str, int lower, int upper, String appendToEnd)смотрит на следующее пустое место.

Я просто собираюсь оставить это здесь:

public static String abbreviate(String s, int maxLength, String appendToEnd) {
    String result = s;
    appendToEnd = appendToEnd == null ? "" : appendToEnd;
    if (maxLength >= appendToEnd.length()) {
        if (s.length()>maxLength) {
            result = s.substring(0, Math.min(s.length(), maxLength - appendToEnd.length())) + appendToEnd;
        }
    } else {
        throw new StringIndexOutOfBoundsException("maxLength can not be smaller than appendToEnd parameter length.");
    }
    return result;
}
yuceel
источник
1
@ VolkanGüven Это из-за этого предложения "ApacheCommons удивил меня". Я совершил грех, критикуя священную библиотеку ApacheCommons. Или что угодно ...
Yuceel
0

Котлин: (если кому-то нужно)

var mText = text.substring(0, text.length.coerceAtMost(20))
Touhid
источник