gone

#[백준] 1152 단어의 개수(StringTokenizer 클래스) 본문

알고리즘

#[백준] 1152 단어의 개수(StringTokenizer 클래스)

예쁨받는 돌맹이 2024. 1. 12. 09:47

 

 

이번 문제는 아무리 봐도 내가 틀린 이유를 도저히 알 수가 없어서 답안지를 참고했으므로 오답노트의 형식으로 풀어갈 것이다. 참고 사이트는 늘 그렇듯 아래와 같다.

 

제출1) 에러

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str = br.readLine();

        // Q. 문자열에 들어가 있는 단어의 총개수 구하기
        // 조건1. 같은 문자여도 카우트해야함.
        // 조건2. 앞뒤 공백이 있을수 있다.

        String[] strarr = str.trim().split(" ");

        System.out.println(strarr.length);

    }
}

 

 

split() 을 이용해서 오답자가 상당히 많이 존재하는 것 같다. 우선 split () 메서드에 대해서 정확히 파악해야 될 것 같다.

 

split () 이란?

더보기
/*
 *  Splits this string around matches of the given regular expression.

 *  This method works as if by invoking the two-argument split method with the given expression and a limit argument of zero. 
 Trailing empty strings are therefore not included in the resulting array.

 *  The string "boo:and:foo", for example, yields the following results with these expressions:

 *  Regex	Result
 *  :	`` { "boo", "and", "foo" }}
 *  o	`` { "b", "", ":and:f" }}
*/

public String[] split(String regex) {
    return split(regex, 0);
}

 

" 이 메서드는 주어진 정규식에 일치하는 부분을 기준으로 문자를 나눈다. "

이 메서드는 주어진 표현식과 제한 인자 0을 둔 인자를 사용하여 두 인자로 된 `split` 메서드를 호출하는 것과 같은 방식으로 동작합니다. 따라서 결과 배열에는 끝에 있는 빈 문자열이 포함되지 않습니다.

java 공식 api 문서에서 발최한 글이다. 결국 가장 중요한 단어는 정규식에 해당하는 부분을 기준으로 나누기 때문에 제출 1)의 코드도 맞잖아!! 했지만 반전이 숨어있었다.

 

 

 

split함수를 더 깊게 파보게 되면 아래와 같은 코드를 찾을 수 있다.

우선,

제출 1) 코드를 다시 실행해서 공백 또는 줄 바꿈만 해보자. 그럼 결과가 1이 나오는 것을 확인할 수 있다.

이제  split함수에 대해 알아보자. java api 문서에서는 아래와 같이 설명한다.

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

 

split을 이용한 정규식처리과정에서 매칭된 값도 없고 처리할 값도 없을 경우 `this`자기 자신을 return 한다는 뜻이다. 

 

따라서 bufferedReader는 공백도 입력받기 때문에 split(" ")만을 이용해서 문제를 접근할 시 공백/줄 바꿈에 대한 예외처리가 되지 않아 오류가 난다는 것을 알 수 있다. 

 

제출 1) 수정코드

예외처리 하나만 해주면 정답으로 인정해 준다.

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str = br.readLine();

        String[] strarr = str.trim().split(" ");

        if (strarr.length > 0 && strarr[0].equals("")) {
        	// 첫번째 배열의 값이 빈문자열'' 이라면 0을 출력해야한다.
            System.out.println(0);
        } else {
            System.out.println(strarr.length);
        }
    }
}

 


제출 2) StringTokenizer 클래스 사용

import java.util.Scanner;
import java.util.StringTokenizer;
 
public class Main {
 
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
 
		String S = in.nextLine();
		in.close();
 
		// st 에 공백을 기준으로 나눈 토큰들을 st 에 저장한다
		StringTokenizer st = new StringTokenizer(S," ");
		
		// countTokens() 는 토큰의 개수를 반환한다
		System.out.println(st.countTokens());	
		
	}
}

 

StringTokenizer()?? 이건 또 뭐지.. ㅠㅠ

 

[Java] StringTokenizer와 Split() 메서드 언제 써야할까?

우선 StringTokenizer에 대해 알아보자. java.util.StringTokenizer 클래스란? 긴 문자열을 지정된 구분자(delimiter)를 기준으로 토큰(token)이라는 여러 개의 문자열로 잘라내는 데 사용된다. 예) “100,200,300,4

velog.io

 

StringTokenizer ?

긴 문자열을 지정된 구분자(delimiter)를 기준으로 토큰(token)이라는 여러 개의 문자열로 잘라내는데 사용된다.split과의 차이점이라면

 

1. split의 구분자(delimiter)은 정규식코드로 작성되어져야되기 때문에 복잡하지만 StringTokenizer 은 아니다.

아래의 코드를 보면 한번에 이해할 수 있다.

더보기
String ex = "x=100*(200+300)/2";

System.out.print("split() \t\t => ");

// 1. split() 메소드를 사용한 문자열 자르기
String[] tokens = ex.split("[+*/=()]+");
for (String token : tokens) {
    // 빈 문자열은 출력하지 않음
    if (!token.isEmpty()) {
        System.out.print(token + " ");
    }
}
System.out.println();
System.out.print("StringTokenizer 클래스 => ");
// 2. StringTokenizer 클래스를 사용한 문자열 자르기
StringTokenizer st = new StringTokenizer(ex, "+=*/()");

while (st.hasMoreTokens()) {
    System.out.print(st.nextToken() + " ");
}

 

 

2. split() 과 다르게 공백은 토큰으로 인식하지 않는다는점이다.

 

제출1) 에서 공백을 토큰을 인식해서 단어의 개수가 1이나와 오류가 나왔지만 StringTokenizer을 사용하게되면 공백을 토큰으로 인식하지 않게 되므로 0 이 나와서 정답처리가 된다.

 

더보기

StringTokenizer와 Split의 차이

StringTokenizerSplit()

java.util에 포함된 클래스다. String 클래스에 속해있는 메소드
문자로 문자열을 구분 정규표현식으로 구분
오직 단 한 문자의 구분자만 사용 가능 정규표현식을 이용하면 두 문자 이상의 구분자도 사용 가능
결과값이 문자열 String 결과값이 문자열 배열 String[]
빈 문자열을 토큰으로 인식하지 않음 빈 문자열을 토큰으로 인식함

→ 데이터 양이 적을 때 배열에 담아 반환하는 split는 데이터를 바로 잘라서 반환해주는 StringTokenizer보다 느릴 수 있다.

 

'알고리즘' 카테고리의 다른 글

#[백준] 1157 단어공부  (0) 2024.01.12
#[백준] 2675 문자열 반복 (StringBuilder 클래스)  (0) 2024.01.11
#[백준] 11720 숫자의 합  (1) 2024.01.11