Chia chuỗi thành các chuỗi con có độ dài bằng nhau trong Java


125

Cách chia chuỗi "Thequickbrownfoxjumps"thành chuỗi con có kích thước bằng nhau trong Java. Ví dụ. "Thequickbrownfoxjumps"4 kích thước bằng nhau sẽ cho đầu ra.

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

Câu hỏi tương tự:

Chia chuỗi thành các chuỗi có độ dài bằng nhau trong Scala


4
Bạn đã thử gì? Tại sao điều đó không làm việc?
Thilo

2
Bạn có cần sử dụng regex cho việc này không? Chỉ cần hỏi vì thẻ regex ...
Tim Pietzcker

@Thilo link anh ấy đăng là dành cho Scala, anh ấy đang hỏi về Java tương tự
Jaydeep Patel

@Thilo: Tôi đã hỏi làm thế nào để làm điều đó trong java, giống như câu trả lời được đưa ra cho scala.
Emil

Câu trả lời:


226

Đây là phiên bản regex one-liner:

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

\Glà một xác nhận có độ rộng bằng 0 phù hợp với vị trí mà trận đấu trước đó kết thúc. Nếu có không phù hợp trước, nó phù hợp với đầu của đầu vào, giống như \A. Giao diện kèm theo khớp với vị trí có bốn nhân vật từ cuối trận đấu cuối cùng.

Cả lookbehind và \Glà các tính năng regex tiên tiến, không được hỗ trợ bởi tất cả các hương vị. Hơn nữa, \Gkhông được thực hiện nhất quán trên các hương vị hỗ trợ nó. Thủ thuật này sẽ hoạt động (ví dụ) trong Java , Perl, .NET và JGSoft, nhưng không phải trong PHP (PCRE), Ruby 1.9+ hoặc TextMate (cả Oniguruma). JavaScript của/y (cờ dính) không linh hoạt như \Gvà không thể được sử dụng theo cách này ngay cả khi JS đã hỗ trợ giao diện.

Tôi nên đề cập rằng tôi không nhất thiết phải đề xuất giải pháp này nếu bạn có các lựa chọn khác. Các giải pháp phi regex trong các câu trả lời khác có thể dài hơn, nhưng chúng cũng tự ghi lại; cái này chỉ là về đối nghịch với điều đó. ;)

Ngoài ra, điều này không hoạt động trong Android, không hỗ trợ việc sử dụng \Gtrong lookbehinds.


2
Trong PHP 5.2.4 hoạt động theo mã sau: return preg_split ('/ (? <= \ G. {'. $ Len. '}) / U', $ str, -1, PREGinksLIT_NO_EMPTY);
Igor

5
Đối với bản ghi, sử dụng String.substring()thay vì biểu thức chính quy, trong khi yêu cầu thêm một vài dòng mã, sẽ chạy ở đâu đó theo thứ tự nhanh hơn 5x ...
đã vẽ

2
Trong Java, điều này không hoạt động đối với một chuỗi với các dòng mới. Nó sẽ chỉ kiểm tra tối đa dòng mới đầu tiên và nếu dòng mới đó xảy ra trước kích thước phân chia thì chuỗi sẽ không bị phân tách. Hay tôi đã bỏ lỡ điều gì?
joensson

5
Để hoàn thiện: chia văn bản trên nhiều dòng cần có tiền tố (?s)trong regex : (?s)(?<=\\G.{4}).
bobbel

1
Các barfs Java về điều này hoàn toàn vào thời gian biên dịch:java.util.regex.PatternSyntaxException: Look-behind pattern matches must have a bounded maximum length
Jeffrey Blattman

132

Chà, thật dễ dàng để làm điều này với các phép toán số học và chuỗi đơn giản:

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

Tôi không nghĩ rằng nó thực sự đáng sử dụng một regex cho việc này.

EDIT: Lý do của tôi không sử dụng regex:

  • Điều này không sử dụng bất kỳ mô hình khớp chính xác nào của regexes. Chỉ là đếm thôi.
  • Tôi nghi ngờ những điều trên sẽ hiệu quả hơn, mặc dù trong hầu hết các trường hợp nó không thành vấn đề
  • Nếu bạn cần sử dụng các kích thước thay đổi ở các vị trí khác nhau, bạn có chức năng lặp lại hoặc hàm trợ giúp để xây dựng biểu thức chính dựa trên tham số - ick.
  • Regex được cung cấp trong một câu trả lời khác trước tiên không được biên dịch (thoát không hợp lệ), và sau đó không hoạt động. Mã của tôi làm việc lần đầu tiên. Đó là một minh chứng cho khả năng sử dụng của regexes so với mã đơn giản, IMO.

8
@Emil: Thật ra, bạn không yêu cầu regex. Nó nằm trong các thẻ, nhưng không có gì trong câu hỏi yêu cầu một biểu thức chính quy. Bạn đặt phương thức này ở một nơi và sau đó bạn có thể chia chuỗi chỉ trong một câu lệnh rất dễ đọc ở bất kỳ đâu trong mã của bạn.
Jon Skeet

3
Emil đây không phải là một regex dành cho. Giai đoạn = Stage.
Chris

3
@Emil: Nếu bạn muốn có một lớp lót để tách chuỗi, tôi khuyên bạn nên sử dụng Guava Splitter.fixedLength(4)theo đề xuất của seanizer.
ColinD

2
@Jay: đến đây bạn không cần phải mỉa mai như vậy. Tôi chắc chắn rằng nó có thể được thực hiện bằng cách sử dụng regex chỉ trong một dòng. Chuỗi con có độ dài cố định cũng là một mẫu. Bạn nói gì về câu trả lời này. stackoverflow.com/questions/3760152/ cấp .
Emil

4
@Emil: Tôi không có ý định thô lỗ, chỉ hay thay đổi. Điểm nghiêm trọng của quan điểm của tôi là trong khi có, tôi chắc chắn rằng bạn có thể đưa ra một Regex để làm điều này - tôi thấy Alan Moore có một cái mà anh ta tuyên bố là hoạt động - nó khó hiểu và do đó, một lập trình viên sau này rất khó hiểu và duy trì. Một giải pháp chuỗi con có thể trực quan và dễ đọc. Xem viên đạn thứ 4 của Jon Skeet: Tôi đồng ý với 100% đó.
Jay

71

Điều này rất dễ dàng với Google Guava :

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

Đầu ra:

Theq
uick
brow
nfox
jump
s

Hoặc nếu bạn cần kết quả dưới dạng một mảng, bạn có thể sử dụng mã này:

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

Tài liệu tham khảo:

Lưu ý: Cấu trúc bộ chia được hiển thị nội tuyến ở trên, nhưng vì Bộ chia là bất biến và có thể tái sử dụng, nên nên lưu trữ chúng trong các hằng số:

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

// more code

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

Cảm ơn vì bài đăng (Để tôi biết về phương pháp thư viện ổi). Nhưng tôi sẽ phải chấp nhận câu trả lời regex stackoverflow.com/questions/3760152/NH vì nó không yêu cầu bất kỳ thư viện bên thứ 3 nào và một lớp lót.
Emil

1
Bao gồm hàng trăm KB mã thư viện chỉ để thực hiện nhiệm vụ đơn giản này gần như chắc chắn không phải là điều đúng đắn.
Jeffrey Blattman

2
@JeffreyBlattman bao gồm cả Guava chỉ vì điều này có lẽ là quá mức, đúng. Nhưng tôi vẫn sử dụng nó như một thư viện đa năng trong tất cả các mã Java của mình, vậy tại sao không sử dụng một chức năng bổ sung này
Sean Patrick Floyd

bất kỳ cách nào để tham gia trở lại với một dải phân cách?
Sức mạnh Bảo Bình

1
@AquariusPowerString.join(separator, arrayOrCollection)
Holger

14

Nếu bạn đang sử dụng của Google ổi thư viện có mục đích chung (và khá trung thực, bất kỳ dự án Java mới có thể nên được), đây là điên cuồng tầm thường với Splitter lớp:

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

và đó là . Dễ như!


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

src.length()lenlà cả hai int, cuộc gọi của bạn ceiling không thực hiện được những gì bạn muốn - hãy xem một số phản hồi khác đang thực hiện như thế nào: (src.length () + len - 1) / len
Michael Brewer-Davis

@Michael: Điểm tốt. Tôi đã không kiểm tra nó với các chuỗi không dài. Bây giờ nó đã được sửa.
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
Không quan tâm, bạn có cái gì chống lại forcác vòng lặp không?
Jon Skeet

Một forvòng lặp thực sự là một lựa chọn 'tự nhiên' hơn cho việc này :-) Cảm ơn bạn đã chỉ ra điều này.
Grodriguez

3

Bạn có thể sử dụng substringtừ String.class(xử lý ngoại lệ) hoặc từ Apache lang commons (nó xử lý ngoại lệ cho bạn)

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

Đặt nó trong một vòng lặp và bạn tốt để đi.


1
Có gì sai với substringphương thức trong Stringlớp tiêu chuẩn ?
Grodriguez

Phiên bản commons tránh các trường hợp ngoại lệ (ngoài giới hạn và như vậy)
Thilo

7
Tôi hiểu rồi; Tôi sẽ nói rằng tôi thích 'tránh ngoại lệ' bằng cách kiểm soát các tham số trong mã gọi thay thế.
Grodriguez

2

Tôi muốn giải pháp đơn giản hơn:

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

Đừng làm điều này! Chuỗi là bất biến nên mã của bạn cần sao chép toàn bộ chuỗi còn lại cứ sau 4 ký tự. Do đó, đoạn mã của bạn mất phương trình bậc hai thay vì thời gian tuyến tính theo kích thước của Chuỗi.
Tobias

@Tobias: Ngay cả khi String có thể thay đổi, đoạn mã này thực hiện bản sao dự phòng được đề cập, ngoại trừ có các quy trình biên dịch phức tạp liên quan đến nó. Lý do duy nhất để sử dụng đoạn mã này là sự đơn giản mã.
Cheetah Coder

Bạn đã thay đổi mã của bạn kể từ lần đầu tiên bạn đăng nó? Phiên bản mới nhất không thực sự tạo ra các bản sao - chuỗi con () chạy hiệu quả (thời gian không đổi, ít nhất là trên các phiên bản cũ của Java); nó giữ một tham chiếu đến toàn bộ chuỗi char [] (ít nhất là trên các phiên bản Java cũ), nhưng trong trường hợp này vẫn ổn vì bạn giữ tất cả các ký tự. Vì vậy, mã mới nhất mà bạn có ở đây thực sự ổn (modulo rằng mã của bạn in một dòng trống nếu nội dung bắt đầu dưới dạng chuỗi trống, có thể không phải là thứ mà người ta dự định).
Tobias

@Tobias: Tôi không nhớ bất kỳ thay đổi nào.
Cheetah Coder

@Tobias việc substringtriển khai đã thay đổi với Java 7, cập nhật 6 vào giữa năm 2012, khi các trường offsetcounttrường bị xóa khỏi Stringlớp. Vì vậy, sự phức tạp của việc substringchuyển sang tuyến tính từ lâu trước khi câu trả lời này được thực hiện. Nhưng đối với một chuỗi nhỏ như ví dụ, nó vẫn chạy đủ nhanh và đối với các chuỗi dài hơn thì nhiệm vụ này hiếm khi xảy ra trong thực tế.
Holger

2

Đây là một triển khai thực hiện bằng cách sử dụng các luồng 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();

Nó cho đầu ra sau:

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

1
Đó là một giải pháp khủng khiếp, chống lại ý định của API, sử dụng các hàm trạng thái và phức tạp hơn đáng kể so với vòng lặp thông thường, không nói về quyền anh và quyền kết nối chuỗi. Nếu bạn muốn một giải pháp Stream, hãy sử dụng một cái gì đó nhưString[] 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

Đây là phiên bản một lớp sử dụng Java 8 IntStream để xác định các chỉ mục của phần bắt đầu lát:

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

Trong trường hợp bạn muốn phân chia chuỗi bằng nhau về phía sau, ví dụ từ phải sang trái, ví dụ, để phân tách 1010001111thành [10, 1000, 1111], đây là mã:

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

tôi sử dụng giải pháp java 8 sau:

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

Giải pháp Java 8 (như thế này nhưng đơn giản hơn một chút):

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

Tôi đã hỏi @Alan Moore trong một bình luận cho giải pháp được chấp nhận về cách xử lý các chuỗi với dòng mới. Ông đề nghị sử dụng DOTALL.

Sử dụng gợi ý của anh ấy, tôi đã tạo ra một mẫu nhỏ về cách thức hoạt động:

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

}

Nhưng tôi cũng thích giải pháp @Jon Skeets trong https://stackoverflow.com/a/3760193/1237974 . Để duy trì trong các dự án lớn hơn, nơi không phải ai cũng có kinh nghiệm như nhau trong các biểu thức chính quy, có lẽ tôi sẽ sử dụng giải pháp Jons.


-1

Một giải pháp vũ phu khác có thể là,

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

Trường hợp mã chỉ bước qua chuỗi với chuỗi con


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

Kết quả

 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

Đây là phiên bản của tôi dựa trên các luồng RegEx và Java 8. Điều đáng nói là Matcher.results()phương thức này có sẵn kể từ Java 9.

Bao gồm kiểm tra.

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;
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.