
채널이 데이터를 읽고 쓰는 버퍼는 모두
ByteBuffer이다.
그렇기 때문에 채널을 통해 읽은 데이터를 복원하려면 ByteBuffer를 문자열 또는 다른 타입 버퍼(CharBuffer, ShortBuffer, IntBuffer, LongBuffer ,FloatBuffer, DoubleBuffer)로 변환해야 한다. 반대로 문자열 또는 다른 타입 버퍼의 내용을 채널을 통해 쓰고 싶다면 ByteBuffer로 변환해야 한다.
프로그램에서 가장 많이 처리되는 데이터는 String 타입, 즉 문자열이다. 채널을 통해 문자열을 파일이나 네트워크로 전송하려면 특정 문자셋(UTF-8, EUC-KR)으로 인코딩해서 ByteBuffer로 변환해야 한다.
java.nio.charset.Charset) 객체가 필요하다.Charset charset = Charset.forName("UTF-8"); //매개값으로 주어진 문자셋
Charset charset = Charset.defaulCharset(); //운영체제가 사용하는 디폴트 문자셋
Charset을 이용해서 문자열을 ByteBuffer로 변환하려면 다음과 같이 encode() 메소드를 호출하면 된다.String data = ...;
ByteBuffer byteBuffer = charset.encode(data);
반대로 파일이나 네트워크로부터 읽은 ByteBuffer가 특정 문자셋(UTF-8, EUC-KR)으로 인코딩되어 있을 경우, 해당 문자셋으로 디코딩해야만 문자열로 복원할 수 있다. Charset은 ByteBuffer를 디코딩해서 CharBuffer로 변환시키는 decode() 메소드를 제공하고 있기 때문에 다음과 같이 문자열로 쉽게 복원할 수 있다.
ByteBuffer byteBuffer = ...;
String data = charset.decode(byteBuffer).toString();
int[] 배열을 생성하고 이것을 파일이나 네트워크로 출력하기 위해서는 int[] 배열 또는 IntBuffer로부터 ByteBuffer를 생성해야 한다. int 타입은 4byte 크기를 가지므로 int[] 배열 크기 또는 IntBuffer의 capacity보다 4배 큰 capacity를 가진 ByteBuffer를 생성하고, ByteBuffer의 putInt() 메소드로 정수값을 하나씩 저장하면 된다.
다음은 int[] 배열을 IntBuffer로 래핑하고, 4배 큰 ByteBuffer를 생성한 후 정수값을 저장한다. putInt() 메소드는 position을 이동시키기 때문에 모두 저장한 후에는 position을 0으로 되돌려 놓는 flip() 메소드를 호출해야 한다.
int[] data = new int[] { 10, 20 };
IntBuffer intBuffer = IntBuffer.wrap(data);
ByteBuffer byteBuffer = ByteBuffer.allocate(intBuffer.capacity() * 4);
for (int i = 0; i < intBuffer.capacity(); i++) {
byteBuffer.putInt(intBuffer.get(i));
}
byteBuffer.flip();
반대로 파일이나 네트워크로부터 입력된 ByteBuffer에 4바이트씩 연속된 int 데이터가 저장되어 있을 경우, int[] 배열로 복원이 가능하다. ByteBuffer의 asIntBuffer() 메소드로 IntBuffer를 얻고, IntBuffer의 capacity와 동일한 크기의 int[] 배열을 생성한다. 그리고 IntBuffer의 get() 메소드로 int 값들을 배열에 저장하면 된다.
ByteBuffer byteBuffer = ...;
IntBuffer intBuffer = byteBuffer.asIntBuffer();
int[] data = new int[intBuffer.capacity()];
intBuffer.get(data);
🚫 주의
array()메소드는 래핑한 배열만 리턴하기 때문에int[]배열을wrap()메소드로 래핑한IntBuffer에서만 사용할 수 있다! 그러므로ByteBuffer에서asIntBuffer()로 얻은IntBuffer에서는array()메소드를 사용해서int[]배열을 얻을 수 없다.
ByteBuffer와 IntBuffer 간의 변환처럼 ByteBuffer와 ShortBuffer, LongBuffer, FloatBuffer, DoubleBuffer 간의 변환도 비슷한 방법으로 하면 된다.
| 리턴 타입 | 변환 메소드 | 설명 |
|---|---|---|
| ShortBuffer | asShortBuffer() | 2바이트씩 연속된 short 데이터를 가진 ByteBuffer일 경우 |
| IntBuffer | asIntBuffer() | 4바이트씩 연속된 int 데이터를 가진 ByteBuffer일 경우 |
| LongBuffer | asLongBuffer() | 8바이트씩 연속된 long 데이터를 가진 ByteBuffer일 경우 |
| FloatBuffer | asFloatBuffer() | 4바이트씩 연속된 float 데이터를 가진 ByteBuffer일 경우 |
| DoubleBuffer | asDoubleBuffer() | 8바이트씩 연속된 double 데이터를 가진 ByteBuffer일 경우 |
java.nio.channels.FileChannel을 이용하면 파일 읽기/쓰기를 할 수 있다.
FileChannel은 동기화 처리가 되어있기 때문에 멀티 스레드 환경에서 사용해도 안전하다.
FileChannel은 정적 메소드인 open()을 호출해서 얻을 수도 있지만, IO의 FileInputStream, FileOutputStream의 getChannel() 메소드를 호출해서 얻을 수도 있다.
다음은 open() 메소드로 FileChannel을 얻는 방법을 보여준다.
FileChannel fileChannel = FileChannel.open(Path path, OpenOption... options);
path 매개값은 열거나, 생성하고자 하는 파일의 경로를 Path 객체로 생성해서 지정하면 된다.options 매개값은 열기 옵션 값으로, StandardOpenOption의 다음 열거 상수를 나열해주면 된다.| 열거 상수 | 설명 |
|---|---|
| READ | 읽기용으로 파일을 연다. |
| WRITE | 쓰기용으로 파일을 연다. |
| CREATE | 파일이 없다면 새 파일을 생성한다. |
| CREATE_NEW | 새 파일을 만든다. 이미 파일이 있으면 예외와 함께 실패한다. |
| APPEND | 파일 끝에 데이터를 추가한다.(WRITE나 CREATE와 함께 사용됨) |
| DELETE_ON_CLOSE | 채널을 닫을 때 파일을 삭제한다.(임시 파일을 삭제할 때 사용) |
| TRUNCATE_EXISTING | 파일을 0바이트로 잘라낸다.(WRITE 옵션과 함께 사용됨) |
예를 들어 "C:\Temp\file.txt" 파일을 생성하고, 어떤 내용을 쓰고 싶다면 다음과 같이 매개값을 지정하면 된다.
FileChannel fileChannel = FileChannel.open(
Paths.get("C:/Temp/file.txt"),
StandardOpenOption.CREATE_NEW,
StandardOpenOption.WRITE
);
다음은 "C:\Temp\file.txt" 파일을 읽고, 쓸 수 있도록 FileChannel을 생성한다.
FileChannel fileChannel = FileChannel.open(
Paths.get("C:/Temp/file.txt"),
StandardOpenOption.READ,
StandardOpenOption.WRITE
);
FileChannel을 더 이상 이용하지 않을 경우에는 다음과 같이 close() 메소드를 호출해서 닫아주어야 한다.
fileChannel.close();
파일에 바이트를 쓰려면
FileChannel의write()메소드를 호출하면 된다.
매개값으로 ByteBuffer 객체를 주면 되는데, 파일에 쓰여지는 바이트는 ByteBuffer의 position부터 limit까지다. position이 0이고 limit이 capacity와 동일하다면 ByteBuffer의 모든 바이트가 파일에 쓰여진다. write() 메소드의 리턴값은 ByteBuffer에서 파일로 쓰여진 바이트 수이다.
int bytesCount = fileChannel.write(ByteBuffer src);
파일로부터 바이트를 읽기 위해서는 FileChannel의 read() 메소드를 호출하면 된다. 매개값으로 ByteBuffer 객체를 주면 되는데, 파일에 읽혀지는 바이트는 ByteBuffer의 position부터 저장된다. position이 0이면 ByteBuffer의 첫 바이트로부터 저장된다. read() 메소드의 리턴값은 파일에서 ByteBuffer로 읽혀진 바이트 수이다. 한 번 읽을 수 있는 최대 바이트수는 ByteBuffer의 capacity까지이므로 리턴되는 최댓값은 capacity가 된다. 더 이상 읽을 바이트가 없다면 read() 메소드는 -1을 리턴한다.
int bytesCount = fileChannel.read(ByteBuffer dst);
버퍼에 한 바이트를 저장할 때마다 position이 1씩 증가하게 되는데, read() 메소드가 -1을 리턴할 때까지 버퍼에 저장한 마지막 바이트의 위치는 position - 1 인덱스이다.
이것이 자바다 책