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?


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

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.


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"

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);

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.


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


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.


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" )))

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;
}

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))
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.