파일 입출력
git/eomcs-java-basic/src/main/java com.eomcs.io.ex02.Exam0510~Exam0525
git/eomcs-java-basic/src/main/java com.eomcs.io.ex03
바이트 단위로 텍스트 입출력
바이트 단위로 텍스트 출력
바이트 단위로 원하는 텍스트를 출력하기 위해서는 출력될 바이트들의 인코딩 정보를 확실히 인지 후 출력해야 한다. 자바에서 String으로 표현되는 바이트들은 모두 UCS2 형식이지만, 이 String 객체를 getBytes()를 통해 바이트 배열로 변환하면, 그것은 UCS2 형식이 아니라, JVM의 환경 변수인 file.encoding에 설정된 문자집합으로 인코딩된다. 대부분의 웹 브라우저에서 텍스트 인코딩에 사용되는 문자집합은 UTF-8이므로 만약 MS949와 같이 다른 문자집합으로 인코딩된 바이트 배열을 출력한다면 브라우저에서는 폰트가 깨진다. 따라서 바이트들의 인코딩 정보가 중요하다.
자바에서 String을 바이트로 변환할 때에는 앞에서 말한 것처럼 file.encoding에 설정된 문자집합으로 인코딩하는데, 그 환경 변수는 프로그램을 실행할 때 특정 옵션을 달아주지 않는 한, OS의 기본 인코딩을 따른다. OS의 기본 인코딩은 다음과 같다.
- Windows : MS949
- Linux/macOS : UTF-8
만약 OS의 기본 인코딩 정보와 상관없이 file.encoding의 문자집합을 지정하고 싶다면, 콘솔에서 자바 파일을 실행할 때 다음과 같은 옵션을 달아주면 된다.
$ java -Dfile.encoding=원하는 인코딩 문자집합 -cp bin/main .....
혹은 String 에서 byte로 변환하는 getBytes()를 호출할 때 파라미터로 원하는 인코딩 문자집합을 넣어줘도 된다.
str.getBytes("인코딩 문자 집합")
이클립스에서 자바 파일을 실행한다면, 자동으로 file.encoding이 UTF-8로 지정된다.
만약 프로그램이 실행되는 시점에 file.encoding에 설정된 문자집합 정보를 알고 싶다면, System.getProperty("file.encoding))을 통해 환경변수에 저장된 문자집합 정보를 리턴받을 수 있다.
바이트 단위로 텍스트 입력
파일에 써진 텍스트들을 바이트 단위로 읽어낼 때도 마찬가지이다. 파일에 쓰여진 바이트들에 대한 인코딩 정보를 먼저 알아야한다. 그리고 이 읽어들인 바이트들을 자바에서 String으로 변환할 때 , 바이트들의 인코딩 정보를 JVM에게 넘겨 올바르게 UCS2로 변환할 수 있도록 해야한다. 만약 JVM에게 인코딩 정보를 알려주지 않고 바이트 배열을 String 으로 변환했을 경우, String -> byte와 마찬가지로 변환할 바이트 배열이 file.encoding에 저장된 문자집합으로 인코딩되었다고 인지하여 그것에 따라 UCS2로 변환할 것이다. 따라서 바이트 배열의 인코딩 정보를 JVM에게 알려주려면 자바 파일을 실행할 때, 다음과 같은 옵션을 붙여야한다.
$ java -Dfile.encoding=원하는 인코딩 문자집합 -cp bin/main .....
혹은 바이트 배열을 갖고 String 객체를 생성하는 String 생성자의 마지막 파라미터로 바이트 배열의 인코딩 문자집합 정보를 넣는다.
String str = new String(buf, 0, count, "UTF-8");
문자 단위 입출력
문자 단위로 파일을 입출력하기 위해서는 FileWriter, FilerReader과 같은 캐릭터 스트림 클래스들을 사용할 수 있으며, 바이트 스트림과 마찬가지로 read()나 write()와 같이 일관적인 메서드를 통해서 간단히 입출력이 가능하다.
다음과 같이 원하는 데이터를 문자 단위로 입출력할 수가 있다.
- FileWriter 혹은 FileReader 등의 사용할 입출력 도구를 생성한다.
- FileWriter로 한개의 문자를 출력하는 write 메서드를 호출할 때 int 값을 파라미터로 넣어주면, 해당 int 값의 앞 2바이트는 버리고 뒤의 2바이트만 UCS2 문자집합으로 인식하여 읽은 후 특정 문자집합으로 변환하여 출력한다.
- FileReader로 한개의 문자를 입력받는 read 메서드를 호출하면 특정 문자집합으로 파일의 문자를 읽은 후 UCS2 형식의 2바이트로 변환한 값을 int 값에 담아 리턴한다.
import java.io.FileWriter;
import java.io.FileReader;
public class Exam0111 {
public static void main(String[] args) throws Exception {
FileWriter out = new FileWriter("temp/test2.txt");
out.write(0x7a6bac00);
out.write(0x7a5f0041);
out.close();
System.out.println("출력 완료!");
}
}
public class Exam0120 {
public static void main(String[] args) throws Exception {
FileReader in = new FileReader("sample/utf8.txt"); // 41 42 ea b0 81 ea b0 81
int ch1 = in.read(); // 41 => 0041('A')
int ch2 = in.read(); // 42 => 0042('B')
int ch3 = in.read(); // ea b0 80 => ac00('가')
int ch4 = in.read(); // ea b0 81 => ac01('각')
in.close();
System.out.printf("%x, %x, %x, %x\n", ch1, ch2, ch3, ch4);
}
}
FileInputStream이나 FileOutputStream과 같은 바이트 스트림 도구로 텍스트를 입출력할 수도 있지만, 항상 한개의 바이트와 한개의 문자가 대응되지 않으므로, 한개의 문자에 해당하는 여러개의 바이트들을 조합하는 수고로움이 있었다. 캐릭터 스트림은 바이트들을 바이트 단위가 아닌 문자 단위로 입출력하기 때문에 한번에 여러개의 바이트를 받아 하나의 문자로 만드는 과정이 생략되어 편리하다. 그러나 여전히 문자를 출력하기 위해서는 인코딩 문자집합을 염두에 두어야 한다.
자바에서 표현되는 문자 데이터들은 모두 UCS2 인코딩 문자집합을 사용하지만, 이것을 출력할 떄에는 바이트 스트림과 마찬가지로 file.encoding에 저장된 문자집합으로 인코딩한다. 또한 파일에서 문자를 입력받을 때에도 파일에 써진 문자들이 어떤 문자집합으로 인코딩되어있는 지 JVM에게 알려주어야만 알맞게 그것을 UCS2로 변환할 수 있다.
문자 단위로 텍스트 입출력
UCS2로 표현되는 모든 문자들은 2바이트이다. 따라서 FileWriter의 write() 메서드로 int 값을 주면, 앞의 2바이트는 버리고 뒤의 2바이트만 읽어 그것을 특정 문자집합으로 인코딩한 후 출력한다. 디폴트 상태에서는 JVM은 file.encoding에 지정된 문자집합에 따라 읽은 2바이트를 변환한다. 따라서 OS 종류에 상관없이 원하는 문자집합으로 인코딩하고 싶다면, 자바 프로그램을 실행할 때 다음과 같이 옵션을 추가한다.
$ java -Dfile.encoding=원하는 인코딩 문자집합 -cp bin/main .....
혹은 자바 11부터 추가된 기능으로, 원하는 문자집합 정보를 가진 Charset 객체를 생성하여 그것을 FileWriter 생성자의 두번째 파라미터로 넣어주면, write() 메서드를 호출할 때마다 Charset에 지정된 문자집합으로 변환하여 출력한다.
Charset charset = Charset.forName("MS949");
FileWriter out = new FileWriter("temp/test2.txt", charset);
FileReader in = new FileReader("temp/test2.txt", charset);
문자 배열 입출력
char 배열 출력
char[]에 출력하고 싶은 문자들을 모두 담은 후 한번에 출력하는 방법도 있다. FileOutputStream에서 byte[] 을 출력하는 방법과 동일하게 v준비한 char[]에 원하는 문자를 모두 저장한 후, 그 배열을 write 메서드의 파라미터로 넘겨주는 것이다.
그럼 각 배열에 있는 char 데이터가 file.encoding에 설정된 문자집합에 따라 변환되어 차례대로 출력될 것이다.
import java.io.FileWriter;
public class Exam0210 {
public static void main(String[] args) throws Exception {
FileWriter out = new FileWriter("temp/test2.txt");
char[] chars = new char[] {'A', 'B', 'C', '가', '각', '간', '똘', '똥'};
out.write(chars); // 문자 배열 전체를 출력한다.
out.close();
System.out.println("데이터 출력 완료!");
}
}
원하는 배열의 인덱스부터 원하는 개수만큼 문자를 출력하고 싶다면 write 메서드의 두번째 파라미터로 출력하고자 하는 배열 인덱스의 시작점, 세번째 파라미터로 출력하고자 하는 문자 개수를 넘겨주면 된다.
import java.io.FileWriter;
public class Exam0310 {
public static void main(String[] args) throws Exception {
FileWriter out = new FileWriter("temp/test2.txt");
char[] chars = new char[] {'A','B','C','가','각','간','똘','똥'};
out.write(chars, 2, 3); // 2번 문자부터 3 개의 문자를 출력한다.
out.close();
System.out.println("데이터 출력 완료!");
}
}
문자열 출력
문자열을 통째로 출력하고 싶다면 그냥 write 메서드의 파라미터로 String 객체를 넘겨주면 된다. 물론 이 때도 항상 출력될 문자의 인코딩 정보를 염두에 두어야 한다.
import java.io.FileWriter;
public class Exam0410 {
public static void main(String[] args) throws Exception {
FileWriter out = new FileWriter("temp/test2.txt");
String str = new String("AB가각");
out.write(str);
out.close();
System.out.println("데이터 출력 완료!");
}
}
char 배열 입력
한번에 읽고 싶은 만큼 문자를 읽어 char 배열에 담는 방법도 있다. 이것도 FileInputStream에서 byte 배열 길이만큼 byte를 읽은 후 배열에 담았을 때와 동일한 방법으로 원하는 크기의 배열을 준비해서 read()의 파라미터로 넘겨주면 이 배열이 꽉 찰때까지 char 단위로 읽은 후 UCS2 문자 집합으로 변환하여 배열에 담을 것이다. 단 배열이 꽉 차기 전에 파일에 더 이상 읽을 문자가 없다면 배열의 길이보다 덜 입력될 것이다. 실제로 입력된 문자의 개수가 read()의 리턴 값이다.
package com.eomcs.io.ex03;
import java.io.FileReader;
public class Exam0220 {
public static void main(String[] args) throws Exception {
FileReader in = new FileReader("temp/test2.txt");
char[] buf = new char[100];
int count = in.read(buf);
in.close();
System.out.printf("%d\n", count);
for (int i = 0; i < count; i++)
System.out.printf("%c(%x)\n", buf[i], (int)buf[i]);
System.out.println();
}
}
원하는 개수만큼 문자를 입력받아 배열에 원하는 인덱스부터 차례로 담고 싶다면, read 메서드의 두번째 파라미터로 입력받아 담고자하는 인덱스의 시작점, 세번째 파라미터로 입력받고자하는 문자의 개수를 넘겨주면 된다. 다만, 원하는 개수만큼 입력되기 전에 파일에 더 이상 읽을 문작 수가 없는 경우, 원하는 개수보다 덜 입력될 수도 있다. read() 메서드의 리턴 값은 실제로 입력받은 문자의 개수이다.
import java.io.FileReader;
public class Exam0320 {
public static void main(String[] args) throws Exception {
FileReader in = new FileReader("temp/test2.txt");
char[] buf = new char[100];
int count = in.read(buf, 10, 40); // 40개의 문자를 읽어 10번 방부터 저장한다.
in.close();
System.out.printf("%d\n", count);
for (int i = 0; i < 20; i++)
System.out.printf("%c(%x) ", buf[i], (int) buf[i]);
System.out.println();
}
}
캐릭터 버퍼에 문자 입력
한번에 많은 char 들을 입력할 때, char[]이 아닌 CharBuffer를 read() 메서드의 파라미터로 넘겨줄 수도 있다. CharBuffer는 해당 클래스의 스태틱 메서드인 allocate()을 사용하여 생성할 수 있다. 이 객체를 파라미터에 넣어주면 CharBuffer에 할당된 크기만큼 바이트들을 읽어 담아준다.단, CharBuffer에 입력받은 문자를 담을 때마다 커서의 위치가 한칸씩 뒤로 옮겨지기 때문에 CharBuffer에 담긴 char들을 처음부터 읽으려면 커서의 위치를 처음으로 되돌려야 한다. 이처럼 커서의 위치를 처음으로 위치시키는 메서드는 flip() 메서드이다.
import java.io.FileReader;
import java.nio.CharBuffer;
public class Exam0420 {
public static void main(String[] args) throws Exception {
FileReader in = new FileReader("temp/test2.txt");
CharBuffer charBuf = CharBuffer.allocate(100);
int count = in.read(charBuf);
in.close();
System.out.printf("%d\n", count);
charBuf.flip();
System.out.printf("[%s]\n", charBuf.toString());
}
}
'국비 교육' 카테고리의 다른 글
2020.9.21일자 수업 : 파일 입출력 도구들 (0) | 2020.10.04 |
---|---|
2020.9.17 일자 수업 : 파일 입출력, 바이트 스트림, 캐릭터 스트림 (0) | 2020.10.01 |
2020.9.8일자 수업 : 로컬 클래스 (0) | 2020.10.01 |