Jak uzyskać pierwsze n znaków ciągu bez sprawdzania rozmiaru lub wychodzenia poza granice?

163

Jak uzyskać dostęp do pierwszych nznaków ciągu w Javie bez sprawdzania rozmiaru (inline jest dopuszczalne) lub ryzykowania IndexOutOfBoundsException?

antony.trupe
źródło
1
chyba że złapiesz wyjątek, nie wiem, jak zamierzasz postąpić w przypadku, gdy długość znaku jest większa niż długość ciągu.
Matt Boehm
2
Czemu? Jaka jest Twoja niechęć do sprawdzania długości lub łapania wyjątków?
paxdiablo
1
Z ciekawości, dlaczego chcesz uniknąć sprawdzania rozmiaru. To nie jest C.
Tom Hawtin - tackline
chciałem wyrazić chęć uniknięcia bloku if / else, a nie niechęć do sprawdzania długości.
antony.trupe
potencjalny duplikat: stackoverflow.com/questions/8499698/…
Mulki

Odpowiedzi:

347

Oto zgrabne rozwiązanie:

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

Opinia: chociaż to rozwiązanie jest „zgrabne”, myślę, że jest w rzeczywistości mniej czytelne niż rozwiązanie, które wykorzystuje if/ elsew oczywisty sposób. Jeśli czytelnik nie widział tej sztuczki, musi się zastanowić, aby zrozumieć kod. IMO, znaczenie kodu jest bardziej oczywiste w wersji if/ else. Aby uzyskać czystsze / bardziej czytelne rozwiązanie, zobacz odpowiedź @ paxdiablo.

Stephen C.
źródło
1
+1. Jeszcze lepiej, jeśli jest to opakowane w funkcję o nazwie safe_substring lub substring_safe, taką jak odpowiedź paxdiablo, dzięki czemu użycie jest łatwiejsze do odczytania / intencja bardziej oczywista.
ToolmakerSteve
Nie zgadzam się z tym, co mówisz. Jeśli jest to opakowane w funkcję, nie ma znaczenia, co znajduje się wewnątrz funkcji , a wszelki „porządek” jest zdecydowanie przeważony przez brak przejrzystości. Celem tego rozwiązania jest to, że jest „zgrabne” w przypadku, gdy nie chcesz tworzyć funkcji opakowującej.
Stephen C
88

Nie wynajduj koła na nowo ...:

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

Javadoc mówi:

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"

A zatem:

StringUtils.substring("abc", 0, 4) = "abc"
Nickkk
źródło
1
Nie odpowiada na pytanie, ale mimo wszystko nadal dostarcza rozwiązania. Jeśli PO jest w stanie to zrozumieć, myślę, że jest to lepsze rozwiązanie.
aullah
5
Warto również zauważyć, że StringUtils.substring(yourString, 0, n)to nie to samo co yourString.substring(0, n). Pierwsza jest z StringUtils, podczas gdy druga używa String.substring(co daje wyjątek, jeśli indeks końcowy przekracza długość ciągu).
ToolmakerSteve
Tak jak do Twojej wiadomości, jeśli spojrzysz na źródło tej metody, obsługuje przypadek, w którym koniec jest większy niż długość, zmieniając koniec na długość:if (end > str.length()) { end = str.length();}
bholl
1
Ostatni parametr StringUtils.substring(String s, int start, int len)nie jest długi, jest to indeks końcowy.
gorootde
StringUtils.substring ("abc", 0, 4) = "abc", działało dla mnie. Dzięki !
Akash5288
42

Apache Commons Lang ma na to StringUtils.leftmetodę.

String upToNCharacters = StringUtils.left(s, n);
Skuli
źródło
Czy nie powinno to być najlepsze rozwiązanie? Dlaczego nie wielu głosuje za tym?
Czy Will
3
Może dlatego, że inni ludzie nie mają tego samego zdania co Ty? :-)
Stephen C
ta odpowiedź nadeszła znacznie później niż pierwotna data zadania pytania.
Mulki
@DoWill: Ponieważ dodanie (innej) biblioteki innej firmy do środowiska wykonywalnego nie zawsze jest opłacalne.
LarsH
12

Jest pewna klasa pytań dotyczących SO, które czasami mają mniej niż doskonały sens, ten jest niebezpiecznie bliski :-)

Być może mógłbyś wyjaśnić swoją niechęć do używania jednej z dwóch metod, które wykluczałeś.

Jeśli dzieje się tak tylko dlatego, że nie chcesz zasypywać swojego kodu ifinstrukcjami lub kodem przechwytującym wyjątki, jednym z rozwiązań jest użycie funkcji pomocniczej, która zajmie się tym za Ciebie, na przykład:

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

który wcześniej sprawdzi długości i odpowiednio zareaguje (zwróci mniejszy ciąg lub wypełnienie ze spacjami).

Wtedy nie musisz się o to martwić w swoim kodzie, wystarczy zadzwonić:

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

zamiast:

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

To zadziała w przypadku, gdy wydaje się, że martwisz się (na podstawie swoich komentarzy do innych odpowiedzi), nie przerywając przepływu kodu podczas tworzenia wielu rzeczy związanych z budowaniem ciągów.

paxdiablo
źródło
1
Powinieneś uważniej przeczytać komentarz, @antony, zwłaszcza buźkę, i nie być tak cennym w stosunku do tych, którzy próbują pomóc. Po prostu stwierdziłem, że nie podałeś żadnego uzasadnienia, dlaczego musisz unikać tych dwóch metod. I to jest prawdziwa odpowiedź, używając funkcji pomocniczej, dlatego nie ma jej w komentarzu.
paxdiablo
1
+1: To DUŻO lepsze podejście niż przyjęte, biorąc pod uwagę pragnienie OP, aby nie zaśmiecać kodu. (lub zobacz rozwiązanie Nickkk polegające na włączeniu biblioteki, która ma już funkcję, która zachowuje się zgodnie z oczekiwaniami).
ToolmakerSteve,
12
String upToNCharacters = String.format("%."+ n +"s", str);

Okropne, jeśli njest zmienną (więc musisz skonstruować ciąg formatu), ale całkiem jasne, jeśli jest stałą:

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

dokumenty

13ren
źródło
Ciekawa alternatywa, choć nie wyobrażam sobie jej kiedykolwiek, biorąc pod uwagę bardziej tradycyjne podejścia, które były stosowane cztery lata temu.
ToolmakerSteve
Najlepsza odpowiedź, ponieważ wejściowy ciąg jest odczytywany tylko raz, więc nie ma potrzeby przechowywania go w zmiennej, co pozwala na jego dokładne osadzenie.
Profiterole
3

Użyj następującej metody podciągów:

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

Jeśli n jest większe niż długość łańcucha, zgłosi to wyjątek, jak zauważył jeden z komentatorów. jednym prostym rozwiązaniem jest zawinięcie tego wszystkiego w warunek if(s.length()<n)w elseklauzuli, możesz wybrać, czy chcesz po prostu wydrukować / zwrócić cały String, czy też obsłużyć to w inny sposób.

Matt Boehm
źródło
1
grozi to uzyskaniem wyjątku IndexOutOfBoundsException
antony.trupe
Przy okazji, jeśli planujesz programować w Javie, powinieneś spróbować zapamiętać większość metod API dla String ( java.sun.com/j2se/1.5.0/docs/api/java/lang/String.html ).
Matt Boehm
Już wykluczyłem podciąg, przynajmniej sam, jako nie odpowiedź.
antony.trupe
Musisz albo sprawdzić rozmiar, albo złapać wyjątek. Czy mogę zapytać, dlaczego wykonanie któregokolwiek z powyższych nie zadziała w Twojej sytuacji?
Matt Boehm
3
Jak to jest odpowiedź na pytanie? Pytanie dotyczyło tego, jak NIE musieć najpierw sprawdzać rozmiaru ani powodować wyjątku, który musi zostać przechwycony.
ToolmakerSteve
3

Jeśli masz szczęście rozwijać się z Kotlinem,
możesz użyć, takeaby osiągnąć swój cel.

val someString = "hello"

someString.take(10) // result is "hello"
someString.take(4) // result is "hell" )))
Leo Droidcoder
źródło
0

ApacheCommons mnie zaskoczyło, StringUtils.abbreviate(String str, int maxWidth)dopisuje "..." nie ma możliwości zmiany postfiksa. WordUtils.abbreviate(String str, int lower, int upper, String appendToEnd)wyszukuje następną pustą przestrzeń.

Zostawię to tutaj:

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
źródło
1
@ VolkanGüven To z powodu zdania „ApacheCommons mnie zaskoczyło”. Popełniłem grzech, krytykując świętą bibliotekę ApacheCommons. Albo cokolwiek ...
yuceel
0

Kotlin: (jeśli ktoś potrzebuje)

var mText = text.substring(0, text.length.coerceAtMost(20))
Touhid
źródło