저번 글에서 객체 지향까지 살펴보았습니다.
이번에는 입출력에서 알아보도록 하겠습니다. 🙌🙌
<p align='center'>
<image src='https://user-images.githubusercontent.com/34594339/100711415-96349f80-33f4-11eb-9ac7-9d508aef29de.png' width='50%'>
</p>
프로그램을 실행했을 때 문자열을 보여주는 것이 콘솔 출력입니다.
만약 프로그램을 이클립스에서 실행했다면 이클립스 콘솔창이, 도스 명령창에서 실행했다면 도스 명령창이 콘솔이 됩니다.
즉, 콘솔은 사용자의 입력을 받거나 사용자에게 문자열을 출력해주는 역할하는 것을 통칭하는 말입니다.
import java.io.InputStream;
public class StreamTest {
public static void main(String[] args) throws Exception {
InputStream in = System.in;
int a;
a = in.read();
System.out.println(a);
}
}
위의 InputStream
은 자바의 내장 클래스입니다.
그동안 사용했던 System
이나 String
등의 클래스는 java.lang
패키지에 속해있는 클래스이므로 별도의 import
과정이 필요없었습니다. (자바 패키지는 다음 글에서 설명하도록 하겠습니다. 😇
InputStream in = System.in;
int a = in.read();
System.in
은 InputStream
의 객체입니다.
InputStream
의read
메소드는 1 byte의 사용자의 입력을 받아들입니다.
read
메소드로 읽은 1 byte의 데이터는 int 자료형으로 저장됩니다.
저장되는 int 값은 0-255 사이의 정수값으로 아스키 코드값입니다.
0이라는 숫자에 해당되는 아스키코드값은 48, a라는 문자에 해당되는 아스키코드값은 97입니다.
read()
메소드는 사용자의 입력을 받을 때까지 대기합니다.
만약 int a = in.read()
상태에서 'a' 키를 입력했다고 가정해봅시다. (⚠ 이때 엔터키를 입력해야 사용자의 입력이 프로그램에 전달 됩니다.)
97
출력된 a
의 결과는 위와 같습니다.
만약 'abc'를 입력했다면 어떨까요? 결과는 위와 똑같습니다.
이유는 Inputstream
의 read
메소드는 1 byte만 읽기 때문입니다.
스트림(Stream)이란?
이어져있는 데이터(byte) 형태를 스트림(Stream)이라고 합니다.
프로그래밍에서는 다음과 같는 것들을 스트림이라고 합니다.
- 파일 데이터 (파일은 그 시작과 끝이 있는 스트림입니다.)
- HTTP 응답 데이터 (브라우저가 요청하고 서버가 응답하는 스트림입니다.)
- 키보드 입력 (사용자가 키보드로 입력하는 문자열은 스트림입니다.)
그렇다면 3 byte의 문자를 읽고 싶다면 어떻게 할까요? 🤔
import java.io.InputStream;
public class StreamTest {
public static void main(String[] args) throws Exception {
InputStream in = System.in;
byte[] a = new byte[3];
in.read(a);
// 입력 : abc
System.out.println(a[0]); // 97
System.out.println(a[1]); // 98
System.out.println(a[2]); // 99
}
}
read
메소드를 3번 실행하여 각각 변수에 넣어주는 방식도 있지만,
위와 같이 read
메소드 호출 시 입력값으로 전달하면 콘솔 입력이 해당 배열에 저장됩니다.
하지만 읽어들인 값은 항상 아스키코드 값으로 해석되고 있습니다.
입력한 문자값 그대로 출력하기 위해서는 InputStreamReader
를 사용하면 됩니다.
import java.io.InputStream;
import java.io.InputStreamReader;
public class StreamTest {
public static void main(String[] args) throws Exception {
InputStream in = System.in;
InputStreamReader reader = new InputStreamReader(in); --- (1)
char[] a = new char[3]; --- (2)
reader.read(a);
System.out.println(a);
}
}
(1)
: InputStreamReader
객체를 생성할 때는 InputStrea
객체가 필요합니다.(2)
: InputStreamReader
를 이용하면 byte 대신 char 배열을 사용할 수 있습니다.그렇다면 항상 char[3]
과 같이 항상 고정된 길이로만 스트림을 읽어야할까요?
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
public class StreamTest {
public static void main(String[] args) throws Exception {
InputStream in = System.in;
InputStreamReader reader = new InputStreamReader(in);
BufferedReader br = new BufferedReader(reader);
String a = br.readLine();
System.out.println(a);
}
}
BufferedReader
는 객체 생성 시 생성자의 입력값으로 InputStreamReader
의 객체가 필요합니다.
BufferedReader
의 readline
메소드를 이용하면 사용자가 엔터키를 입력할 때까지 입력했던 문자열 전부를 읽을 수 있게 됩니다.
콘솔 입출력 정리
InputStream
: byteInputStreamReader
: characterBufferedReader
: String
J2SE 5.0부터 Scanner
라는 java.util.Scanner
클래스가 새로 추가되었습니다. 이 클래스르 이용하면 콘솔 입력을 보다 쉽게 처리할 수 있습니다.
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println(sc.next());
}
}
Scanner
클래스는 생성자의 입력으로 System.in
, 즉 콘솔 입력인 InputStream
을 필요로합니다.
Scanner
클래스의 여러 메소드
next
: 단어nextLine
: 라인nextInt
: 정수
System.out
은 PrintStream
클래스의 객체 입니다.
PrintStream
은 콘솔에 값을 출력할 때 사용되는 클래스입니다. 보통 System.out.println
은 콘솔에 문자열을 출력할 경우나 디버깅 시 많이 사용됩니다.
System.err
는 오류메시지를 출력할 경우에 사용하게 되어 잇습니다.
String
은 immutable
한 객체입니다. str1+str1
와 같은 연산을 하면 새로운 String
을 생성합니다.
즉, String
을 더하는 행위는 메모리 할당과 해제를 발생시키며 더하는 연산이 많을 수록 성능은 떨어집니다.
이를 위해서 String builder
는 새로운 객체를 생성하는 것이 아니라 기존의 데이터에 더하는 방식을 사용합니다.
기존 방식보다 속도도 빠르고 상대적인 부하가 적습니다.
따라서 긴 문자열을 더하는 상황이 발생할 경우 StringBuilder
를 적극적으로 사용하는 것이 좋습니다.
SyringBuilder sb = new StringBuilder();
sb.append("abc");
sb.append("def");
System.out.println(sb.toString());
import java.io.FileOutputStream;
import java.io.IOException;
public class FileWrite {
public static void main(String[] args) throws IOException {
FileOutputStream output = new FileOutputStream("c:/out.txt");
output.close();
}
}
위 코드를 실행하면 c:/
디렉토리에 out.txt
가 생기는 것을 확일할 수 있습니다.
파일을 생성하기 위해서 FileOutputStream
클래스의 객체를 생성하고 생성자의 입력으로 파일명을 넘겨주어야합니다. (예제에서는 파일명을 생성자의 입력으로 전달하였습니다.)
output.close
는 사용한 파일 객체를 닫아주는 것으로, 자바 프로그램이 종료할 때 사용한 파일 객체를 자동으로 닫아주기 때문에 생략 가능합니다.
그러나 사용했던 파일을 닫지 않고 다시 사용하는 경우 에러가 발생할 수 있기 때문에 직접 사용한 파일을 닫아주는 것이 좋습니다.
import java.io.FileOutputStream;
import java.io.IOException;
public class FileWrite {
public static void main(String[] args) throws IOException {
FileOutputStream output = new FileOutputStream("c:/out.txt");
for(int i=1; i<11; i++) {
String data = i+" 번째 줄입니다.\r\n";
output.write(data.getBytes());
}
output.close();
}
}
OutputStream
역시 byte 단위로 데이터를 처리하는 클래스입니다.
FilOutputStream
은 OutputStream
클래스를 상속받아 만든 클래스로, 역시 바이트 단위로 데이터를 처리하게끔 되어 있습니다.
FileOutputStream
에 값을 쓸 때는 바이트 배열로 써야하므로 getBytes
메소드를 이용해야 합니다. (\r\n
은 줄바꿈 문자입니다. 유닉스는 \n
로 쓰이지만 윈도우즈에서는 \r\n
을 사용해야 에디터에서 제대로 줄바꿈이 표시됩니다.)
그러나 문자열을 파일에 쓸 때 FileOutputStream
은 String
을 byte
배열로 변환해야하기 때문에 번거롭습니다.
import java.io.FileWriter;
import java.io.IOException;
public class FileWrite {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("c:/out.txt");
for(int i=1; i<11; i++) {
String data = i+" 번째 줄입니다.\r\n";
fw.write(data);
}
fw.close();
}
}
위와 같이 FilerWriter
를 이용하면 byte
배열 대신 문자열을 직접 파일에 쓸 수 있습니다.
그러나 \r\n
은 여전히 사용해야합니다. 이러한 불편함을 어떻게 해결해야할까요?
import java.io.IOException;
import java.io.PrintWriter;
public class FileWrite {
public static void main(String[] args) throws IOException {
PrintWriter pw = new PrintWriter("c:/out.txt");
for(int i=1; i<11; i++) {
String data = i+" 번째 줄입니다.";
pw.println(data);
}
pw.close();
}
}
PrintWriter
를 이용하면 \r\n
을 덧붙이는 대신 println
이라는 메소드를 사용할 수 있습니다.
import java.io.FileWriter;
import java.io.IOException;
public class FileWrite {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("c:/out.txt");
for(int i=1; i<11; i++) {
String data = i+" 번째 줄입니다.\r\n";
fw.write(data);
}
fw.close();
FileWriter fw2 = new FileWriter("c:/out.txt", true);
for(int i=11; i<21; i++) {
String data = i+" 번째 줄입니다.\r\n";
fw2.write(data);
}
fw2.close();
}
}
파일에 내용을 쓰고 난 뒤 또 새로운 내용을 추가하고 싶을 때는 작성한 파일을 추가모드로 열어야합니다.
new FileWriter(파일명, 추가모드구분)
을 통해서 추가모드를 열어주면 기존 파일 내용 이후부터 파일이 쓰여지게 됩니다.
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class FileWrite {
public static void main(String[] args) throws IOException {
PrintWriter pw = new PrintWriter("c:/out.txt");
for(int i=1; i<11; i++) {
String data = i+" 번째 줄입니다.";
pw.println(data);
}
pw.close();
PrintWriter pw2 = new PrintWriter(new FileWriter("c:/out.txt", true));
for(int i=11; i<21; i++) {
String data = i+" 번째 줄입니다.";
pw2.println(data);
}
pw2.close();
}
}
PrintWriter
을 이용하고 싶은 경우 위와 같이 추가 모드로 열린 FilerWriter
객체를 사용하면 됩니다.
import java.io.FileInputStream;
import java.io.IOException;
public class FileRead {
public static void main(String[] args) throws IOException {
byte[] b = new byte[1024];
FileInputStream input = new FileInputStream("c:/out.txt");
input.read(b);
System.out.println(new String(b));
input.close();
}
}
파일을 읽기 위해서는 FileInputStream
을 사용하면 됩니다.
다만 byte 배열을 이용하여 읽기 대문에 정확한 길이를 모른 경우에 문제가 생깁니다.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class FileRead {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("c:/out.txt"));
while(true) {
String line = br.readLine();
if (line==null) break;
System.out.println(line);
}
br.close();
}
}
FileInputStream
대신 FileReader
과 BufferedReader
를 사용하면 라인단위로 파일을 읽을 수 있습니다.
BufferedReader
의 readline
메소드는 더이상 읽을 라인이 없는 경우 null을 리턴합니다.
입출력을 알고있지만 모르겠는 그런 내용인 거 같습니다.. 😥
다음 글은 접근 제어자로 돌아오겠습니다. 🤗