Split string ke substring dengan panjang yang sama di Jawa

125

Cara membagi string "Thequickbrownfoxjumps"ke substring dengan ukuran yang sama di Jawa. Misalnya. "Thequickbrownfoxjumps"dari 4 ukuran yang sama harus memberikan output.

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

Pertanyaan serupa:

Pisahkan string menjadi substring dengan panjang yang sama di Scala

Emil
sumber
4
Apa yang kamu coba? Mengapa itu tidak berhasil?
Thilo
2
Apakah Anda perlu menggunakan regex untuk ini? Hanya bertanya karena label regex ...
Tim Pietzcker
@Thilo tautan yang dia posting adalah untuk Scala, dia menanyakan hal yang sama di Jawa
Jaydeep Patel
@ Thilo: Saya bertanya bagaimana melakukannya di java, seperti jawaban yang diberikan untuk scala.
Emil

Jawaban:

226

Berikut versi satu-baris regex:

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

\Gadalah pernyataan nol-lebar yang cocok dengan posisi di mana pertandingan sebelumnya berakhir. Jika ada itu tidak ada pertandingan sebelumnya, cocok dengan awal input, sama seperti \A. Tampilan terlampir di belakang cocok dengan posisi yang terdiri dari empat karakter sejak akhir pertandingan terakhir.

Keduanya terlihat di belakang dan \Gmerupakan fitur regex canggih, tidak didukung oleh semua rasa. Selain itu, \Gtidak diterapkan secara konsisten di seluruh rasa yang mendukungnya. Trik ini akan berfungsi (misalnya) di Java , Perl, .NET dan JGSoft, tetapi tidak dalam PHP (PCRE), Ruby 1.9+ atau TextMate (keduanya Oniguruma). JavaScript /y(flag lengket) tidak sefleksibel \G, dan tidak dapat digunakan dengan cara ini meskipun JS memang mendukung tampilan di belakang.

Saya harus menyebutkan bahwa saya tidak selalu merekomendasikan solusi ini jika Anda memiliki opsi lain. Solusi non-regex dalam jawaban lain mungkin lebih lama, tetapi mereka juga mendokumentasikan diri sendiri; ini hanya kebalikan dari itu. ;)

Juga, ini tidak berfungsi di Android, yang tidak mendukung penggunaan tampilan \Gdi belakang.

Alan Moore
sumber
2
Dalam PHP 5.2.4 berfungsi kode berikut: return preg_split ('/ (? <= \ G. {'. $ Len. '}) / U', $ str, -1, PREG_SPLIT_NO_EMPTY);
Igor
5
Sebagai catatan, menggunakan String.substring()alih-alih regex, sementara membutuhkan beberapa baris kode tambahan, akan berjalan di suatu tempat dengan urutan 5x lebih cepat ...
drore moore
2
Di Jawa ini tidak berfungsi untuk string dengan baris baru. Itu hanya akan memeriksa ke baris baru pertama, dan jika baris baru itu terjadi sebelum ukuran split, maka string tidak akan terpecah. Atau apakah saya melewatkan sesuatu?
joensson
5
Demi kelengkapan: teks membelah lebih multilines membutuhkan diawali (?s)di regex: (?s)(?<=\\G.{4}).
bobbel
1
Java muntah dalam hal ini pada saat kompilasi:java.util.regex.PatternSyntaxException: Look-behind pattern matches must have a bounded maximum length
Jeffrey Blattman
132

Yah, cukup mudah untuk melakukan ini dengan operasi aritmatika dan string sederhana:

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

Saya tidak berpikir itu benar-benar layak menggunakan regex untuk ini.

EDIT: Alasan saya untuk tidak menggunakan regex:

  • Ini tidak menggunakan salah satu dari pencocokan pola nyata regex. Itu hanya menghitung.
  • Saya menduga hal di atas akan lebih efisien, meskipun dalam kebanyakan kasus itu tidak masalah
  • Jika Anda perlu menggunakan ukuran variabel di tempat yang berbeda, Anda memiliki pengulangan atau fungsi pembantu untuk membangun regex itu sendiri berdasarkan parameter - ick.
  • Regex yang disediakan dalam jawaban lain pertama tidak dikompilasi (melarikan diri tidak valid), dan kemudian tidak berhasil. Kode saya berfungsi pertama kali. Itu lebih merupakan bukti kegunaan regex vs kode biasa, IMO.
Jon Skeet
sumber
8
@ Emil: Sebenarnya, Anda tidak meminta regex. Ada dalam tag, tetapi tidak ada dalam pertanyaan itu sendiri yang meminta regex. Anda meletakkan metode ini di satu tempat, dan kemudian Anda dapat membagi string hanya dalam satu pernyataan yang sangat mudah dibaca di mana saja dalam kode Anda.
Jon Skeet
3
Emil ini bukan untuk apa regex. Titik.
Chris
3
@ Emil: Jika Anda ingin satu-liner untuk memisahkan string, saya akan merekomendasikan Guava's Splitter.fixedLength(4)seperti yang disarankan oleh seanizer.
ColinD
2
@ Jay: ayolah kamu tidak perlu sarkastik itu. Aku yakin itu bisa dilakukan dengan menggunakan regex hanya dalam satu baris. Sub-string panjang tetap juga sebuah pola. Apa yang kamu katakan tentang jawaban ini. stackoverflow.com/questions/3760152/… .
Emil
4
@ Emil: Saya tidak bermaksud kasar, hanya aneh. Bagian penting dari poin saya adalah bahwa sementara ya, saya yakin Anda bisa membuat Regex untuk melakukan ini - saya melihat Alan Moore memiliki satu yang ia klaim berfungsi - itu samar dan karenanya sulit bagi programmer selanjutnya untuk mengerti dan memelihara. Solusi substring dapat menjadi intuitif dan mudah dibaca. Lihat peluru ke-4 Jon Skeet: Saya setuju dengan itu 100%.
Jay
71

Ini sangat mudah dengan Google Guava :

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

Keluaran:

Theq
uick
brow
nfox
jump
s

Atau jika Anda membutuhkan hasilnya sebagai array, Anda dapat menggunakan kode ini:

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

Referensi:

Catatan: Konstruksi splitter ditunjukkan pada baris di atas, tetapi karena Splitter tidak dapat diubah dan dapat digunakan kembali, itu praktik yang baik untuk menyimpannya dalam konstanta:

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

// more code

for(final String token : FOUR_LETTERS.split("Thequickbrownfoxjumps")){
    System.out.println(token);
}
Sean Patrick Floyd
sumber
Terima kasih atas posnya (Untuk membuat saya sadar akan metode perpustakaan jambu biji). Tetapi saya harus menerima jawaban regex stackoverflow.com/questions/3760152/... karena tidak memerlukan perpustakaan pihak ke-3 dan satu-liner.
Emil
1
Termasuk ratusan KB kode perpustakaan hanya untuk melakukan tugas sederhana ini hampir pasti bukan hal yang benar.
Jeffrey Blattman
2
@ JeffreyBlattman termasuk Guava hanya untuk ini mungkin berlebihan, benar. Tapi saya menggunakannya sebagai pustaka serba guna di semua kode Java saya, jadi mengapa tidak menggunakan fungsionalitas tambahan ini
Sean Patrick Floyd
Adakah cara untuk bergabung kembali dengan pemisah?
Aquarius Power
1
@AquariusPowerString.join(separator, arrayOrCollection)
Holger
14

Jika Anda menggunakan perpustakaan serba guna jambu Google (dan sejujurnya, proyek Java baru mana pun seharusnya ), ini adalah hal sepele yang gila dengan kelas Splitter :

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

dan hanya itu . Semudah!

Cowan
sumber
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;
}
Saulus
sumber
Karena src.length()dan lenkeduanya ints, panggilan Anda ceiling tidak mencapai apa yang Anda inginkan - memeriksa bagaimana beberapa tanggapan lain melakukannya: (src.length () + len - 1) / len
Michael Brewer-Davis
@Michael: Poin bagus. Saya tidak mengujinya dengan string tanpa panjang multi-panjang. Sudah diperbaiki sekarang.
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;
}
Grodriguez
sumber
6
Karena ketertarikan, apakah Anda memiliki sesuatu yang bertentangan for?
Jon Skeet
Sebuah forloop memang pilihan penggunaan yang lebih 'alami' untuk ini :-) Terima kasih telah menunjukkan ini.
Grodriguez
3

Anda dapat menggunakan substringdari String.class(menangani pengecualian) atau dari Apache lang commons (ini menangani pengecualian untuk Anda)

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

Letakkan di dalam lingkaran dan Anda siap melakukannya.

pakore
sumber
1
Apa yang salah dengan substringmetode di Stringkelas standar ?
Grodriguez
Versi umum menghindari pengecualian (di luar batas dan semacamnya)
Thilo
7
Saya melihat; Saya akan mengatakan saya lebih memilih untuk 'menghindari pengecualian' dengan mengontrol parameter dalam kode panggilan.
Grodriguez
2

Saya lebih suka solusi sederhana ini:

String content = "Thequickbrownfoxjumps";
while(content.length() > 4) {
    System.out.println(content.substring(0, 4));
    content = content.substring(4);
}
System.out.println(content);
Cheetah Coder
sumber
Jangan lakukan ini! String tidak dapat diubah sehingga kode Anda perlu menyalin seluruh string yang tersisa setiap 4 karakter. Karena itu cuplikan Anda membutuhkan kuadrat daripada waktu linier dalam ukuran Tali.
Tobias
@Tobias: Sekalipun String bisa diubah, cuplikan ini melakukan salinan redundan yang disebutkan, kecuali ada proses kompilasi rumit tentangnya. Satu-satunya alasan untuk menggunakan cuplikan ini adalah kesederhanaan kode.
Cheetah Coder
Apakah Anda mengubah kode Anda sejak pertama kali mempostingnya? Versi terbaru tidak benar-benar membuat salinan - substring () berjalan secara efisien (waktu yang konstan, setidaknya pada versi lama Java); itu membuat referensi ke karakter seluruh string [] (setidaknya pada versi Jawa yang lama), tapi tidak masalah dalam hal ini karena Anda menyimpan semua karakter. Jadi kode terbaru yang Anda miliki di sini sebenarnya tidak apa-apa (modulo bahwa kode Anda mencetak baris kosong jika konten dimulai sebagai string kosong, yang mungkin bukan yang diinginkan).
Tobias
@Tobias: Saya tidak ingat perubahan apa pun.
Cheetah Coder
@Tobias substringimplementasinya berubah dengan Java 7, perbarui 6 pada pertengahan 2012, ketika offsetdan countbidang dihapus dari Stringkelas. Jadi kompleksitas substringberalih ke linear jauh sebelum jawaban ini dibuat. Tetapi untuk string kecil seperti contoh, itu masih berjalan cukup cepat dan untuk string yang lebih lama ... yah tugas ini jarang terjadi dalam praktek.
Holger
2

Berikut ini adalah implementasi satu liner menggunakan stream Java8:

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

Ini memberikan output sebagai berikut:

[Theq, uick, brow, nfox, jump, s]
Pankaj Singhal
sumber
1
Itu solusi yang mengerikan, melawan niat API, menggunakan fungsi stateful dan secara signifikan lebih rumit daripada loop biasa, tidak untuk berbicara tentang overhead tinju dan rangkaian string. Jika Anda menginginkan solusi Stream, gunakan sesuatu sepertiString[] 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

Berikut ini adalah versi satu-baris yang menggunakan Java 8 IntStream untuk menentukan indeks awal irisan:

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);
Marko Previsic
sumber
1

Jika Anda ingin membagi string sama mundur, yaitu dari kanan ke kiri, misalnya untuk membagi 1010001111ke [10, 1000, 1111], inilah kodenya:

/**
 * @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;
}
Ivan Huang
sumber
1

saya menggunakan solusi java 8 berikut:

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());
}
rloeffel
sumber
0

Solusi Java 8 (seperti ini tetapi sedikit lebih sederhana):

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;
}
Timofey Gorshkov
sumber
-1

Saya bertanya kepada @Alan Moore dalam sebuah komentar atas solusi yang diterima bagaimana string dengan baris baru dapat ditangani. Dia menyarankan menggunakan DOTALL.

Menggunakan sarannya, saya membuat sampel kecil bagaimana itu bekerja:

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
     */

}

Tapi saya suka solusi @Jon Skeets di https://stackoverflow.com/a/3760193/1237974 juga. Untuk pemeliharaan dalam proyek yang lebih besar di mana tidak semua orang sama-sama berpengalaman dalam ekspresi reguler, saya mungkin akan menggunakan solusi Jons.

joensson
sumber
-1

Solusi brute force lainnya adalah,

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

Di mana kode hanya melangkah melalui string dengan substring

Hubbly
sumber
-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));
    }}

Hasil

 Enter String
 Thequickbrownfoxjumps
 Enter length Of Sub-string
 4

 ["Theq","uick","brow","nfox","jump","s"]
Ravichandra
sumber
-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);
}
Adrian-Bogdan Ionescu
sumber
-1

Ini adalah versi saya berdasarkan aliran RegEx dan Java 8. Perlu disebutkan bahwa Matcher.results()metode ini tersedia sejak Java 9.

Tes disertakan.

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());
}
itachi
sumber
-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;
}
User8461
sumber
-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;
    }
Raj Hirani
sumber