개발새발

String.split() 본문

java

String.split()

무비인 2022. 2. 20. 19:29

String.split() 의 비밀

자동차 이름 예외처리를 하다가 "pobi, jun," 처럼

문자의 제일 끝에 ,가 오는 경우에 예외처리를 해줘야 할 것 같았는데,

예외 처리를 하지 않아도 동작에 이상이 없는것이 이상했다. 그래서 알아보기로 했다.

String.split() 의 동작 원리

평소 사용하던 split() 내부를 보니 , limit 기본이 0 으로 설정되어 있었다.

스크린샷 2021-12-05 오후 8 05 13

split 에 있는 limit의 3가지 역할

생각보다 limit 는 많은 역할을 하고 있었다.

스크린샷 2021-12-05 오후 8 05 13

  • limit 0 이면 맨뒤에 부터 문자가 1이상 전까지 공백이 제거됨. (그래서 "pobi, jun,,".split(",") 일 때 배열 크기가 2 였음. )
  • limit 음수면 공백 포함됨
  • limit 양수면 공백 포함, 크기가 limit만큼 제한됨 limit=3 이면 배열의 크기가 최대 3까지임.

limit의 0 일 때 예시

여기서 조금 특이한 상황이 생긴다. 일단 설명해본다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 1 번째
 
String winners ="aaa,bbb,ccc,"
String[] list = winners.split(",");
 
=> ["aaa","bbb","ccc"] 이때 맨뒤에 공백이 제거되어 4개가 아니라 3개만 배열로 만들어진다.
 
 
// 2 번째
 
String winners ="aaa,,,"
String[] list = winners.split(",");
 
=> ["aaa"] 맨 뒤부터 공백이면 제거되다가, 문자 사이즈가 1이상이면 공백 제거를 멈춘다.
 
 따라서 aaa만 배열로 만들어졌다. 
 
 
// 3번째
 
String winners ="aaa,,bb,,"
String[] list = winners.split(",");
 
=> ["aaa","","bb"] 맨 뒤부터 공백이면 제거되다가, 문자 사이즈가 1이상이면 공백 제거를 멈추게 되면
 
중간에 있는 공백은 제거가 안된다. 따라서 aaa,공백,bb 가 배열로 만들어졌다.
cs

limit의 양수 일 때 예시

배열 크기가 제한된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1 번째
 
String winners ="aaa,bbb,ccc,"
String[] list = winners.split(",",2);
 
=> ["aaa","bbb,ccc,"] 배열 크기가 limit 가 되면 나머지는 regex로 잘리지 않고 그대로 반환된다.
 
 
// 2 번째
 
String winners ="aaa,,,"
String[] list = winners.split(",",6);
 
=> ["aaa","","",""] 공백 포함해서 4개의 배열이 리턴된다. 양수는 공백도 포함한다.
cs

limit의 음수 일 때 예시

공백이 포함되고, 크기에 상관 없이 반환된다.

1
2
3
4
5
6
7
8
 
// 1 번째
 
String winners ="aaa,bbb,ccc,"
String[] list = winners.split(",",-1);
 
=> ["aaa","bbb",ccc",""] 전부다 잘려서 총 4의 크기로 반환된다.
 
 
cs

내부 구조 설명

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
   public String[] split(String regex, int limit) {
 
        char ch = 0;
        if (생략)
        {
            int off = 0;
            int next = 0;
            boolean limited = limit > 0//  0 , -1 면 false, 1 이상이면 true;
            ArrayList<String> list = new ArrayList<>();
            while ((next = indexOf(ch, off)) != -1) { // String 문자열 길이만큼 반복
                if (!limited || list.size() < limit - 1) { 
                    /**
                      limited 가 false 이거나, list.size 가 < limit -1 
                       이 조건은 0, 이나 음수일 경우, 배열 크기에 상관없이 
                       우리가 지정한 regex를 기준으로 값을 분리해서 반환.
                    **/
                    // 
                    list.add(substring(off, next));
                    off = next + 1;
                } else {    // last one
 
                    // limit 가 음수이면, 그 갯수만큼 반환하기위해 break
                    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()) {
             /**
              배열에 맨 뒤부터 공백을 확인하면서, 크기를 줄임. 만약 1이상인 문자열 만나면 그대로 반복문 나감.
             **/
                    resultSize--;
                }
            }
            String[] result = new String[resultSize]; // 줄여진 배열 크기만큼 생성
            return list.subList(0, resultSize).toArray(result); 
// 줄여진 길이만큼만 배열로 만들어서 반환
        }
        return Pattern.compile(regex).split(this, limit);
   }

    cs

내용을 돌아보며 (feat. String.split () 은 중간 공백을 잡지 못한다.)

처음엔 문자열 마지막에 , 이 왔을 경우 에러가 나겠다 !!! 생각했는데

"sudal,,fobi" 이렇게 중간에 쉼표가 들어오면 에러가 날 수 있다는 것을 알게 되었다...!!!

따라서 나는 아래와 같이 에러처리를 해주었다.

5번

느낀점

메서드 내부를 구경하다보니 시간이 훌쩍 지났다..

구현체들의 기능만 알고 사용해왔는데, 내부 로직을 살펴보니 응용해서 쓰거나,

주의해서 사용해야할 부분을 발견하게 되었다. 

끈기 있게 살펴보기 잘했다는 생각이 든다. 

참고