삽질기록 #4 자바 Scanner next()와 nextLine()의 차이

    (2022.02.23)

    가끔 명확한 개념을 모른 채로 이건가? 저건가? 이거 안 되면 저거 써보지 뭐 하면서 바꿔가면서 쓰는 개념들이나 메서드들이 있다. 그 중 하나가 Scanner인 것 같은데, 오늘 특히 next()와 nextLine()의 차이가 헷갈려서 잠깐 삽질을 좀 했다.

     

    next()와 nextLine()의 차이

    next()

    먼저 next()는 간단히 말해서 공백(스페이스)를 구분자로 해서 문자열을 토큰화한다고 보면 된다.

    Scanner sc = new Scanner(System.in);
    int i = 1;
    while (sc.hasNext()) {
        System.out.println(i);
        System.out.println(sc.next());
        i++;
    }

    여기서 hello there hello world anybody there?이라는 문자열을 입력하면 출력은 다음과 같다.

    1
    hello
    2
    there
    3
    hello
    4
    world
    5
    anybody
    6
    there?

    공백을 기준으로 문자열이 토큰화되어 하나씩 출력되는 걸 볼 수 있다.

     

    참고로 hasNext() 메서드는 버퍼에 입력된 내용이 없으면 block 상태로 입력을 기다리고, 입력된 내용이 있으면 true를 반환한다. 그래서 사실 여기서는 종료 조건을 명시하지 않는 이상 scanner의 버퍼가 비워지면 hasnext()가 값을 반환하지 않고 block 상태로 입력을 대기하기 때문에 무한루프 형태로 반복된다.

     

    nextLine()

    nextLine()은 줄바꿈 \n을 EOF처럼 인식해서 \n 이전까지의 문자열을 출력한다. 그냥 문자열을 공백까지 포함해서 길게 입력하고 엔터를 치면, 사실상 그 문자열 전체가 리턴된다고 보면 된다. 만약 아래와 같은 코드에 앞에서처럼 hello there hello world anybody there?를 입력한다면 출력도 그냥 hello there hello world anybody there?이 될 것이다.

    Scanner sc = new Scanner(System.in);
    System.out.println(sc.nextLine()); // hello there hello world anybody there?

     

    next()는 버퍼에 \n을 남겨둔다!

    그런데 여기서 문제가 발생하는데, next()는 우리가 콘솔에 엔터를 칠 때 입력받은 '\n'을 제외한 인풋을 반환한다. 사실 우리가 hello라고 치면 엄밀히 말해 버퍼에는 hello\n이 들어가는 것인데, next()는 여기에서 \n을 제외한 hello까지만 출력하기 때문에 버퍼에는 \n이 아직 남아 있다. 이건 의외의 상황에서 문제를 일으킬 수 있는데, next() 뒤에서 같은 버퍼를 사용하는 스캐너로 nextLine()을 호출했을 때다. 그러면 nextLine()이 버퍼를 읽었을 때 남아있는 \n을 발견하게 되고... nextLine()은 \n을 만나면 종료되므로 그대로 끝난다.

    Scanner sc = new Scanner(System.in);
    
    System.out.println(sc.next());
    System.out.println(sc.nextLine());
    System.out.println("terminated!");

    이 상황에서 hello!라는 입력을 받았다고 해 보자. 출력은 이렇게 된다.

    hello
    
    terminated!

    (4라인의 sc.nextLine()은 빈 문자열을 리턴하는 셈이고, 이건 곧 System.out.println()을 인자 없이 호출하는 것과 같아서 hello와 terminated! 사이에 빈 줄이 하나 생긴다)

    대부분의 경우 이런 결과를 원하진 않을 것이다. 그래서 이런 일을 막으려면 sc.nextLine()을 한 번 더 쓰든지 해줘야 한다. 이렇게 next()가 \n을 버퍼에 남겨두는 건 그냥 next() 뿐만 아니라 nextInt() 같은 next숫자형() 메서드들도 마찬가지다.

     

    next()든 nextLine()이든 반환형은 String

    그래서 char 한 문자를 받으려면 charAt(0)을 붙여줘야 한다. 대부분의 자료에서 next().charAt(0)을 쓰지만, 위에서 두 메서드의 차이점을 보았기 때문에 그냥 한 문자만 입력하는 경우에는 사실상 nextLine().charAt(0)도 같은 결과를 준다.

     


    참고자료

    댓글