Teilen eines Strings bei jedem n-ten Zeichen


78

In JavaScript können wir so eine Zeichenfolge bei jedem dritten Zeichen teilen

"foobarspam".match(/.{1,3}/g)

Ich versuche herauszufinden, wie das in Java geht. Irgendwelche Hinweise?


Ich würde Regex nicht für diese Aufgabe verwenden.
Kennytm

3
OK. Was würden Sie dann vorschlagen?
Vijay Dev

1
So etwas wie Simons Antwort.
Kennytm

Ich stimme Ihrer Empfehlung zu. Da keine zusätzlichen Bibliotheken installiert werden mussten, funktionierte Simons Lösung hervorragend.
Harperville

Antworten:


131

Sie könnten es so machen:

String s = "1234567890";
System.out.println(java.util.Arrays.toString(s.split("(?<=\\G...)")));

welches produziert:

[123, 456, 789, 0]

Der reguläre Ausdruck (?<=\G...)entspricht einer leeren Zeichenfolge mit der letzten Übereinstimmung ( \G), gefolgt von drei Zeichen ( ...) davor ( (?<= ))


15
Ich würde es hassen zu glauben, dass jemand diese Antwort abgelehnt hat, nur weil er keine regulären Ausdrücke mag.
William Brendel

55
verrückte Requisiten für das höchste Regex-Mojo, aber als Leser dieses Codes würde ich dich jagen und dein Haus ärgern. :)
Kevin Bourrillion

4
Solange Sie dies über eine korrekt benannte Funktion (dh splitIntoParts) aufrufen und diese Zeile nicht direkt in Ihren Code einbetten, ist alles gut. Ansonsten lass die Jagd beginnen :)
GreenieMeanie

3
Ein Teil dessen, was diesen Trick so beängstigend macht, ist, dass er nicht in allen Sprachen funktioniert. Beispielsweise wird JavaScript nicht unterstützt \Gund Python wird nicht in einen regulären Ausdruck aufgeteilt, der null Zeichen entspricht. Aber wenn Java wie jede andere Sprache eine Methode zum Abrufen aller Übereinstimmungen hätte, hätten Sie diesen Trick nicht erst erfinden müssen, @Bart. ;)
Alan Moore

7
Ich kopiere / füge dies in mein Android Studio-Projekt ein und erhalte [123, 4567890]als Ergebnis :(
Evren Yurtesen

83

Java bietet keine voll funktionsfähigen Aufteilungsdienstprogramme, daher tun die Guava-Bibliotheken Folgendes:

Iterable<String> pieces = Splitter.fixedLength(3).split(string);

Schauen Sie sich das Javadoc für Splitter an . es ist sehr mächtig.


7
+1 Dies ist die richtige Antwort (auch bekannt als: Kennen und Verwenden der Bibliotheken )
Jonik

4
Ich würde diese Antwort über den regulären Ausdruck bringen ... nur weil sie wartbarer ist (z. B. die Tatsache, dass weniger Leute über RegEx Bescheid wissen als ppl, die "lesbaren" Code lesen können)
sivabudh

4
Nur gut, wenn Sie bereits eine Guava-Abhängigkeit haben. Andernfalls müssen Sie eine weitere Abhängigkeit hinzufügen - etwas, das Sie nicht tun sollten, ohne vorher mit Kollegen / Systemarchitekten zu sprechen.
foo

1
Das Hinzufügen einer vollständigen Bibliothek, sodass Sie nur eine Methode verwenden können, ist in den meisten Fällen nicht die beste Vorgehensweise. Auch das Hinzufügen einer Bibliothek ist in einer Unternehmensumgebung immer eine große Entscheidung.
GaboSampaio

50
import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        for (String part : getParts("foobarspam", 3)) {
            System.out.println(part);
        }
    }
    private static List<String> getParts(String string, int partitionSize) {
        List<String> parts = new ArrayList<String>();
        int len = string.length();
        for (int i=0; i<len; i+=partitionSize)
        {
            parts.add(string.substring(i, Math.min(len, i + partitionSize)));
        }
        return parts;
    }
}

Wenn Sie eine Sammlung von Teilzeichenfolgen behalten, die den gesamten ursprünglichen String abdecken, verschwendet die neue String-Methode tatsächlich (n-1) * sizeof (int). Die char-Arrays der neuen Strings belegen denselben Speicher, aber jedes hat ein separates Längenfeld. Wenn jedoch Teilzeichenfolgen später verworfen werden, kann ein neuer String den Speicher reduzieren. Ich würde mir so oder so keine Sorgen machen, wenn die ursprüngliche Saite nicht sehr groß wäre.
ILMTitan

@ DenisTulskiy könnten Sie näher darauf eingehen? Die substringMethode ist tatsächlich intelligent genug, um die übergeordneten Zeichenfolgen char[]für die Daten zu verwenden. Weitere Informationen finden Sie in dieser Antwort .
wchargin

1
@WChargin: hmm, du hast recht, ich habe keine Ahnung warum ich diesen Kommentar geschrieben habe. Ich werde es löschen. Vielen Dank.
Denis Tulskiy

7

Als Ergänzung zu Bart Kiers Antwort möchte ich hinzufügen, dass es möglich ist, anstatt die drei Punkte ...im regulären Ausdruck zu verwenden, die drei Zeichen darstellen, die Sie schreiben können .{3}und die dieselbe Bedeutung haben.

Dann würde der Code wie folgt aussehen:

String bitstream = "00101010001001010100101010100101010101001010100001010101010010101";
System.out.println(java.util.Arrays.toString(bitstream.split("(?<=\\G.{3})")));

Damit wäre es einfacher, die Zeichenfolgenlänge zu ändern, und die Erstellung einer Funktion ist jetzt mit einer variablen Eingabezeichenfolgenlänge sinnvoll. Dies könnte folgendermaßen aussehen:

public static String[] splitAfterNChars(String input, int splitLen){
    return input.split(String.format("(?<=\\G.{%1$d})", splitLen));
}

Ein Beispiel in IdeOne: http://ideone.com/rNlTj5


3

Späte Einreise.

Es folgt eine kurze Implementierung unter Verwendung von Java8-Streams, einem Einzeiler:

String foobarspam = "foobarspam";
AtomicInteger splitCounter = new AtomicInteger(0);
Collection<String> splittedStrings = foobarspam
                                    .chars()
                                    .mapToObj(_char -> String.valueOf((char)_char))
                                    .collect(Collectors.groupingBy(stringChar -> splitCounter.getAndIncrement() / 3
                                                                ,Collectors.joining()))
                                    .values();

Ausgabe:

[foo, bar, spa, m]

5
"a one liner";)
Chris

1

Dies ist eine späte Antwort, aber ich stelle sie trotzdem für neue Programmierer zur Verfügung:

Wenn Sie keine regulären Ausdrücke verwenden möchten und sich nicht auf eine Bibliothek eines Drittanbieters verlassen möchten, können Sie stattdessen diese Methode verwenden, die in einer 2,80-GHz-CPU (weniger als eine Millisekunde) zwischen 89920 und 100113 Nanosekunden dauert. Es ist nicht so hübsch wie das Beispiel von Simon Nickerson, aber es funktioniert:

   /**
     * Divides the given string into substrings each consisting of the provided
     * length(s).
     * 
     * @param string
     *            the string to split.
     * @param defaultLength
     *            the default length used for any extra substrings. If set to
     *            <code>0</code>, the last substring will start at the sum of
     *            <code>lengths</code> and end at the end of <code>string</code>.
     * @param lengths
     *            the lengths of each substring in order. If any substring is not
     *            provided a length, it will use <code>defaultLength</code>.
     * @return the array of strings computed by splitting this string into the given
     *         substring lengths.
     */
    public static String[] divideString(String string, int defaultLength, int... lengths) {
        java.util.ArrayList<String> parts = new java.util.ArrayList<String>();

        if (lengths.length == 0) {
            parts.add(string.substring(0, defaultLength));
            string = string.substring(defaultLength);
            while (string.length() > 0) {
                if (string.length() < defaultLength) {
                    parts.add(string);
                    break;
                }
                parts.add(string.substring(0, defaultLength));
                string = string.substring(defaultLength);
            }
        } else {
            for (int i = 0, temp; i < lengths.length; i++) {
                temp = lengths[i];
                if (string.length() < temp) {
                    parts.add(string);
                    break;
                }
                parts.add(string.substring(0, temp));
                string = string.substring(temp);
            }
            while (string.length() > 0) {
                if (string.length() < defaultLength || defaultLength <= 0) {
                    parts.add(string);
                    break;
                }
                parts.add(string.substring(0, defaultLength));
                string = string.substring(defaultLength);
            }
        }

        return parts.toArray(new String[parts.size()]);
    }

1

Verwenden von einfachem Java:

    String s = "1234567890";
    List<String> list = new Scanner(s).findAll("...").map(MatchResult::group).collect(Collectors.toList());
    System.out.printf("%s%n", list);

Erzeugt die Ausgabe:

[123, 456, 789]

Beachten Sie, dass dadurch übrig gebliebene Zeichen verworfen werden (in diesem Fall 0).


0

Sie können auch eine Zeichenfolge bei jedem n-ten Zeichen teilen und jeweils in jeden Index einer Liste einfügen:

Hier habe ich eine Liste von Strings mit dem Namen Sequence erstellt:

List <String> Sequenz

Dann teile ich den String "KILOSO" im Grunde alle 2 Wörter. Also würde 'KI' 'LO' 'SO' in einen separaten Index der Liste namens Sequence aufgenommen.

String S = KILOSO

Sequence = Arrays.asList (S.split ("(? <= \ G ..)"));

Also, wenn ich mache:

System.out.print (Sequenz)

Es sollte gedruckt werden:

[KI, LO, SO]

um zu überprüfen, ob ich schreiben kann:

System.out.print (Sequence.get (1))

es wird gedruckt:

LO


0

Ich bin kürzlich auf dieses Problem gestoßen, und hier ist die Lösung, die ich gefunden habe

final int LENGTH = 10;
String test = "Here is a very long description, it is going to be past 10";

Map<Integer,StringBuilder> stringBuilderMap = new HashMap<>();
for ( int i = 0; i < test.length(); i++ ) {
    int position = i / LENGTH; // i<10 then 0, 10<=i<19 then 1, 20<=i<30 then 2, etc.

    StringBuilder currentSb = stringBuilderMap.computeIfAbsent( position, pos -> new StringBuilder() ); // find sb, or create one if not present
    currentSb.append( test.charAt( i ) ); // add the current char to our sb
}

List<String> comments = stringBuilderMap.entrySet().stream()
        .sorted( Comparator.comparing( Map.Entry::getKey ) )
        .map( entrySet -> entrySet.getValue().toString() )
        .collect( Collectors.toList() );
//done



// here you can see the data
comments.forEach( cmt -> System.out.println( String.format( "'%s' ... length= %d", cmt, cmt.length() ) ) );
// PRINTS:
// 'Here is a ' ... length= 10
// 'very long ' ... length= 10
// 'descriptio' ... length= 10
// 'n, it is g' ... length= 10
// 'oing to be' ... length= 10
// ' past 10' ... length= 8

// make sure they are equal
String joinedString = String.join( "", comments );
System.out.println( "\nOriginal strings are equal " + joinedString.equals( test ) );
// PRINTS: Original strings are equal true
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.