Jak mogę wpisać ciąg w Javie?


431

Czy jest jakiś prosty sposób na wypełnianie napisów w Javie?

Wygląda na coś, co powinno być w interfejsie API typu StringUtil, ale nie mogę znaleźć niczego, co by to zrobiło.


W kwestii funkcji apache vs formater struny nie ma wątpliwości, że funkcja apache jest bardziej przejrzysta i czytelna. Zawijanie formatera w nazwach funkcji nie jest idealne.
Terra Caines,

Odpowiedzi:


189

Apache StringUtilsma kilka metod: leftPad, rightPad, centeri repeat.

Należy jednak pamiętać, że - jak inni wspominali i wykazali w tej odpowiedzi - String.format()i Formatterklasy w JDK są lepszymi opcjami. Użyj ich zamiast wspólnego kodu.


91
robi to java.util.Formatter (i String.format ()). Zawsze preferuj JDK od biblioteki zewnętrznej, jeśli wersja JDK wykonuje to zadanie (co robi).
cletus

6
Z kilku powodów wolałbym teraz Guava niż Apache Commons; ta odpowiedź pokazuje, jak to zrobić w Guava .
Jonik

1
@Jonik, czy chciałbyś wymienić niektóre ze swoich powodów? Interfejsy API wyglądają prawie tak samo.
glen3b

3
@ glen3b: W przypadku pojedynczego narzędzia, takiego jak te pomocniki dopełniania łańcucha, tak naprawdę nie ma znaczenia, której biblioteki użyć. To powiedziawszy, Guava jest ogólnie bardziej nowoczesnym, czystszym i lepiej udokumentowanym libem niż jego odpowiedniki w różnych projektach Apache Commons (Commons Lang, Commons Collections, Commons IO itp.). Jest również zbudowany przez naprawdę inteligentnych facetów (Kevin Bourrillion i in.), Z których wielu jest aktywnych w SO . Sam skończyłem, zastępując różne biblioteki Apache Commons zaledwie Guava lata temu i nie żałowałem.
Jonik

3
@ glen3b: Kilka dobrych dalszych lektur na to pytanie .
Jonik

634

Od wersji Java 1.5 String.format()można używać do wstawiania lewej / prawej strony danego ciągu.

public static String padRight(String s, int n) {
     return String.format("%-" + n + "s", s);  
}

public static String padLeft(String s, int n) {
    return String.format("%" + n + "s", s);  
}

...

public static void main(String args[]) throws Exception {
 System.out.println(padRight("Howto", 20) + "*");
 System.out.println(padLeft("Howto", 20) + "*");
}

Wyjście to:

Howto               *
               Howto*

39
Co zrobić, jeśli potrzebujesz lpad z innymi znakami (nie spacjami)? Czy nadal jest to możliwe dzięki String.format? Nie jestem w stanie sprawić, żeby działało ...
Guido

6
AFAIK String.format () nie może tego zrobić, ale kodowanie nie jest zbyt trudne, patrz rgagnon.com/javadetails/java-0448.html (drugi przykład)
RealHowTo

61
@ Nullw0rm, jeśli odwołujesz się do rgagnon.com, pamiętaj, że RealHowTo jest także autorem rgagnon.com, więc sam by się podał ...
Colin Pickard

3
przynajmniej w mojej JVM „#” nie działa, „-” jest w porządku. (Wystarczy usunąć #). Może to być zgodne z dokumentacją formatyzatora, nie wiem; mówi: „# '' \ u0023 'Wymaga użycia wyjściowego formularza alternatywnego. Definicja formularza jest określona przez konwersję.”
gatoatigrado

6
Dzięki za świetną odpowiedź. Mam jednak wątpliwości, po co 1$? @leo udzielił bardzo podobnej odpowiedzi, która nie jest używana 1$i najwyraźniej ma ten sam efekt. Czy to robi różnicę?
Fabrício Matté

257

Wypełnienie do 10 znaków:

String.format("%10s", "foo").replace(' ', '*');
String.format("%-10s", "bar").replace(' ', '*');
String.format("%10s", "longer than 10 chars").replace(' ', '*');

wynik:

  *******foo
  bar*******
  longer*than*10*chars

Wyświetl znaki „*” dla znaków hasła:

String password = "secret123";
String padded = String.format("%"+password.length()+"s", "").replace(' ', '*');

wynik ma taką samą długość jak ciąg hasła:

  secret123
  *********

50
Tak długo, jak wszyscy pamiętamy, aby nie przechodzić sznurkiem ze spacjami ...: D
leviathanbadger

4
Jestem z @ aboveyou00 - jest to przerażające rozwiązanie dla ciągów ze spacjami, w tym przykład podany przez Leo. Rozwiązanie wypełniania łańcucha po lewej lub po prawej stronie nie powinno zmieniać łańcucha wejściowego tak, jak jest wyświetlany na wyjściu, chyba że oryginalne wypełnienie łańcucha wejściowego powoduje, że przekracza on określoną długość dopełniania.
Brian Warshaw

3
Potwierdź problem dotyczący miejsca. .replace(' ', '*')miał raczej na celu pokazanie efektu wypełnienia. Wyrażenie ukrywania hasła i tak nie ma tego problemu ... Aby uzyskać lepszą odpowiedź na temat dostosowywania lewego i prawego łańcucha dopełniania za pomocą natywnej funkcji Java, zobacz moją drugą odpowiedź.
leo

Jeśli ciąg zawiera tylko pojedyncze spacje i chcesz wstawiać różne znaki, możesz użyć wyrażenia regularnego zastępując go spojrzeniem do tyłu / do przodu: String.format("%30s", "pad left").replaceAll("(?<=( |^)) ", "*")lubString.format("%-30s", "pad right").replaceAll(" (?=( |$))", "*")
Jarosław Pawlak


33

Coś prostego:

Wartość powinna być ciągiem. przekonwertować na ciąg, jeśli nie jest. Jak "" + 123lubInteger.toString(123)

// let's assume value holds the String we want to pad
String value = "123";

Podciąg rozpoczyna się od indeksu długości wartości indeksu aż do końcowej długości dopełnienia:

String padded="00000000".substring(value.length()) + value;

// now padded is "00000123"

Bardziej dokładnie

podkładka w prawo:

String padded = value + ("ABCDEFGH".substring(value.length())); 

// now padded is "123DEFGH"

pad lewy:

String padString = "ABCDEFGH";
String padded = (padString.substring(0, padString.length() - value.length())) + value;

// now padded is "ABCDE123"

24

Spójrz na org.apache.commons.lang.StringUtils#rightPad(String str, int size, char padChar).

Ale algorytm jest bardzo prosty (pad aż do wielkości znaków):

public String pad(String str, int size, char padChar)
{
  StringBuffer padded = new StringBuffer(str);
  while (padded.length() < size)
  {
    padded.append(padChar);
  }
  return padded.toString();
}

11
Zauważ, że od wersji Java 1.5 warto używać StringBuilder zamiast StringBuffer.
Jon Skeet,

21

Oprócz Apache Commons, zobacz także, String.formatktóre powinny być w stanie zająć się prostym wypełnieniem (np. Spacjami).


17
public static String LPad(String str, Integer length, char car) {
  return (str + String.format("%" + length + "s", "").replace(" ", String.valueOf(car))).substring(0, length);
}

public static String RPad(String str, Integer length, char car) {
  return (String.format("%" + length + "s", "").replace(" ", String.valueOf(car)) + str).substring(str.length(), length + str.length());
}

LPad("Hi", 10, 'R') //gives "RRRRRRRRHi"
RPad("Hi", 10, 'R') //gives "HiRRRRRRRR"
RPad("Hi", 10, ' ') //gives "Hi        "
RPad("Hi", 1, ' ')  //gives "H"
//etc...

4
Uwaga: odwrócono nazwy funkcji; jeśli go uruchomisz, zobaczysz.
niebieskawy

plus jeśli oryginalny ciąg zawiera spacje, to nie działa
Steven Shaw

@StevenShaw Jeśli się nie mylę, zamiana nie jest kierowana na oryginał str, więc jest to poprawne.
mafu

3
Zgodnie z konwencją nie należy używać wielkich liter w nazwach funkcji.
principal-ideal-domain

Czy brak jest warunku zwrócenia ciągu, gdy (length - str.length())jest 0? Formater zgłasza, FormatFlagsConversionMismatchExceptionkiedy %0sjest ciągiem formatu.
Devin Walters

7

Zajęło mi to trochę czasu. Prawdziwym kluczem jest przeczytanie dokumentacji formatera.

// Get your data from wherever.
final byte[] data = getData();
// Get the digest engine.
final MessageDigest md5= MessageDigest.getInstance("MD5");
// Send your data through it.
md5.update(data);
// Parse the data as a positive BigInteger.
final BigInteger digest = new BigInteger(1,md5.digest());
// Pad the digest with blanks, 32 wide.
String hex = String.format(
    // See: http://download.oracle.com/javase/1.5.0/docs/api/java/util/Formatter.html
    // Format: %[argument_index$][flags][width]conversion
    // Conversion: 'x', 'X'  integral    The result is formatted as a hexadecimal integer
    "%1$32x",
    digest
);
// Replace the blank padding with 0s.
hex = hex.replace(" ","0");
System.out.println(hex);

Dlaczego koniec jest tak skomplikowany? Mogłeś właśnie zrobić String hex = String.format ("% 032x", skrót); i miał dokładnie takie same wyniki, tylko bez niepotrzebnego zastępowania () i konieczności używania $, aby uzyskać dostęp do określonych zmiennych (w końcu jest tylko jedna.)
ArtOfWarfare

@ArtOfWarfare fair point. Ktoś pierwotnie poprosił o wartości szesnastkowe wypełniania w innym pytaniu, widziałem, że mam link do dokumentacji formatyzatora. Jest to skomplikowane ze względu na kompletność.
Nthalk

6

wiem, że ten wątek jest trochę stary i pierwotne pytanie dotyczyło łatwego rozwiązania, ale jeśli powinno być naprawdę szybkie, powinieneś użyć tablicy znaków.

public static String pad(String str, int size, char padChar)
{
    if (str.length() < size)
    {
        char[] temp = new char[size];
        int i = 0;

        while (i < str.length())
        {
            temp[i] = str.charAt(i);
            i++;
        }

        while (i < size)
        {
            temp[i] = padChar;
            i++;
        }

        str = new String(temp);
    }

    return str;
}

rozwiązanie formatyzatora nie jest optymalne. samo zbudowanie ciągu formatu tworzy 2 nowe ciągi.

rozwiązanie apache można ulepszyć, inicjując SB o docelowy rozmiar, zastępując go poniżej

StringBuffer padded = new StringBuffer(str); 

z

StringBuffer padded = new StringBuffer(pad); 
padded.append(value);

zapobiegnie wzrostowi bufora wewnętrznego kogoś.


Jeśli StringBuffernie można uzyskać dostępu do wielu wątków jednocześnie (co w tym przypadku jest prawdą), należy użyć StringBuilder(w JDK1.5 +), aby uniknąć narzutu synchronizacji.
glen3b

5

Oto inny sposób na przesunięcie w prawo:

// put the number of spaces, or any character you like, in your paddedString

String paddedString = "--------------------";

String myStringToBePadded = "I like donuts";

myStringToBePadded = myStringToBePadded + paddedString.substring(myStringToBePadded.length());

//result:
myStringToBePadded = "I like donuts-------";

5

Od wersji Java 11 String.repeat (int) może być używany do wstawiania danego ciągu w lewo / w prawo.

System.out.println("*".repeat(5)+"apple");
System.out.println("apple"+"*".repeat(5));

Wynik:

*****apple
apple*****

1
Najlepsza odpowiedź dla java 11!
Nagy Attila

4

Możesz zmniejszyć narzut na połączenie, zachowując dane wypełniania, zamiast odbudowywać je za każdym razem:

public class RightPadder {

    private int length;
    private String padding;

    public RightPadder(int length, String pad) {
        this.length = length;
        StringBuilder sb = new StringBuilder(pad);
        while (sb.length() < length) {
            sb.append(sb);
        }
        padding = sb.toString();
   }

    public String pad(String s) {
        return (s.length() < length ? s + padding : s).substring(0, length);
    }

}

Alternatywnie możesz ustawić długość wyniku jako parametr pad(...)metody. W takim przypadku dostosuj ukryte wypełnienie w tej metodzie zamiast w konstruktorze.

(Wskazówka: Aby uzyskać dodatkowy kredyt, upewnij się, że jest bezpieczny dla wątków! ;-)


1
To jest bezpieczne dla wątków, z wyjątkiem niebezpiecznej publikacji (oczywiście, aby pola były ostateczne). Myślę, że wydajność można poprawić, wykonując podłańcuch przed znakiem +, który powinien zostać zastąpiony przez concat (co dziwne).
Tom Hawtin - tackline

3

Znaleziono to na Dzone

Pad z zerami:

String.format("|%020d|", 93); // prints: |00000000000000000093|

To powinna być odpowiedź, prosta i zwięzła, bez potrzeby instalowania bibliotek zewnętrznych.
Wong Jia Hau

2

możesz użyć wbudowanej metody StringBuilder append () i insert () do wypełniania zmiennych długości łańcucha:

AbstractStringBuilder append(CharSequence s, int start, int end) ;

Na przykład:

private static final String  MAX_STRING = "                    "; //20 spaces

    Set<StringBuilder> set= new HashSet<StringBuilder>();
    set.add(new StringBuilder("12345678"));
    set.add(new StringBuilder("123456789"));
    set.add(new StringBuilder("1234567811"));
    set.add(new StringBuilder("12345678123"));
    set.add(new StringBuilder("1234567812234"));
    set.add(new StringBuilder("1234567812222"));
    set.add(new StringBuilder("12345678122334"));

    for(StringBuilder padMe: set)
        padMe.append(MAX_STRING, padMe.length(), MAX_STRING.length());

2

To działa:

"".format("%1$-" + 9 + "s", "XXX").replaceAll(" ", "0")

Wypełni Twój Ciąg XXX do 9 Znaków spacją. Następnie wszystkie białe znaki zostaną zastąpione przez 0. Możesz zmienić białe znaki i 0 na cokolwiek chcesz ...


Powinieneś wywołać tę staticmetodę jako String.format(…)zamiast nadużywania pustego ciągu. Zapisanie czterech znaków w kodzie źródłowym z pewnością nie jest warte utraty czytelności.
Holger

2
public static String padLeft(String in, int size, char padChar) {                
    if (in.length() <= size) {
        char[] temp = new char[size];
        /* Llenado Array con el padChar*/
        for(int i =0;i<size;i++){
            temp[i]= padChar;
        }
        int posIniTemp = size-in.length();
        for(int i=0;i<in.length();i++){
            temp[posIniTemp]=in.charAt(i);
            posIniTemp++;
        }            
        return new String(temp);
    }
    return "";
}

2

Pozwól, że zostawię odpowiedź na niektóre przypadki, w których musisz podać lewy / prawy dopełnienie (lub ciąg prefiksu / sufiksu lub spacje) przed połączeniem z innym ciągiem i nie chcesz testować długości ani żadnego warunku.

Tak samo jak w przypadku wybranej odpowiedzi, wolałbym StringUtilsApache Commons, ale używając tej metody:

StringUtils.defaultString(StringUtils.leftPad(myString, 1))

Wyjaśnić:

  • myString: ciąg, który wprowadzam, może być pusty
  • StringUtils.leftPad(myString, 1): jeśli łańcuch jest pusty, to instrukcja również zwróci null
  • następnie użyj, defaultStringaby podać pusty ciąg, aby zapobiec konkatenacji wartości null

2

Odpowiedzi @ck i @Marlon Tarak są jedynymi, które używają a char[], co jest najlepszym rozwiązaniem dla aplikacji, które mają kilka wywołań metod wypełniania na sekundę. Nie korzystają one jednak z żadnych optymalizacji manipulacji tablicami i są dla mnie trochę nadpisane; można to zrobić bez żadnych pętli .

public static String pad(String source, char fill, int length, boolean right){
    if(source.length() > length) return source;
    char[] out = new char[length];
    if(right){
        System.arraycopy(source.toCharArray(), 0, out, 0, source.length());
        Arrays.fill(out, source.length(), length, fill);
    }else{
        int sourceOffset = length - source.length();
        System.arraycopy(source.toCharArray(), 0, out, sourceOffset, source.length());
        Arrays.fill(out, 0, sourceOffset, fill);
    }
    return new String(out);
}

Prosta metoda testowa:

public static void main(String... args){
    System.out.println("012345678901234567890123456789");
    System.out.println(pad("cats", ' ', 30, true));
    System.out.println(pad("cats", ' ', 30, false));
    System.out.println(pad("cats", ' ', 20, false));
    System.out.println(pad("cats", '$', 30, true));
    System.out.println(pad("too long for your own good, buddy", '#', 30, true));
}

Wyjścia:

012345678901234567890123456789
cats                          
                          cats
                cats
cats$$$$$$$$$$$$$$$$$$$$$$$$$$
too long for your own good, buddy 

1
Możesz użyć, getCharsaby pozwolić na Stringskopiowanie jego zawartości bezpośrednio do outtablicy, zamiast wywoływania source.toCharArray(), a następnie System.arraycopy. Rozważałbym nawet uproszczenie implementacji do char[] out = new char[length]; Arrays.fill(out, 0, length, fill); source.getChars(0, source.length(), out, right? 0: length - source.length()); return new String(out);; choć wygląda na to, fillże wykonałby więcej pracy, w rzeczywistości pozwala JVM na wyeliminowanie domyślnego zerowania.
Holger

1

java.util.Formatterzrobi lewe i prawe wypełnienie. Nie ma potrzeby dziwnych zależności stron trzecich (czy chcesz je dodać do czegoś tak trywialnego).

[Pominąłem szczegóły i uczyniłem ten post „społecznością wiki”, ponieważ nie jest to coś, czego potrzebuję.]


4
Nie zgadzam się. W każdym większym projekcie Java zwykle wykonujesz tak dużo manipulacji ciągami, że zwiększona czytelność i unikanie błędów jest warte użycia Apache Commons Lang. Wszyscy znacie kod jak someString.subString (someString.indexOf (startCharacter), someString.lastIndexOf (endCharacter)), którego można łatwo uniknąć za pomocą StringUtils.
Bananeweizen

1

Wszystkie operacje na łańcuchach zwykle muszą być bardzo wydajne - szczególnie jeśli pracujesz z dużymi zestawami danych. Chciałem czegoś szybkiego i elastycznego, podobnego do tego, co dostaniesz w poleceniu pad plsql. Nie chcę też zawierać ogromnej biblioteki dla jednej drobnej rzeczy. Biorąc pod uwagę powyższe rozważania, żadne z tych rozwiązań nie było zadowalające. Oto rozwiązania, które wymyśliłem, które miały najlepsze wyniki testów porównawczych, jeśli ktokolwiek może to poprawić, proszę dodać komentarz.

public static char[] lpad(char[] pStringChar, int pTotalLength, char pPad) {
    if (pStringChar.length < pTotalLength) {
        char[] retChar = new char[pTotalLength];
        int padIdx = pTotalLength - pStringChar.length;
        Arrays.fill(retChar, 0, padIdx, pPad);
        System.arraycopy(pStringChar, 0, retChar, padIdx, pStringChar.length);
        return retChar;
    } else {
        return pStringChar;
    }
}
  • Uwaga: wywoływana jest za pomocą String.toCharArray (), a wynik można przekonwertować na String za pomocą nowego ciągu ((wynik [char [])). Powodem tego jest to, że jeśli zastosujesz wiele operacji, możesz wykonać je wszystkie na char [], a nie konwertować między formatami - za kulisami String jest przechowywany jako char []. Gdyby te operacje zostały uwzględnione w samej klasie String, byłoby to dwa razy bardziej wydajne - pod względem szybkości i pamięci.

To rzeczywiście wygląda na najbardziej efektywny sposób
David Lilljegren,

1

Użyj tej funkcji.

private String leftPadding(String word, int length, char ch) {
   return (length > word.length()) ? leftPadding(ch + word, length, ch) : word;
}

jak używać?

leftPadding(month, 2, '0');

wyjście: 01 02 03 04 .. 11 12


1

Wiele osób ma kilka bardzo interesujących technik, ale chcę to uprościć, więc idę z tym:

public static String padRight(String s, int n, char padding){
    StringBuilder builder = new StringBuilder(s.length() + n);
    builder.append(s);
    for(int i = 0; i < n; i++){
        builder.append(padding);
    }
    return builder.toString();
}

public static String padLeft(String s, int n,  char padding) {
    StringBuilder builder = new StringBuilder(s.length() + n);
    for(int i = 0; i < n; i++){
        builder.append(Character.toString(padding));
    }
    return builder.append(s).toString();
}

public static String pad(String s, int n, char padding){
    StringBuilder pad = new StringBuilder(s.length() + n * 2);
    StringBuilder value = new StringBuilder(n);
    for(int i = 0; i < n; i++){
        pad.append(padding);
    }
    return value.append(pad).append(s).append(pad).toString();
}

2
Jest to bardzo nieefektywne, powinieneś użyć StringBuilder
wkarl

Ponieważ znasz już długość, powinieneś powiedzieć StringBuilder, aby przydzielił tyle miejsca podczas jego tworzenia.
Ariel,

@Ariel interesujące, że wspomniałeś o tym, ponieważ właśnie rozmawiałem z kimś innym o tym dzisiaj lol.
Aelphaeis

0

Proste rozwiązanie bez interfejsu API będzie wyglądać następująco:

public String pad(String num, int len){
    if(len-num.length() <=0) return num;
    StringBuffer sb = new StringBuffer();
    for(i=0; i<(len-num.length()); i++){
        sb.append("0");
    }
    sb.append(num);
    return sb.toString();
}

0

Oneliner w Javie, bez fantazyjnej biblioteki.

// 6 characters padding example
String pad = "******";
// testcases for 0, 4, 8 characters
String input = "" | "abcd" | "abcdefgh"

Pad w lewo, nie ograniczaj

result = pad.substring(Math.min(input.length(),pad.length())) + input;
results: "******" | "**abcd" | "abcdefgh"

Pad Prawo, nie ograniczaj

result = input + pad.substring(Math.min(input.length(),pad.length()));
results: "******" | "abcd**" | "abcdefgh"

Pad Lewa, ograniczenie do długości padu

result = (pad + input).substring(input.length(), input.length() + pad.length());
results: "******" | "**abcd" | "cdefgh"

Pad Right, ograniczenie do długości padu

result = (input + pad).substring(0, pad.length());
results: "******" | "abcd**" | "abcdef"

0

Co powiesz na użycie rekurencji? Rozwiązanie podane poniżej jest kompatybilne ze wszystkimi wersjami JDK i nie wymaga bibliotek zewnętrznych :)

private static String addPadding(final String str, final int desiredLength, final String padBy) {
        String result = str;
        if (str.length() >= desiredLength) {
            return result;
        } else {
            result += padBy;
            return addPadding(result, desiredLength, padBy);
        }
    }

UWAGA : To rozwiązanie doda dopełnienie, z niewielkim dopracowaniem możesz poprzedzić wartość pada.


0

Oto wersja równoległa dla tych z was, którzy mają bardzo długie ciągi :-)

int width = 100;
String s = "129018";

CharSequence padded = IntStream.range(0,width)
            .parallel()
            .map(i->i-(width-s.length()))
            .map(i->i<0 ? '0' :s.charAt(i))
            .collect(StringBuilder::new, (sb,c)-> sb.append((char)c), (sb1,sb2)->sb1.append(sb2));

0

-1

Prostym rozwiązaniem byłoby:

package nl;
public class Padder {
    public static void main(String[] args) {
        String s = "123" ;
        System.out.println("#"+("     " + s).substring(s.length())+"#");
    }
}

-1

Jak to jest

Łańcuch jest „cześć”, a wymagane wypełnienie to 15 z lewą podkładką „0”

String ax="Hello";
while(ax.length() < 15) ax="0"+ax;

10
ten generuje do 15 nowych ciągów.
claj

@claj Tak, ale to .
ruffin
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.