Java에서 문자열을 동일한 길이의 하위 문자열로 분할


125

"Thequickbrownfoxjumps"Java에서 문자열을 동일한 크기의 하위 문자열로 분할하는 방법 예 : "Thequickbrownfoxjumps"크기가 4와 같으면 출력해야합니다.

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

비슷한 질문 :

스칼라에서 문자열을 같은 길이의 하위 문자열로 분할


4
무엇을 시도 했습니까? 왜 작동하지 않습니까?
Thilo

2
이를 위해 정규식을 사용해야합니까? 그냥 ... 때문에 정규식 태그의 요구
팀 Pietzcker

그는 게시 @Thilo 링크는 그는 자바에서 거의 동일 요구하고, 스칼라입니다
Jaydeep 파텔

@ Thilo : scala에 대한 답변과 같이 java에서 어떻게 해야하는지 묻고있었습니다.
Emil

답변:


226

다음은 정규식 한 줄짜리 버전입니다.

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

\G이전 일치가 종료 된 위치와 일치하는 너비가 0 인 어설 션입니다. 이 경우 없었다 에는 이전의 일치, 그것은 입력의 시작, 같은 일치 \A. 둘러싼 lookbehind는 마지막 일치의 끝에서 네 문자 인 위치와 일치합니다.

Lookbehind와 \G고급 정규식 기능은 모든 맛에서 지원되는 것은 아닙니다. 또한 \G이를 지원하는 여러 가지 특성에 일관되게 구현되지 않습니다. 이 트릭은 Java , Perl, .NET 및 JGSoft에서 작동하지만 PHP (PCRE), Ruby 1.9+ 또는 TextMate (Oniguruma 모두)에서는 작동 하지 않습니다 . JavaScript /y(sticky flag)는만큼 유연 \G하지 않으며 JS가 lookbehind를 지원하더라도 이러한 방식으로 사용할 수 없습니다.

다른 옵션이있는 경우 반드시이 솔루션을 권장 하지는 않습니다 . 다른 답변의 비정규 솔루션은 더 길 수 있지만 자체 문서화도 가능합니다. 이것은 그것 의 반대 입니다. ;)

또한 Android에서 작동하지 않으므로 \Glookbehinds에서 의 사용을 지원하지 않습니다 .


2
PHP 5.2.4에서는 다음 코드가 작동합니다. return preg_split ( '/ (? <= \ G. {'. $ len. '}) / u', $ str, -1, PREG_SPLIT_NO_EMPTY);
Igor

5
레코드 String.substring()의 경우 정규식 대신 사용 하면 몇 줄의 추가 코드가 필요하지만 5 배 정도 빠른 속도로 실행됩니다.
moore moore

2
Java에서는 줄 바꿈이있는 문자열에는 작동하지 않습니다. 첫 번째 줄 바꿈까지만 확인하고 해당 줄 바꿈이 분할 크기보다 앞에 오는 경우 문자열이 분할되지 않습니다. 아니면 내가 놓친 것이 있습니까?
joensson

5
완성도를 높이기 위해 여러 줄로 텍스트를 나누 (?s)려면 정규 표현식 앞에 접두사 가 필요합니다 (?s)(?<=\\G.{4}).
bobbel

1
컴파일 타임에 이것에 대한 Java barfs :java.util.regex.PatternSyntaxException: Look-behind pattern matches must have a bounded maximum length
Jeffrey Blattman

132

간단한 산술 및 문자열 연산 으로이 작업을 수행하는 것이 매우 쉽습니다.

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

나는 이것을 위해 정규 표현식을 사용할 가치가 있다고 생각하지 않습니다.

편집 : 정규식을 사용하지 않는 이유 :

  • 이것은 정규식의 실제 패턴 일치를 사용하지 않습니다. 방금 계산 중입니다.
  • 대부분의 경우 중요하지 않지만 위의 내용이 더 효율적 이라고 생각 합니다.
  • 다른 장소에서 가변 크기를 사용해야하는 경우 매개 변수 -ick를 기반으로 정규 표현식 자체를 작성하는 반복 또는 도우미 함수가 있습니다.
  • 다른 답변으로 제공된 정규 표현식은 먼저 컴파일되지 않았으며 (잘못된 이스케이프 처리) 작동하지 않았습니다. 내 코드는 처음으로 작동했습니다. 그것은 정규 코드와 일반 코드 IMO의 유용성에 대한 증거입니다.

8
@ 에밀 : 사실, 당신 정규식을 요구 하지 않았습니다 . 태그에 있지만 질문 자체에는 정규 표현식을 요구하는 것이 없습니다. 이 방법을 한 곳에 두면 코드의 어느 곳에서나 읽기 쉬운 문장으로 문자열을 나눌 수 있습니다.
Jon Skeet

3
Emil 이것은 정규 표현식이 아닙니다. 기간.
Chris

3
@Emil : 줄을 나누기 위해 1 줄짜리 라이너를 원한다면 Splitter.fixedLength(4)seanizer가 제안한대로 Guava 를 추천합니다.
ColinD

2
@Jay : come-on 당신은 sarcastic 일 필요는 없습니다. 단지 한 줄로 정규 표현식을 사용하여 수행 할 수 있다고 확신합니다. 고정 길이 하위 문자열도 패턴입니다.이 답변에 대해 무엇을 말합니까? stackoverflow.com/questions/3760152/... .
Emil

4
@ 에밀 : 나는 그것이 무례하고, 기발한 것을 의도하지 않았습니다. 내 요점의 진지한 부분은 그렇습니다. 그렇지만 당신이 이것을하기 위해 Regex를 생각 해낼 수 있다고 확신했습니다 .Alan Moore는 그가 일한다고 주장하는 것을 가지고 있습니다. 이해하고 유지하십시오. 부분 문자열 솔루션은 직관적이고 읽기 쉽습니다. Jon Skeet의 네 번째 글 머리표 참조 : 나는 그 100 %에 동의합니다.
Jay

71

Google Guava를 사용하면 매우 쉽습니다 .

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

산출:

Theq
uick
brow
nfox
jump
s

또는 배열로 결과가 필요한 경우 다음 코드를 사용할 수 있습니다.

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

참고:

참고 : 스플리터 구성은 위에 인라인으로 표시되어 있지만 스플리터는 변경할 수없고 재사용 할 수 있으므로 상수로 저장하는 것이 좋습니다.

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

// more code

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

게시물에 감사드립니다 (구아바 라이브러리 방법을 알고 있기 위해).하지만 서드 파티 라이브러리와 하나의 라이너가 필요하지 않기 때문에 정규식 답변 stackoverflow.com/questions/3760152/… 를 수락 해야합니다.
Emil

1
이 간단한 작업을 수행하기 위해 수백 KB의 라이브러리 코드를 포함시키는 것은 옳지 않은 일입니다.
Jeffrey Blattman

2
구아바를 포함한 @JeffreyBlattman은 아마도 과잉 일 것입니다. 그러나 나는 왜 기능이 하나 개의 추가 조각을 사용하지 어쨌든, 내 모든 자바 코드에서 범용 라이브러리로 사용
숀 패트릭 플로이드

분리기와 다시 결합 할 수있는 방법이 있습니까?
물병 자리 힘

1
@AquariusPowerString.join(separator, arrayOrCollection)
Holger

14

구글의 구아바 범용 라이브러리를 사용하고 있다면 (그리고 솔직히 새로운 자바 프로젝트 라면 아마 ) Splitter 클래스를 사용하는 것은 그리 쉬운 일이 아닙니다 :

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

그게 다야 . 쉽게!


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()그리고 len둘 다 있기 때문에 int, 당신의 전화 ceiling 는 당신이 원하는 것을 성취하지 못합니다-다른 응답 중 일부가 어떻게하는지 확인하십시오 : (src.length () + len-1) / len
Michael Brewer-Davis

@ 마이클 : 좋은 지적입니다. 다중 길이가 아닌 문자열로 테스트하지 않았습니다. 이제 수정되었습니다.
사울

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
관심이 없다면 for루프 에 대해 뭔가가 있습니까?
Jon Skeet

for루프는 사실이 밖으로 포인팅이 :-) 감사에 대한보다 '자연'선택 사용하는 것입니다.
Grodriguez

3

(예외 처리) 또는 Apache lang commons ( 예외 처리) substring에서 사용할 수 있습니다.String.class

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

루프 안에 넣으면 갈 수 있습니다.


1
substring표준 String클래스 의 메소드에 어떤 문제가 있습니까?
Grodriguez

커먼즈 버전은 예외를 피합니다 (범위 외)
Thilo

7
내가 참조; 대신 호출 코드의 매개 변수를 제어하여 '예외를 피하십시오'를 선호한다고 말하고 싶습니다.
Grodriguez

2

오히려이 간단한 해결책을 원합니다.

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

이러지 마! 문자열은 변경할 수 없으므로 코드는 4 개의 문자마다 나머지 문자열 전체를 복사해야합니다. 따라서 스 니펫은 문자열 크기에서 선형 시간이 아닌 2 차 시간이 걸립니다.
Tobias

@Tobias : String이 변경 가능하더라도이 스 니펫은 언급 된 중복 사본을 수행하지만 복잡한 컴파일 프로세스가 있습니다. 이 스 니펫을 사용하는 유일한 이유는 코드 단순성입니다.
치타 코더

처음 게시 한 이후 코드를 변경 했습니까? 최신 버전은 실제로 사본을 만들지 않습니다. substring ()은 효율적으로 실행됩니다 (최소한 이전 버전의 Java에서는 일정 시간). 전체 문자열의 char [] (적어도 이전 버전의 Java)에 대한 참조를 유지하지만이 경우 모든 문자를 유지하기 때문에 좋습니다. 따라서 여기에있는 최신 코드는 실제로 괜찮습니다 (내용이 빈 문자열로 시작하면 코드가 빈 줄을 인쇄하는 모듈로 모듈이 의도하지 않을 수 있음).
Tobias

@Tobias : 나는 어떤 변화도 기억하지 못한다.
치타 코더

@Tobias는 클래스 에서 및 필드가 제거 된 substring2012 년 중반에 Java 7, 업데이트 6으로 구현이 변경되었습니다 . 따라서이 답변을 만들기 오래 전에 복잡성이 선형 으로 바뀌 었습니다. 그러나 예제와 같은 작은 문자열의 경우 여전히 충분히 빠르게 실행되고 더 긴 문자열의 경우…이 작업은 실제로 거의 발생하지 않습니다. offsetcountStringsubstring
Holger

2

다음은 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();

다음과 같은 출력을 제공합니다.

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

1
이것은 API의 의도와 싸우고, 상태 저장 함수를 사용하고, 복싱과 문자열 연결 오버 헤드를 말하는 것이 아니라 일반 루프보다 훨씬 더 복잡한 끔찍한 솔루션입니다. 스트림 솔루션을 원한다면 다음과 같이 사용하십시오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

Java 8 IntStream 을 사용하여 슬라이스 시작 색인을 결정 하는 한 줄짜리 버전이 있습니다 .

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

문자열을 똑같이 뒤로 (예 : 오른쪽에서 왼쪽으로) 분할하려는 경우 (예 :) 1010001111는 다음 [10, 1000, 1111]과 같습니다.

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

다음 Java 8 솔루션을 사용합니다.

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

(같은 자바 (8) 솔루션 있지만, 조금 더 간단) :

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에게 허용 된 솔루션 에 대한 의견을 물었습니다 . 그는 DOTALL 사용을 제안했습니다.

그의 제안을 사용하여 그 작동 방식에 대한 작은 샘플을 만들었습니다.

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

}

그러나 https://stackoverflow.com/a/3760193/1237974의 @Jon Skeets 솔루션도 좋아 합니다. 모든 사람이 정규 표현식을 똑같이 경험하지 않는 대규모 프로젝트의 유지 관리를 위해 Jons 솔루션을 사용했을 것입니다.


-1

또 다른 무차별 대입 솔루션은

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

코드가 부분 문자열로 문자열을 단계별로 이동하는 경우


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

결과

 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 및 Java 8 스트림을 기반으로 한 버전입니다. Matcher.results()Java 9부터 메소드를 사용할 수 있다는 점을 언급 할 가치 가 있습니다.

테스트가 포함되었습니다.

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;
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.