Teilen Sie die Zeichenfolge in Java in Teilzeichenfolgen gleicher Länge


125

So teilen Sie den String "Thequickbrownfoxjumps"in Java in gleich große Teilzeichenfolgen. Z.B. "Thequickbrownfoxjumps"von 4 gleicher Größe sollte die Ausgabe geben.

["Theq","uick","brow","nfox","jump","s"]

Ähnliche Frage:

Teilen Sie die Zeichenfolge in Scala in gleichlange Teilzeichenfolgen auf


4
Was hast du versucht? Warum hat das nicht funktioniert?
Thilo

2
Benötigen Sie hierfür einen regulären Ausdruck? Ich frage nur wegen des Regex-Tags ...
Tim Pietzcker

@Thilo Link, den er gepostet hat, ist für Scala, er fragt nach dem gleichen in Java
Jaydeep Patel

@Thilo: Ich habe gefragt, wie es in Java geht, wie die Antwort für Scala.
Emil

Antworten:


226

Hier ist die Regex-Einzeiler-Version:

System.out.println(Arrays.toString(
    "Thequickbrownfoxjumps".split("(?<=\\G.{4})")
));

\Gist eine Behauptung mit einer Breite von Null, die mit der Position übereinstimmt, an der die vorherige Übereinstimmung beendet wurde. Wenn es war kein vorheriges Spiel, paßt es den Beginn des Eingangs, das gleiches wie \A. Das beiliegende Erscheinungsbild entspricht der Position, die vier Zeichen vom Ende der letzten Übereinstimmung entfernt ist.

Beide sehen gut aus und \Gsind erweiterte Regex-Funktionen, die nicht von allen Geschmacksrichtungen unterstützt werden. Darüber hinaus \Gwird nicht konsistent über die Geschmacksrichtungen implementiert, die es unterstützen. Dieser Trick funktioniert (zum Beispiel) in Java , Perl, .NET und JGSoft, jedoch nicht in PHP (PCRE), Ruby 1.9+ oder TextMate (beide Oniguruma). JavaScript /y(Sticky Flag) ist nicht so flexibel wie \Gund kann auf diese Weise nicht verwendet werden, selbst wenn JS Lookbehind unterstützt.

Ich sollte erwähnen, dass ich diese Lösung nicht unbedingt empfehle , wenn Sie andere Optionen haben. Die Nicht-Regex-Lösungen in den anderen Antworten sind möglicherweise länger, aber sie dokumentieren sich auch selbst. Dies ist genau das Gegenteil davon. ;)

Dies funktioniert auch nicht in Android, was die Verwendung \Gin Lookbehinds nicht unterstützt .


2
In PHP 5.2.4 funktioniert folgender Code: return preg_split ('/ (? <= \ G. {'. $ Len. '}) / U', $ str, -1, PREG_SPLIT_NO_EMPTY);
Igor

5
Für die Aufzeichnung wird die Verwendung String.substring()anstelle eines regulären Ausdrucks, obwohl einige zusätzliche Codezeilen erforderlich sind, irgendwo in der Größenordnung von 5x schneller ausgeführt ...
zeichnete

2
In Java funktioniert dies nicht für eine Zeichenfolge mit Zeilenumbrüchen. Es wird nur bis zur ersten neuen Zeile geprüft, und wenn diese neue Zeile zufällig vor der Teilungsgröße liegt, wird die Zeichenfolge nicht geteilt. Oder habe ich etwas verpasst?
Joey

5
Der Vollständigkeit halber: Für die Aufteilung von Text auf mehrere Zeilen muss (?s)im regulären Ausdruck ein Präfix stehen : (?s)(?<=\\G.{4}).
Bobbel

1
Java Barfs auf diesem vollständig zur Kompilierungszeit:java.util.regex.PatternSyntaxException: Look-behind pattern matches must have a bounded maximum length
Jeffrey Blattman

132

Nun, es ist ziemlich einfach, dies mit einfachen Arithmetik- und String-Operationen zu tun:

public static List<String> splitEqually(String text, int size) {
    // Give the list the right capacity to start with. You could use an array
    // instead if you wanted.
    List<String> ret = new ArrayList<String>((text.length() + size - 1) / size);

    for (int start = 0; start < text.length(); start += size) {
        ret.add(text.substring(start, Math.min(text.length(), start + size)));
    }
    return ret;
}

Ich denke nicht, dass es sich wirklich lohnt, einen regulären Ausdruck dafür zu verwenden.

EDIT: Meine Argumentation für die Verwendung eines regulären Ausdrucks:

  • Dies verwendet keinen der realen Mustervergleiche von Regexen. Es zählt nur.
  • Ich vermute, dass das oben Genannte effizienter ist, obwohl es in den meisten Fällen keine Rolle spielt
  • Wenn Sie an verschiedenen Stellen variable Größen verwenden müssen, haben Sie entweder eine Wiederholung oder eine Hilfsfunktion, um den regulären Ausdruck selbst basierend auf einem Parameter zu erstellen.
  • Der in einer anderen Antwort angegebene reguläre Ausdruck wurde zunächst nicht kompiliert (ungültiges Escapezeichen) und funktionierte dann nicht. Mein Code hat beim ersten Mal funktioniert. Dies ist eher ein Beweis für die Verwendbarkeit von regulären Ausdrücken im Vergleich zu einfachem Code, IMO.

8
@Emil: Eigentlich hast du nicht nach einer Regex gefragt. Es ist in den Tags, aber nichts in der Frage selbst verlangt nach einem regulären Ausdruck. Sie platzieren diese Methode an einer Stelle und können dann die Zeichenfolge an einer beliebigen Stelle in Ihrem Code in nur eine gut lesbare Anweisung aufteilen .
Jon Skeet

3
Emil, dafür ist ein Regex nicht gedacht. Zeitraum.
Chris

3
@Emil: Wenn Sie einen Einzeiler zum Teilen der Zeichenfolge Splitter.fixedLength(4)benötigen , würde ich Guavas empfehlen, wie von Seanizer vorgeschlagen.
ColinD

2
@ Jay: Komm schon, du musst nicht so sarkastisch sein. Ich bin sicher, dass es mit Regex in nur einer Zeile gemacht werden kann. Ein Teilstring mit fester Länge ist auch ein Muster. Was sagst du zu dieser Antwort? stackoverflow.com/questions/3760152/… .
Emil

4
@Emil: Ich wollte nicht, dass das unhöflich ist, nur skurril. Der ernste Teil meines Punktes war, dass, obwohl ja, ich sicher bin, dass Sie sich einen Regex einfallen lassen könnten, um dies zu tun - ich sehe, Alan Moore hat einen, von dem er behauptet, dass er funktioniert -, der kryptisch ist und daher für einen späteren Programmierer schwierig ist verstehen und pflegen. Eine Teilzeichenfolgenlösung kann intuitiv und lesbar sein. Siehe Jon Skeets 4. Kugel: Ich stimme dem zu 100% zu.
Jay

71

Dies ist mit Google Guava sehr einfach :

for(final String token :
    Splitter
        .fixedLength(4)
        .split("Thequickbrownfoxjumps")){
    System.out.println(token);
}

Ausgabe:

Theq
uick
brow
nfox
jump
s

Wenn Sie das Ergebnis als Array benötigen, können Sie diesen Code verwenden:

String[] tokens =
    Iterables.toArray(
        Splitter
            .fixedLength(4)
            .split("Thequickbrownfoxjumps"),
        String.class
    );

Referenz:

Hinweis: Die Splitterkonstruktion ist oben inline dargestellt. Da Splitter jedoch unveränderlich und wiederverwendbar sind, empfiehlt es sich, sie in Konstanten zu speichern:

private static final Splitter FOUR_LETTERS = Splitter.fixedLength(4);

// more code

for(final String token : FOUR_LETTERS.split("Thequickbrownfoxjumps")){
    System.out.println(token);
}

Vielen Dank für den Beitrag (um mich auf die Guavenbibliotheksmethode aufmerksam zu machen). Aber ich muss die Regex-Antwort stackoverflow.com/questions/3760152/… akzeptieren, da keine Bibliothek von Drittanbietern und ein Einzeiler erforderlich sind.
Emil

1
Es ist mit ziemlicher Sicherheit nicht das Richtige, Hunderte von KB Bibliothekscode einzuschließen, um diese einfache Aufgabe auszuführen.
Jeffrey Blattman

2
@ JeffreyBlattman einschließlich Guave nur dafür ist wahrscheinlich übertrieben, stimmt. Aber ich benutze es trotzdem als Allzweckbibliothek in meinem gesamten Java-Code. Warum also nicht diese eine zusätzliche Funktionalität nutzen
Sean Patrick Floyd

Gibt es eine Möglichkeit, sich mit einem Trennzeichen wieder anzuschließen?
Wassermann Macht

1
@ AquariusPowerString.join(separator, arrayOrCollection)
Holger

14

Wenn Sie die Guaven -Allzweckbibliotheken von Google verwenden (und ganz ehrlich, jedes neue Java-Projekt sollte es wahrscheinlich sein), ist dies mit der Splitter- Klasse wahnsinnig trivial :

for (String substring : Splitter.fixedLength(4).split(inputString)) {
    doSomethingWith(substring);
}

und das wars . Einfach wie!


8
public static String[] split(String src, int len) {
    String[] result = new String[(int)Math.ceil((double)src.length()/(double)len)];
    for (int i=0; i<result.length; i++)
        result[i] = src.substring(i*len, Math.min(src.length(), (i+1)*len));
    return result;
}

Da src.length()und lenbeide ints sind, erreicht Ihr Anruf ceiling nicht das, was Sie wollen - überprüfen Sie, wie einige der anderen Antworten dies tun: (src.length () + len - 1) / len
Michael Brewer-Davis

@ Michael: Guter Punkt. Ich habe es nicht mit Strings nicht mehrfacher Länge getestet. Es ist jetzt behoben.
Saul

6
public String[] splitInParts(String s, int partLength)
{
    int len = s.length();

    // Number of parts
    int nparts = (len + partLength - 1) / partLength;
    String parts[] = new String[nparts];

    // Break into parts
    int offset= 0;
    int i = 0;
    while (i < nparts)
    {
        parts[i] = s.substring(offset, Math.min(offset + partLength, len));
        offset += partLength;
        i++;
    }

    return parts;
}

6
Haben Sie aus Interesse etwas gegen forSchleifen?
Jon Skeet

Eine forSchleife ist in der Tat eine "natürlichere" Wahl :-) Vielen Dank, dass Sie darauf hingewiesen haben.
Grodriguez

3

Sie können substringvon String.class(Behandlung von Ausnahmen) oder von Apache lang Commons (es behandelt Ausnahmen für Sie) verwenden.

static String   substring(String str, int start, int end) 

Legen Sie es in eine Schleife und Sie können loslegen.


1
Was ist falsch an der substringMethode in der Standardklasse String?
Grodriguez

Die Commons-Version vermeidet Ausnahmen (außerhalb der Grenzen und dergleichen)
Thilo

7
Aha; Ich würde sagen, ich bevorzuge es, Ausnahmen zu vermeiden, indem ich stattdessen die Parameter im aufrufenden Code steuere.
Grodriguez

2

Ich hätte lieber diese einfache Lösung:

String content = "Thequickbrownfoxjumps";
while(content.length() > 4) {
    System.out.println(content.substring(0, 4));
    content = content.substring(4);
}
System.out.println(content);

Tu das nicht! Die Zeichenfolge ist unveränderlich, daher muss Ihr Code alle 4 Zeichen die gesamte verbleibende Zeichenfolge kopieren. Ihr Snippet benötigt daher in der Größe des Strings eher eine quadratische als eine lineare Zeit.
Tobias

@Tobias: Auch wenn String veränderbar war, führt dieses Snippet die erwähnte redundante Kopie aus, es sei denn, es gibt komplexe Kompilierungsprozesse, die dies betreffen. Der einzige Grund für die Verwendung dieses Snippets ist die Einfachheit des Codes.
Cheetah Coder

Haben Sie Ihren Code geändert, seit Sie ihn zum ersten Mal veröffentlicht haben? Die neueste Version erstellt keine Kopien - substring () wird effizient ausgeführt (konstante Zeit, zumindest bei alten Java-Versionen); Es enthält einen Verweis auf das char [] der gesamten Zeichenfolge (zumindest in alten Java-Versionen), aber das ist in diesem Fall in Ordnung, da Sie alle Zeichen behalten. Der neueste Code, den Sie hier haben, ist also in Ordnung (Modulo, dass Ihr Code eine leere Zeile druckt, wenn der Inhalt als leere Zeichenfolge beginnt, was möglicherweise nicht das ist, was man beabsichtigt).
Tobias

@Tobias: Ich erinnere mich an keine Veränderung.
Cheetah Coder

@Tobias Die substringImplementierung wurde mit Java 7, Update 6 Mitte 2012 geändert, als die Felder offsetund countaus der StringKlasse entfernt wurden. Die Komplexität wurde also substringlange vor dieser Antwort linear. Aber für eine kleine Saite wie das Beispiel läuft sie immer noch schnell genug und für längere Saiten… nun, diese Aufgabe tritt in der Praxis selten auf.
Holger

2

Hier ist eine Einzeiler-Implementierung mit Java8-Streams:

String input = "Thequickbrownfoxjumps";
final AtomicInteger atomicInteger = new AtomicInteger(0);
Collection<String> result = input.chars()
                                    .mapToObj(c -> String.valueOf((char)c) )
                                    .collect(Collectors.groupingBy(c -> atomicInteger.getAndIncrement() / 4
                                                                ,Collectors.joining()))
                                    .values();

Es gibt die folgende Ausgabe:

[Theq, uick, brow, nfox, jump, s]

1
Das ist eine schreckliche Lösung, die die Absicht der API bekämpft, zustandsbehaftete Funktionen verwendet und wesentlich komplizierter als eine gewöhnliche Schleife ist, ganz zu schweigen vom Aufwand für Boxing und String-Verkettung. Wenn Sie eine Stream-Lösung wünschen, verwenden Sie etwas wieString[] result = IntStream.range(0, (input.length()+3)/4) .mapToObj(i -> input.substring(i *= 4, Math.min(i + 4, input.length()))) .toArray(String[]::new);
Holger

2

Hier ist ein Einzeiler - Version , die verwendet Java 8 IntStream die Indizes der Scheibe Anfänge zu bestimmen:

String x = "Thequickbrownfoxjumps";

String[] result = IntStream
                    .iterate(0, i -> i + 4)
                    .limit((int) Math.ceil(x.length() / 4.0))
                    .mapToObj(i ->
                        x.substring(i, Math.min(i + 4, x.length())
                    )
                    .toArray(String[]::new);

1

Im Fall , dass Sie die Zeichenfolge gleich nach hinten, also von rechts nach links, zum Beispiel spalten, zu spalten 1010001111zu [10, 1000, 1111], hier ist der Code:

/**
 * @param s         the string to be split
 * @param subLen    length of the equal-length substrings.
 * @param backwards true if the splitting is from right to left, false otherwise
 * @return an array of equal-length substrings
 * @throws ArithmeticException: / by zero when subLen == 0
 */
public static String[] split(String s, int subLen, boolean backwards) {
    assert s != null;
    int groups = s.length() % subLen == 0 ? s.length() / subLen : s.length() / subLen + 1;
    String[] strs = new String[groups];
    if (backwards) {
        for (int i = 0; i < groups; i++) {
            int beginIndex = s.length() - subLen * (i + 1);
            int endIndex = beginIndex + subLen;
            if (beginIndex < 0)
                beginIndex = 0;
            strs[groups - i - 1] = s.substring(beginIndex, endIndex);
        }
    } else {
        for (int i = 0; i < groups; i++) {
            int beginIndex = subLen * i;
            int endIndex = beginIndex + subLen;
            if (endIndex > s.length())
                endIndex = s.length();
            strs[i] = s.substring(beginIndex, endIndex);
        }
    }
    return strs;
}

1

Ich benutze die folgende Java 8-Lösung:

public static List<String> splitString(final String string, final int chunkSize) {
  final int numberOfChunks = (string.length() + chunkSize - 1) / chunkSize;
  return IntStream.range(0, numberOfChunks)
                  .mapToObj(index -> string.substring(index * chunkSize, Math.min((index + 1) * chunkSize, string.length())))
                  .collect(toList());
}

0

Java 8-Lösung ( so, aber etwas einfacher):

public static List<String> partition(String string, int partSize) {
  List<String> parts = IntStream.range(0, string.length() / partSize)
    .mapToObj(i -> string.substring(i * partSize, (i + 1) * partSize))
    .collect(toList());
  if ((string.length() % partSize) != 0)
    parts.add(string.substring(string.length() / partSize * partSize));
  return parts;
}

-1

Ich fragte @Alan Moore in einem Kommentar zur akzeptierten Lösung, wie Zeichenfolgen mit Zeilenumbrüchen behandelt werden könnten. Er schlug vor, DOTALL zu verwenden.

Mit seinem Vorschlag habe ich eine kleine Auswahl erstellt, wie das funktioniert:

public void regexDotAllExample() throws UnsupportedEncodingException {
    final String input = "The\nquick\nbrown\r\nfox\rjumps";
    final String regex = "(?<=\\G.{4})";

    Pattern splitByLengthPattern;
    String[] split;

    splitByLengthPattern = Pattern.compile(regex);
    split = splitByLengthPattern.split(input);
    System.out.println("---- Without DOTALL ----");
    for (int i = 0; i < split.length; i++) {
        byte[] s = split[i].getBytes("utf-8");
        System.out.println("[Idx: "+i+", length: "+s.length+"] - " + s);
    }
    /* Output is a single entry longer than the desired split size:
    ---- Without DOTALL ----
    [Idx: 0, length: 26] - [B@17cdc4a5
     */


    //DOTALL suggested in Alan Moores comment on SO: https://stackoverflow.com/a/3761521/1237974
    splitByLengthPattern = Pattern.compile(regex, Pattern.DOTALL);
    split = splitByLengthPattern.split(input);
    System.out.println("---- With DOTALL ----");
    for (int i = 0; i < split.length; i++) {
        byte[] s = split[i].getBytes("utf-8");
        System.out.println("[Idx: "+i+", length: "+s.length+"] - " + s);
    }
    /* Output is as desired 7 entries with each entry having a max length of 4:
    ---- With DOTALL ----
    [Idx: 0, length: 4] - [B@77b22abc
    [Idx: 1, length: 4] - [B@5213da08
    [Idx: 2, length: 4] - [B@154f6d51
    [Idx: 3, length: 4] - [B@1191ebc5
    [Idx: 4, length: 4] - [B@30ddb86
    [Idx: 5, length: 4] - [B@2c73bfb
    [Idx: 6, length: 2] - [B@6632dd29
     */

}

Aber ich mag auch die @ Jon Skeets-Lösung in https://stackoverflow.com/a/3760193/1237974 . Für die Wartbarkeit in größeren Projekten, in denen nicht jeder gleichermaßen Erfahrung mit regulären Ausdrücken hat, würde ich wahrscheinlich die Jons-Lösung verwenden.


-1

Eine andere Brute-Force-Lösung könnte sein:

    String input = "thequickbrownfoxjumps";
    int n = input.length()/4;
    String[] num = new String[n];

    for(int i = 0, x=0, y=4; i<n; i++){
    num[i]  = input.substring(x,y);
    x += 4;
    y += 4;
    System.out.println(num[i]);
    }

Wobei der Code nur mit Teilzeichenfolgen durch die Zeichenfolge tritt


-1
    import static java.lang.System.exit;
   import java.util.Scanner;
   import Java.util.Arrays.*;


 public class string123 {

public static void main(String[] args) {


  Scanner sc=new Scanner(System.in);
    System.out.println("Enter String");
    String r=sc.nextLine();
    String[] s=new String[10];
    int len=r.length();
       System.out.println("Enter length Of Sub-string");
    int l=sc.nextInt();
    int last;
    int f=0;
    for(int i=0;;i++){
        last=(f+l);
            if((last)>=len) last=len;
        s[i]=r.substring(f,last);
     // System.out.println(s[i]);

      if (last==len)break;
       f=(f+l);
    } 
    System.out.print(Arrays.tostring(s));
    }}

Ergebnis

 Enter String
 Thequickbrownfoxjumps
 Enter length Of Sub-string
 4

 ["Theq","uick","brow","nfox","jump","s"]

-1
@Test
public void regexSplit() {
    String source = "Thequickbrownfoxjumps";
    // define matcher, any char, min length 1, max length 4
    Matcher matcher = Pattern.compile(".{1,4}").matcher(source);
    List<String> result = new ArrayList<>();
    while (matcher.find()) {
        result.add(source.substring(matcher.start(), matcher.end()));
    }
    String[] expected = {"Theq", "uick", "brow", "nfox", "jump", "s"};
    assertArrayEquals(result.toArray(), expected);
}

-1

Hier ist meine Version, die auf RegEx- und Java 8-Streams basiert. Es ist erwähnenswert, dass die Matcher.results()Methode seit Java 9 verfügbar ist.

Test enthalten.

public static List<String> splitString(String input, int splitSize) {
    Matcher matcher = Pattern.compile("(?:(.{" + splitSize + "}))+?").matcher(input);
    return matcher.results().map(MatchResult::group).collect(Collectors.toList());
}

@Test
public void shouldSplitStringToEqualLengthParts() {
    String anyValidString = "Split me equally!";
    String[] expectedTokens2 = {"Sp", "li", "t ", "me", " e", "qu", "al", "ly"};
    String[] expectedTokens3 = {"Spl", "it ", "me ", "equ", "all"};

    Assert.assertArrayEquals(expectedTokens2, splitString(anyValidString, 2).toArray());
    Assert.assertArrayEquals(expectedTokens3, splitString(anyValidString, 3).toArray());
}

-1
public static String[] split(String input, int length) throws IllegalArgumentException {

    if(length == 0 || input == null)
        return new String[0];

    int lengthD = length * 2;

    int size = input.length();
    if(size == 0)
        return new String[0];

    int rep = (int) Math.ceil(size * 1d / length);

    ByteArrayInputStream stream = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_16LE));

    String[] out = new String[rep];
    byte[]  buf = new byte[lengthD];

    int d = 0;
    for (int i = 0; i < rep; i++) {

        try {
            d = stream.read(buf);
        } catch (IOException e) {
            e.printStackTrace();
        }

        if(d != lengthD)
        {
            out[i] = new String(buf,0,d, StandardCharsets.UTF_16LE);
            continue;
        }

        out[i] = new String(buf, StandardCharsets.UTF_16LE);
    }
    return out;
}

-1
public static List<String> getSplittedString(String stringtoSplit,
            int length) {

        List<String> returnStringList = new ArrayList<String>(
                (stringtoSplit.length() + length - 1) / length);

        for (int start = 0; start < stringtoSplit.length(); start += length) {
            returnStringList.add(stringtoSplit.substring(start,
                    Math.min(stringtoSplit.length(), start + length)));
        }

        return returnStringList;
    }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.