티스토리 뷰

여기서는 파일에서 학생 데이터를 읽어 객체로 저장하는 방법을 살펴본다. 

실행결과는 다음과 같다. 입력파일에는 학번 이름 학과 학년 그리고 점수 여러 개가 나오는 여러 줄의 데이터가 들어있다. 그런 정보를 읽어서 아래와 같이 출력하는 프로그램을 작성하고 싶다.

산업경영공학과	201222028	최은서	3 	5 4 3 3 1 4
컴퓨터과학과	201722825	임소정	1 	3 2 3 2
컴퓨터과학과	201222209	남정수	2 	7 6 6 6 6 4 3 2

[201222028] 최은서 산업경영공학과 (3학년) - [4, 3, 3, 1, 4]
[201722825] 임소정 컴퓨터과학과 (1학년) - [2, 3, 2]
[201222209] 남정수 컴퓨터과학과 (2학년) - [6, 6, 6, 6, 4, 3, 2]

먼저 파일명을 입력받아 그 파일을 열어서 데이터를 읽어올 준비를 해야 한다. 이 때 파일 위치는 이클립스 프로젝트의 홈디렉토리에 있어야 한다. 홈디렉토리는 프로젝트를 만들 때 지정해준 디렉토리에 프로젝트 이름으로 새로 생긴 디렉토리다. (이클립스의 Project 메뉴 -> properties 대화상자에 프로젝트 디렉토리의 경로가 나와있다.)

이 디렉토리에 지정된 이름의 파일이 있으면 따로 경로를 표시해 주지 않아도 자바 프로젝트에서 그 파일을 찾을 수 있다. 물론 파일명에 C:\\ 부터 시작하는 전체경로를 써주어도 되지만 꼭 필요한 경우가 아니라면 그보다는 프로젝트 디렉토리를 이용하는 것이 더 바람직하다. 지정한 파일이 그 경로에 없으면 FileNotFoundException이 발생한다. 이 예외는 checked 예외여서 반드시 try ... catch를 해 주어야 컴파일러를 통과할 수 있다. 이를 위해 다음과 같이 파일을 열고 스캐너에 연결해주는 부분을 작성할 수 있다.

String filename;
filename = keyin.next();
Scanner filein = null;
try {
  File f = new File(filename);
  filein = new Scanner(f);
} catch (FileNotFoundException e) {
  throw new RuntimeException(e);
}

File 변수는 지정된 이름의 파일을 열어서 파일 객체를 만들고 그것을 가리킨다. 이 때 그 파일이 해당 경로에 없으면 FileNotFoundException 예외가 발생한다. 이 예외는 checked 예외이므로 반드시 catch 되어야 해서 try {} catch {} 절을 이용해 핸들러를 만들어 주었다. catch 절에서 아무 일도 하지 않아도 컴파일러를 통과할 수 있다. 그러나 여기서 핸들러는 예외를 해결하고 수행을 계속하게 할 수가 없다. (파일이 없으면 이 부분을 지나도 어차피 파일을 읽는 코드에서 다시 NullPointerException이 발생한다. 그러므로 경로와 파일을 확인하고 프로그램을 다시 실행할 필요가 있다.) 여기서 프로그램을 종료하기 위해 컴파일러가 불평하지 않는 unchecked 예외로 바꾸어서 throw를 해 준다. 그러면 이 예외는 프로그램을 종료하게 하고 VM이 원래 발생한 예외 e에 대한 정보도 같이 출력해 준다. 출력의 형태는 빨간색으로 오류가 발생한 위치의 스택 트레이스를 보여준다. 스택 트레이스란 현재 위치와 거기에 오기까지 호출했던 함수와 호출부의 위치정보를 보여주는 출력이다. System.exit();을 써서 강제 종료하는 방법도 있는데 그보다는 RuntimeException을 이용하면 VM이 오류메시지를 찍어주게 하는 효과가 있어 더 바람직하다. 여기까지 하면 우리는 파일명에 해당하는 스캐너를 정상적으로 만들어서 다음 수행을 위한 준비를 마쳤다.

다음으로 파일 스캐너에서 학생 정보를 읽어들이는 부분을 생각해 보자. 학생 클래스에서 만들어둔 read(Scanner s) 메소드는 스캐너가 키보드 입력에서 오는 것인지 파일 스캐너인지 구별하지 않고 next() 또는 nextInt()를 이용해서 데이터를 읽어온다. 그러므로 read 메소드는 수정할 필요가 없다. 달라지는 부분은 파일에서 학생 데이터를 읽어오는 doit() 반복부의 종료 조건이다. 키보드 입력에서는 끝을 알 수 없기 때문에 별도로 끝을 표시하는 값(0)을 넣거나 입력의 개수를 미리 알려 주어 언제가 끝인지를 판단할 수 있게 해야 한다. 앞의 1단계에서는 학생의 수를 미리 입력받은 후 그 수만큼 반복하면서 학생을 입력받았다. 그러나 파일 입력의 경우에는 파일의 끝인지 읽어올 데이터가 더 있는지 알려주는 기능이 있다. Scanner.hasNext() 메소드를 이용하면 파일의 끝(end of file, EOF)에 도달하면 거짓이 돌아오고 그렇지 않으면 참이 돌아온다. 그러므로 hasNext()가 참일 동안은 데이터가 더 있다고 보고 while 루프를 들어간다.

while (filein.hasNext()) {
  st = new Student();
  st.read(filein);
  stList.add(st);
}

이렇게 하면 파일에 있는 학생 정보의 개수만큼 학생 객체가 생성되고 데이터가 입력된다.

(1) 입력 문제 해결 1 : 입력 파일이 학생 데이터를 순서에 맞게 가지고 있어야 하는데 잘못해서 다른 값이나 오타가 들어있다면 문제가 된다. 특히 숫자가 입력되어야 하는데 숫자가 아닌 것이 있으면 InputMismatchException이 발생하게 된다. 즉 스캐너는 숫자를 기대하는데 숫자가 아닌 것이 들어왔다는 뜻이다. 입력 파일에 문제가 없다면 코드에서 입력하는 순서가 안 맞거나 어딘가에서 어긋나서 학번을 읽어야 할 차례인데 이름이 들어온다거나 하는 경우가 많다. 이럴 때는 코드에서 읽는 순서나 개수가 틀렸을 수 있으므로 name 같은 대표 값을 읽은 후 바로 출력 하도록 코드를 고쳐서 디버깅해 본다. 안되면 값을 하나하나 출력하게 하거나 디버거를 통해 실행을 멈추고 값을 하나씩 검사하면서 순서대로 값을 읽고 있는지 다시 확인해본다.

(2) 입력 문제 해결 2 : 입력 파일의 끝에 빈칸이나 빈줄이 들어있는 경우 문제가 생길 수 있다. 위의 코드에서 while (filein.hasNext()) 부분은 EOF 즉 파일의 끝을 만나야 false가 돌아온다. 그런데 끝에 빈칸이나 빈줄이 있으면 true가 돌아오므로 프로그램은 학생이 더 있다고 생각하고 while 루프에 진입한다. 그러면 NoSuchElementException이 발생한다. 이것은 학생의 read에서 읽어올 데이터가 더이상 없을 때 발생한다. 그러므로 혹시 입력 파일의 끝에 실수로 빈칸이나 빈줄이 들어가지는 않았는지 확인해 수정해야 한다. 또한 읽는 순서나 개수가 맞지 않아 파일이 끝났는데도 뭔가를 더 읽으려고 하는 경우에도 이 예외가 발생할 것이다.

(3) 입력 문제 해결 3 : 가장 어려운 문제는 인코딩 문제다. 입력파일의 인코딩이 이클립스 프로젝트에서 설정된 인코딩과 다른 경우 Scanner는 입력을 처리하지 못한다. 이런 경우 filein.hasNext()에서 바로 false가 돌아와서 아예 while 루프 안으로 들어가지 못하는 사태가 발생한다. 그러므로 학생이 한 명도 읽어지지 않았다면 인코딩 문제를 의심해 보아야 한다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함