자바에서 입출력을 사용할 때 스트림을 사용한다.
스트림에는 입력과 출력으로 나누어지는데 스트림이 이루어질 때 사용되는 기본자료형은 _바이트 단위_이다.
입력스트림 사용하기
package stream;
import java.io.IOException;
public class SystemInTest1 {
public static void main(String[] args) {
System.out.println("알파벳 하나를 쓰고 [Enter]를 누르세요");
int i;
try {
i = System.in.read();
System.out.println((char)i);
}catch(IOException e) {
e.printStackTrace();
}
}
}
- 스트림은 반드시 예외처리를 해야한다.
- 입력스트림을 바이트 단위로 받아 "문자형"으로 형변환 후 출력하였다.
여러개 입력 받기
package stream;
import java.io.IOException;
public class SystemInTest2 {
public static void main(String[] args) {
System.out.println("알파벳 여러개를 쓰고 [Enter]를 누르세요");
int i;
try {
while((i = System.in.read()) != -1) {
System.out.print((char)i);
}
}catch(IOException e) {
e.printStackTrace();
}
}
}
- 사용자가 엔터를 누르기전까지 계속해서 문자열을 저장해뒀다가 엔터를 누르면 출력한다.
스캐너로 입력받기.
System.in.read 와 달리 Scanner클래스를 이용하면 다양한 자료형의 입력스트림을 받을 수 있다.
package stream;
import java.util.Scanner;
public class ScannerTest {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("이름:");
String name = scanner.nextLine();
System.out.println("직업:");
String job = scanner.nextLine();
System.out.println("사번:");
int num = scanner.nextInt();
System.out.println("------- 사원 정보 --------");
System.out.println(name);
System.out.println(job);
System.out.println(num);
}
}
파일 읽기, 쓰기
텍스트 파일 읽기1
package stream;
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamTest1 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("input.txt");
System.out.println((char)fis.read());
System.out.println((char)fis.read());
System.out.println((char)fis.read());
} catch (IOException e) {
System.out.println(e);
}finally {
try {
fis.close();
} catch (IOException e) {
System.out.println(e);
} catch (NullPointerException e) {
System.out.println(e);
}
}
System.out.println("end");
}
}
- input.txt 라는 파일을 읽어온다.
- 한줄씩 일어와 형변환 후 출력한다 (텍스트 파일에 3줄이 되있으므로 3번 써주었다)
텍스트 파일 읽기1
package stream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest2 {
public static void main(String[] args) {
try(FileInputStream fis = new FileInputStream("input.txt")) {
int i;
while((i = fis.read()) != -1) { // 파일을 한줄 씩 끝까지 반복함
System.out.println((char)i);
}
System.out.println("end");
} catch (FileNotFoundException e) {
System.out.println(e);
} catch (IOException e) {
System.out.println(e);
}
}
}
- 위에서 세줄을 읽어오기 위해 read() 메서드를 세번 써주었지만 파일의 라인수가 많은 경우나 계속 변경되는 경우에는 라인수를 할 수 없으므로 반복문을 통해 텍스트를 읽어온다.
배열로 파일 읽어오기
package stream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest3 {
public static void main(String[] args) {
try(FileInputStream fis = new FileInputStream("input2.txt")) {
byte[] bs = new byte[10];
int i;
while((i = fis.read(bs)) != -1) { // 파일을 한줄 씩 끝까지 반복함
for(byte b : bs) {
System.out.print((char)b);
}
System.out.print(": " + i + "바이트 읽음");
}
} catch (FileNotFoundException e) {
System.out.println(e);
} catch (IOException e) {
System.out.println(e);
}
System.out.println("end");
}
}
- 파일을 읽을 때 문자를 10개씩 배열로 저장해 반복해서 출력하였다.
- 결과값 3번째 줄을 보면 Z뒤에 "QRST" 가 출력되었다. 이것은 기존 2번째 라인을 저장할 때 byte 배열에 이미 저장된 요소들이 그대로 출력된 것이다.
- 이를 방지하기 위해서는 읽어들인 바이트 수 만큼만 출력하도록 출력부분을 바꾸면 해결된다.
for(int k=0; k < i; k++) {
System.out.print((char)bs[k]);
}
텍스트 파일에 쓰기
package stream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutStreamTest {
public static void main(String[] args) {
//try(FileOutputStream fos = new FileOutputStream("output.txt")) {
try(FileOutputStream fos = new FileOutputStream("output.txt", true)) {
fos.write(65);
fos.write(66);
fos.write(67);
}catch(IOException e) {
e.printStackTrace();
}
System.out.println("출력이 완료되었습니다.");
}
}
- 파일아웃풋스트림으로 텍스트파일을 생성해 그 안에 아스키코드로 알파벳 ABC를 출력한다.
- 만약 파일을 이어서 작성하고 싶다면 FileOutStream 생성자 두번째 인자에 true를 넘겨준다.
FileOutputStream fos = new FileOutputStream("output.txt", true)
배열로 저장해 파일에 쓰기
package stream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutStreamTest2 {
public static void main(String[] args) {
try(FileOutputStream fos = new FileOutputStream("output2.txt")) {
byte[] bs = new byte[26];
byte data = 65;
for(int i=0; i < bs.length; i++) {
bs[i] = data;
data++;
}
fos.write(bs);
}catch(IOException e) {
e.printStackTrace();
}
System.out.println("출력이 완료되었습니다.");
}
}
- InputStream과 마찬가지로 byte 배열을 사용해 자료를 한꺼번에 출력할 수 있다.
- 만약 읽어드린 데이터에서 일부만 출력하고 싶다면 write() 메서드의 인자를 아래와 같이 넘기면 원하는 부분만 출력할 수 있다.
fos.write(bs, 2, 10);
문자 단위 스트림
앞서 파일스트림은 바이트 단위로 문자를 읽고/쓰기 때문에 한글처리에 문제를 가진다.
한글텍스트 파일 읽기 문제점.
앞에 실습했던 FileInputStreamTest2 예제해서 한글로 된 파일을 읽어서 출력해보자.
package stream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest2 {
public static void main(String[] args) {
try(FileInputStream fis = new FileInputStream("reader.txt")) {
int i;
while((i = fis.read()) != -1) { // 파일을 한줄 씩 끝까지 반복함
System.out.println((char)i);
}
System.out.println("end");
} catch (FileNotFoundException e) {
System.out.println(e);
} catch (IOException e) {
System.out.println(e);
}
}
}
- 바이트단위로 읽어오지만 한글은 한글자당 기본 2바이트의 크기를 가지고 있으므로, 알아볼 수 없는 문자가 출력되었다.
FileReader로 한글파일 읽기.
package stream;
import java.io.FileReader;
public class FileReaderTest {
public static void main(String[] args) {
// 파일리더로 "안녕하세요"가 적혀있는 텍스트 파일을 읽어옴.
try(FileReader fr = new FileReader("reader.txt")) {
int i;
while((i = fr.read()) != -1) {
System.out.print((char)i);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 사용방법은 FileInputStream 이랑 똑같다.
- 문자를 다룰 때는 FileReader 클래스로 처리하는게 좋다.
FileReader로 쓰기
package stream;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
public static void main(String[] args) {
try(FileWriter fw = new FileWriter("writer.txt")) {
fw.write('A');
char buf[] = {'B', 'C', 'D', 'E', 'F', 'G'};
fw.write(buf);
fw.write("안녕하세요. 잘써지나요?");
fw.write(buf, 1, 2);
fw.write(65);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("출력이 완료되었습니다.");
}
}
보조스트림
보조스트림은 기반스트림처럼 파일이나 네트워크에 읽기/쓰기 권한은 없다.
하지만 기반스트림이 못하는 기능을 옆에서 도와줘서 기반스트림이 할 수 없는 스트림 기능을 구현할 수 있다.
보조스트림을 활용해 한글텍스트 읽어오기
package stream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class InputStreamReaderTest {
public static void main(String[] args) {
try(InputStreamReader isr = new InputStreamReader(new FileInputStream("reader.txt"))) {
int i;
while((i = isr.read()) != -1) {
System.out.print((char)i);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 원래 FileInputStream 클래스는 바이트단위로 문자를 처리하기에 한글을 읽을 수 없다.
- 하지만 InputStreamReader의 생성자로 넘겨주었기에 바이트단위로 파일을 읽어오더라도 문자로 변환을 시켜주기에 정상적으로 한글을 읽어올수 있게 되었다.
Buffered 스트림
Buffered 스트림을 활용하면 한 바이트씩 입/출력을 수행하는 것보다 더 빠르게 프로그램을 수행할 수 있다.
FileInputStream 을 이용하여 파일 복사하기.
package stream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileCopyTest {
public static void main(String[] args) {
long millisecond = 0;
try(FileInputStream fis = new FileInputStream("a.zip");
FileOutputStream fos = new FileOutputStream("copy.zip")) {
millisecond = System.currentTimeMillis();
int i;
while((i = fis.read()) != -1) {
fos.write(i);
}
millisecond = System.currentTimeMillis() - millisecond;
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("파일 복사하는데" + millisecond + " 초 소요되었습니다.");
}
}
보조스트림 Buffered 활용하여 파일 복사하기
package stream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class BufferedStreamTest {
public static void main(String[] args) {
long millisecond = 0;
try(FileInputStream fis = new FileInputStream("a.zip");
FileOutputStream fos = new FileOutputStream("copy.zip");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos)) {
millisecond = System.currentTimeMillis();
int i;
while((i = bis.read()) != -1) {
bos.write(i);
}
millisecond = System.currentTimeMillis() - millisecond;
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("파일 복사하는데" + millisecond + " 초 소요되었습니다.");
}
}
- 기반스트림과 보조스트림을 활용하여 파일을 복사했다.
- 파일을 읽고 쓰는 속도가 굉장히 빨라졌다.
- Buffered 스트림은 한번에 8192 바이트의 배열을 저장할 수 있으므로 이러한 속도가 가능한 것이다.
DataInputStream, DataOutputStream
(생략)
직렬화
직렬화란 인스턴스 자체를 외부에 전달할 수 있도록 값 형태로 저장하는 것을 의미한다.
즉, 인스턴스 변수 값을 스트림으로 만드는 것이다.
Person 클래스 직렬화/역직렬화 구현하기.
package stream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Person implements Serializable {
/**
* 직렬화 버전 생성 고유아이디
*/
private static final long serialVersionUID = -2384750752214662238L;
String name;
String job;
public Person() {}
public Person(String name, String job) {
this.name = name;
this.job = job;
}
public String toString() {
return name + "," + job;
}
}
public class SerializationTest {
public static void main(String[] args) throws ClassNotFoundException {
Person personAhn = new Person("안대용", "과장");
Person personYang = new Person("양기모", "사원");
// 직렬화
try(FileOutputStream fos = new FileOutputStream("serial.out");
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(personAhn);
oos.writeObject(personYang);
} catch (IOException e) {
e.printStackTrace();
}
// 역직렬화
try(FileInputStream fis = new FileInputStream("serial.out");
ObjectInputStream ois = new ObjectInputStream(fis)) {
Person p1 = (Person)ois.readObject();
Person p2 = (Person)ois.readObject();
System.out.println(p1);
System.out.println(p2);
} catch (IOException e) {
e.printStackTrace();
}
}
}
- Person객체의 변수 값들을 직렬화로 한 후 다시 읽어서 콘솔에 출력하였다(역직렬화)
- 직렬화를 한다는 것은 객체정보를 외부에 노출 시킨다는 의미이기에 이 객체는 직렬화 할 것이라는 정보를 기재해야한다.
- 그러므로 Person 객체는 Serializable 인터페이스를 상속받았다.
- 객체의 직렬화 대상에서 제외하고자 한다면 변수 앞에 transient 예약어를 추가한다.
- 예) transient String job;
File 클래스
파일을 직접 입/출력할 수 없지만, 파일 정보나 생성을 할 수 있다.
package stream;
import java.io.File;
import java.io.IOException;
public class FileTest {
public static void main(String[] args) throws IOException {
File file = new File("C:\\fileTest\\newFile.txt");
file.createNewFile();
System.out.println(file.isFile());
System.out.println(file.isDirectory());
System.out.println(file.getName());
System.out.println(file.getAbsolutePath());
System.out.println(file.getPath());
System.out.println(file.canRead());
System.out.println(file.canWrite());
file.delete();
}
}
출처: do it 자바프로그래밍