Java'da dizeyi eşit uzunlukta alt dizelere bölün


125

Dize "Thequickbrownfoxjumps"Java'da eşit büyüklükteki alt dizelere nasıl bölünür ? Örneğin. "Thequickbrownfoxjumps"4 eşit büyüklükte çıktı vermelidir.

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

Benzer Soru:

Scala'da dizeyi eşit uzunlukta alt dizelere bölün


4
Ne denedin Bu neden işe yaramadı?
Thilo

2
Bunun için bir normal ifade kullanmanız gerekiyor mu? Sadece normal ifade etiketi yüzünden soruyorum ...
Tim Pietzcker

Gönderdiği @Thilo bağlantısı Scala için, Java'da da aynı soruyu soruyor
Jaydeep Patel

@Thilo: Scala için verilen cevap gibi javada nasıl yapılır diye soruyordum.
Emil

Yanıtlar:


226

Tek satırlık normal ifade sürümü:

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

\Gönceki maçın bittiği konumla eşleşen sıfır genişlikli bir iddiadır. Önceki eşleşme yoksa , girişin başlangıcıyla aynı şekilde eşleşir \A. Çevreleyen bakış, son maçın sonundan itibaren dört karakter olan konumla eşleşir.

Her ikisi de geriye doğru bakma ve \Ggelişmiş normal ifade özellikleridir ve tüm tatlar tarafından desteklenmez. Ayrıca, \Gonu destekleyen tatlarda tutarlı bir şekilde uygulanmamaktadır. Bu numara (örneğin) Java , Perl, .NET ve JGSoft'ta çalışacak, ancak PHP (PCRE), Ruby 1.9+ veya TextMate'de (her ikisi de Oniguruma) çalışmayacaktır. JavaScript'ler /y(yapışkan bayrak) kadar esnek \Gdeğildir ve JS geriye bakmayı desteklese bile bu şekilde kullanılamaz.

Ben ille olmadığını belirtmeliyim tavsiye diğer seçenekleri varsa bu çözümü. Diğer yanıtlardaki normal ifade olmayan çözümler daha uzun olabilir, ancak aynı zamanda kendi kendini belgeliyorlar; bu hemen hemen bunun tam tersi . ;)

Ayrıca, bu, \Garkaya bakma kullanımını desteklemeyen Android'de çalışmaz .


2
PHP 5.2.4'te şu kod çalışır: return preg_split ('/ (? <= \ G. {'. $ Len. '}) / U', $ str, -1, PREG_SPLIT_NO_EMPTY);
Igor

5
Kayıt için, String.substring()bir normal ifade yerine kullanmak , birkaç ekstra kod satırı gerektirirken, 5 kat daha hızlı bir yerde çalışacaktır ...
moore çekti

2
Java'da bu, satırsonu olan bir dizede çalışmaz. Yalnızca ilk satırsonu satırına kadar kontrol eder ve bu satırsonu bölünmüş boyuttan önce gelirse, dizi bölünmez. Yoksa bir şeyi mi kaçırdım?
joensson

5
Multilines üzerinde bölme metin bir öneki gerekir: Bütünlük adına (?s)regex: (?s)(?<=\\G.{4}).
bobbel

1
Java, derleme sırasında bunu tamamen ortadan kaldırıyor:java.util.regex.PatternSyntaxException: Look-behind pattern matches must have a bounded maximum length
Jeffrey Blattman

132

Bunu basit aritmetik ve dizgi işlemleriyle yapmak oldukça kolaydır:

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

Bunun için bir normal ifade kullanmaya gerçekten değeceğini sanmıyorum.

DÜZENLEME: Normal ifade kullanmama nedenim:

  • Bu, normal ifadelerin gerçek örüntü eşleşmelerinden herhangi birini kullanmaz. Sadece sayıyor.
  • Ben şüpheli rağmen çoğu durumda bu mesele olmaz, yukarıda daha verimli olacak
  • Değişken boyutları farklı yerlerde kullanmanız gerekiyorsa, normal ifadeyi bir parametreye (ick) dayalı olarak oluşturmak için ya tekrar ya da yardımcı bir işleviniz vardır.
  • Başka bir yanıtta verilen normal ifade önce derlenmedi (geçersiz kaçış) ve sonra çalışmadı. Kodum ilk kez çalıştı. Bu daha çok normal ifadelerin kullanılabilirliğine karşı düz kod, IMO'nun bir kanıtıdır.

8
@Emil: Aslında vermedi bir regex isteyin. Etiketlerin içindedir, ancak sorudaki hiçbir şey normal ifade istemez. Bu yöntemi tek bir yere koyarsınız ve ardından dizeyi kodunuzun herhangi bir yerinde çok okunabilir tek bir ifadeye bölebilirsiniz .
Jon Skeet

3
Emil, bu bir normal ifadenin amacı değildir. Dönemi.
Chris

3
@Emil: Dizeyi bölmek için tek satırlık bir malzeme istiyorsanız, Splitter.fixedLength(4)seanizer'ın önerdiği gibi Guava's'ı öneririm.
ColinD

2
@Jay: hadi, bu kadar alaycı olmana gerek yok, eminim sadece tek satırda regex kullanılarak yapılabilir, sabit uzunlukta bir alt dizge de bir kalıptır, bu cevap hakkında ne dersin? stackoverflow.com/questions/3760152/… .
Emil

4
@Emil: Bunun kaba olmasını düşünmedim, sadece kaprisli. Demek istediğimin ciddi kısmı, evet, eminim bunu yapmak için bir Regex bulabilirsin - Görüyorum ki Alan Moore'un işe yaradığını iddia ettiği bir Regex var - bu şifreli ve bu nedenle sonraki bir programcı için zor anlamak ve sürdürmek. Bir alt dize çözümü sezgisel ve okunabilir olabilir. Jon Skeet'in 4. mermisine bakın: Buna% 100 katılıyorum.
Jay

71

Google Guava ile bu çok kolaydır :

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

Çıktı:

Theq
uick
brow
nfox
jump
s

Ya da sonuca bir dizi olarak ihtiyacınız varsa, bu kodu kullanabilirsiniz:

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

Referans:

Not: Ayırıcı yapısı yukarıda satır içi olarak gösterilmiştir, ancak Ayırıcılar değişmez ve yeniden kullanılabilir olduğundan, bunları sabitlerde saklamak iyi bir uygulamadır:

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

// more code

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

Gönderi için teşekkürler (Guava kitaplığı yönteminden haberdar olduğum için). Ancak herhangi bir üçüncü taraf kitaplığı ve tek satırlık bir kitaplık gerektirmediğinden , regex yanıtını stackoverflow.com/questions/3760152/… kabul etmeliyim .
Emil

1
Sadece bu basit görevi gerçekleştirmek için yüzlerce KB kütüphane kodu dahil etmek neredeyse kesinlikle doğru şey değildir.
Jeffrey Blattman

2
@JeffreyBlattman, sadece bunun için Guava dahil olmak üzere muhtemelen aşırıdır, doğru. Ama yine de onu tüm Java kodumda genel amaçlı bir kitaplık olarak kullanıyorum, öyleyse neden bu ek işlevsellik parçasını kullanmıyorsunuz
Sean Patrick Floyd

ayırıcı ile tekrar birleştirmenin bir yolu var mı?
Aquarius Power

1
@AquariusPowerString.join(separator, arrayOrCollection)
Holger

14

Google'ın guava genel amaçlı kitaplıklarını kullanıyorsanız (ve oldukça dürüst olmak gerekirse, herhangi bir yeni Java projesi muhtemelen olmalıdır ), bu Splitter sınıfı için delice önemsizdir :

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

ve işte bu . Kadar kolay!


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

Yana src.length()ve lenher ikisi de intler, çağrı ceiling (src.length () + len - 1) / len: diğer bazı cevapların nasıl yapıyorlar kontrol - Ne istediğini gerçekleştirerek değil
Michael Brewer-Davis

@Michael: İyi nokta. Birden fazla uzunlukta olmayan dizelerle test etmedim. Şimdi düzeltildi.
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
İlgisiz, fordöngülere karşı bir şeyiniz var mı?
Jon Skeet

Bir fordöngü aslında bunun için daha 'doğal' bir seçimdir :-) Bunu işaret ettiğiniz için teşekkürler.
Grodriguez

3

Sen kullanabilirsiniz substringdan String.class(durumları işleme) ya da gelen Apache lang commons (sizin için istisnalar kolları)

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

Bir döngü içine koyun ve gitmeniz iyi olur.


1
substringStandart Stringsınıftaki yöntemin nesi yanlış ?
Grodriguez

Ortak sürüm istisnalardan kaçınır (sınırların dışında ve benzeri)
Thilo

7
Anlıyorum; Bunun yerine çağıran koddaki parametreleri kontrol ederek 'istisnalardan kaçınmayı' tercih ettiğimi söyleyebilirim.
Grodriguez

2

Bu basit çözümü tercih ederim:

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

Bunu yapma! Dize değişmezdir, bu nedenle kodunuzun kalan dizenin tamamını her 4 karakterde bir kopyalaması gerekir. Snippet'iniz bu nedenle Dize boyutunda doğrusal zaman yerine ikinci dereceden zaman alır.
Tobias

@Tobias: String değiştirilebilir olsa bile, bu pasaj, söz konusu yedek kopyayı yapar, ancak bununla ilgili karmaşık derleme süreçleri vardır. Bu pasajı kullanmanın tek nedeni kod basitliğidir.
Cheetah Coder

Kodunuzu ilk yayınladığınızdan beri değiştirdiniz mi? En son sürüm gerçekte kopya oluşturmaz - substring () verimli bir şekilde çalışır (sabit zamanda, en azından Java'nın eski sürümlerinde); tüm dizenin karakterine [] bir gönderme yapar (en azından Java'nın eski sürümlerinde), ancak bu durumda tüm karakterleri tuttuğunuz için sorun değil. Yani burada sahip olduğunuz en son kod aslında tamamdır (eğer içerik boş dize olarak başlarsa, kodunuzun boş bir satır yazdırmasını modulo, ki bu, birinin niyet ettiği şey olmayabilir).
Tobias

@Tobias: Herhangi bir değişiklik hatırlamıyorum.
Cheetah Coder

@Tobias substringuygulaması, 2012 ortasında offsetve countalanları Stringsınıftan kaldırıldığında Java 7, güncelleme 6 ile değişti . Yani substringbu cevap verilmeden çok önce karmaşıklığı lineer hale geldi. Ancak örnek gibi küçük bir dizi için, hala yeterince hızlı ve daha uzun dizeler için çalışıyor… bu görev pratikte nadiren gerçekleşir.
Holger

2

İşte Java8 akışlarını kullanan tek satırlık bir uygulama:

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

Aşağıdaki çıktıyı verir:

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

1
Bu korkunç bir çözüm, API'nin amacına karşı savaşmak, durum bilgisi olan işlevleri kullanmak ve sıradan bir döngüden önemli ölçüde daha karmaşık olmak, boks ve dizi birleştirme ek yükünden bahsetmemek. Bir Akış çözümü istiyorsanız, aşağıdaki gibi bir şey kullanınString[] 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

İşte Java 8 kullanan tek satırlık bir sürüm IntStream dilim başlangıçların endeksleri belirlemek için:

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

Eğer bölmek, örneğin, eşit geriye dize bölmek, yani sağdan sola istediğiniz 1010001111için [10, 1000, 1111], burada kod:

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

aşağıdaki java 8 çözümünü kullanıyorum:

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

(Gibi Java 8 çözüm bu ama biraz daha basit):

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

@ Alan Moore'a kabul edilen çözüme bir yorumda satırsonu içeren dizelerin nasıl işlenebileceğini sordum . DOTALL kullanmayı önerdi.

Önerisini kullanarak bunun nasıl çalıştığına dair küçük bir örnek oluşturdum:

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

}

Ama @Jon Skeets çözümünü https://stackoverflow.com/a/3760193/1237974'te de seviyorum . Düzenli ifadelerde herkesin eşit derecede deneyimli olmadığı daha büyük projelerde sürdürülebilirlik için muhtemelen Jons çözümünü kullanırdım.


-1

Başka bir kaba kuvvet çözümü şunlar olabilir:

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

Kodun sadece alt dizelerle dizede geçtiği yer


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

Sonuç

 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

RegEx ve Java 8 akışlarına dayalı sürümüm. Bu Matcher.results()yöntemin Java 9'dan beri mevcut olduğunu belirtmekte fayda var .

Test dahildir.

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;
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.