String.split의 limit?

주노·2023년 4월 4일
0
post-thumbnail

서론

콤마(,)를 기준으로 사람이름을 구분하려고 한다.
이 때 String에 정의되어있는 split 메소드를 사용하면 다음과 같이 사용할 수 있다.

public void split() {
    String names = "주노, 누누, 필립, 폴로";
    String[] split = names.split(",");
    ...
}

그런데 split 함수는 limit라는 인자도 가질 수 있다..?!

문제인식

위와 같은 입력이 이뤄졌을 때 뒤에 오는 ,,라는 문자로 인해 잘못된 입력이라는 결과를 기대할 수 있다.

하지만 위 입력을 처리하는 로직은 다음과 같이 구성되어있다.

public List<String> readParticipantName() {
    System.out.println("게임에 참여할 사람의 이름을 입력하세요.(쉼표 기준으로 분리)");
    final String input = scanner.nextLine();
    return splitByComma(input);
}

private List<String> splitByComma(final String input) {
    return List.of(input.split(","));
}

입력이 정상적으로 수행된다..? 왜 이런일이 일어날까?

String.split 메소드가 limit 인자를 받으면서 해당 문제를 어떻게 해결할 수 있을 지 알아보자

Limit 인자의 역할?

String 클래스에 정의되어있는 메소드를 살펴보면 다음과 같이 작성되어있다.

public String[] split(String regex, int limit) {
    /* fastpath if the regex is a
     (1)one-char String and this character is not one of the
        RegEx's meta characters ".$|()[{^?*+\\", or
     (2)two-char String and the first char is the backslash and
        the second is not the ascii digit or ascii letter.
     */
    char ch = 0;
    if (((regex.length() == 1 &&
         ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
         (regex.length() == 2 &&
          regex.charAt(0) == '\\' &&
          (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
          ((ch-'a')|('z'-ch)) < 0 &&
          ((ch-'A')|('Z'-ch)) < 0)) &&
        (ch < Character.MIN_HIGH_SURROGATE ||
         ch > Character.MAX_LOW_SURROGATE))
    {
        int off = 0;
        int next = 0;
        boolean limited = limit > 0;
        ArrayList<String> list = new ArrayList<>();
        while ((next = indexOf(ch, off)) != -1) {
            if (!limited || list.size() < limit - 1) {
                list.add(substring(off, next));
                off = next + 1;
            } else {    // last one
                //assert (list.size() == limit - 1);
                int last = length();
                list.add(substring(off, last));
                off = last;
                break;
            }
        }
        // If no match was found, return this
        if (off == 0)
            return new String[]{this};
        // Add remaining segment
        if (!limited || list.size() < limit)
            list.add(substring(off, length()));
        // Construct result
        int resultSize = list.size();
        if (limit == 0) {
            while (resultSize > 0 && list.get(resultSize - 1).isEmpty()) {
                resultSize--;
            }
        }
        String[] result = new String[resultSize];
        return list.subList(0, resultSize).toArray(result);
    }
    return Pattern.compile(regex).split(this, limit);
}

대충 읽어봐서는 무슨소린지 잘 모르겠으니 3줄 요약을 해보자.

크게 limit인자에 대해 3가지의 경우로 나뉜다.

limit > 0

limit가 0보다 클 경우 limit 개수만큼 split을 진행한다.

@Test
void splitTest() {
    String names = "주노-누누-폴로-----";
    String[] split = names.split("-", 2);
    System.out.println(Arrays.toString(split));
}

결과는 다음과 같다.

[주노, 누누-폴로-----]

limit == 0

limit가 0일 경우 맨 뒤의 값이 빈값이면 생략한다.
split(",")처럼 limit인자가 주어지지 않으면 기본적으로 limit = 0인 상태로 split 메소드가 동작한다.

@Test
void splitTest() {
    String names = "주노-누누-폴로-----";
    String[] split = names.split("-", 0);
    System.out.println(Arrays.toString(split));
}

결과는 다음과 같다.

[주노, 누누, 폴로]

limit < 0

limit가 0보다 작을 경우 맨 뒤의 빈값에 대해 split을 모두 진행한다.

어떤 의미인지 한번 알아보자.

@Test
void splitTest() {
    String names = "주노-누누-폴로-----";
    String[] split = names.split("-", -1);
    System.out.println(Arrays.toString(split));
}

결과는 다음과 같다.

[주노, 누누, 폴로, , , , , ]

끝의 비어있는 값에 대해서도 split을 진행하는 것을 알 수 있다.

결론

문제상황에서 겪었던 잘못된 입력에 대해 정상적인 validation 과정을 거치기 위해서는 -1 옵션을 추가해주는 방향으로 진행해볼 수 있겠다. 👍

Reference

String split method limit 이용

profile
안녕하세요 😆

0개의 댓글