์๋ฐ์์ InputStream์ ๋ค๋ฃจ๋ค ๋ณด๋ฉด ํ์ผ์ ํค๋๋ง ์ด์ง ์ฝ์ด ํ์ผ์ ํ์์ ํ์ธํ ๋ค ๋ค์ ์ฒ์๋ถํฐ ์ ์ฒด ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์ผ ํ๋ ์ํฉ์ด ์๊ธฐ๋ ๊ฒฝ์ฐ๊ฐ ์๋ค. ์ด ๋ ์ง๊ด์ ์ผ๋ก reset() ๋ฉ์๋๋ฅผ ๋ ์ฌ๋ฆฌ์ง๋ง ๋ง์ ์ฝ๋๋ฅผ ์คํํ๋ฉด IOException์ด ๋ฐ์ํ๋ ๊ฒฝ์ฐ๊ฐ ์๋ค. ์ด๋ฒ ํฌ์คํ ์์๋ ์คํธ๋ฆผ์์ ์ด์ ์์น๋ก ๋์๊ฐ๋ ๋ฐฉ๋ฒ๊ณผ ํจ๊ป BufferedInputStream์ mark/reset/fill์ ๋์์ ๋ํด์ ๋ถ์ํ๊ณ RandomAccessFile์ ์ฌ์ฉ๋ฒ์ ๋ํด์ ์ ๋ฆฌํด๋ณด๊ณ ์ ํ๋ค.
InputStream
์๋ฐ์ java.io.InputStream์ ๋ชจ๋ ๋ฐ์ดํธ ์ ๋ ฅ ์คํธ๋ฆผ์ ์ต์์ ์ถ์ ํด๋์ค๋ค. InputStream์ read() ๋ฉ์๋๋ฅผ ์ ์ํ์ง๋ง ๋ฐ์ดํฐ๋ฅผ ๋๋๋ฆฌ๋ ๊ธฐ๋ฅ์ ๋ํด์๋ ๋งค์ฐ ๋ณด์์ ์ด๋ค. ์ด์ ๋ ์คํธ๋ฆผ์ Source๊ฐ ๋ค์ํ๊ธฐ ๋๋ฌธ์ด๋ค. ํ์ผ๊ณผ ๊ฐ์ด ๋ฐ์ดํฐ๊ฐ ๊ณ ์ ๋ ์ ์ฅ์์ ์๋ค๋ฉด ๋๋์๊ฐ๋ ๊ฒ์ด ๊ฐ๋ฅํ์ง๋ง, ํค๋ณด๋ ์ ๋ ฅ์ด๋ ๋คํธ์ํฌ ์์ผ๊ณผ ๊ฐ์ด ํ๋ฐ์ฑ์ด ๊ฐํ ์คํธ๋ฆผ์ ๊ฒฝ์ฐ์๋ ๋ฌผ๋ฆฌ์ ์ผ๋ก ๋๋์ ๊ฐ๋ ๊ฒ์ด ๋ถ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ด๋ค. ์ต์์ ์ถ์ ํด๋์ค์ธ InputStream์ ๋ชจ๋ ์คํธ๋ฆผ์ ๊ณ ๋ คํ ์ถ์๋ฉ์๋๋ฅผ ์ ์ํด์ผ ํ๊ธฐ ๋๋ฌธ์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋๋๋ฆฌ๋ ๊ธฐ๋ฅ์ ์ง์ํ์ง ์๋๋ก ํ๊ณ ์ด๋ฅผ ๊ตฌํํ๋ ๊ตฌํ์ฒด์๊ฒ ๋๋๋ฆฌ๋ ๊ธฐ๋ฅ์ Overwrite ํ๋๋ก ํ๋ค.
markSupported – ๋๋๋ฆด ์ ์์ด?
reset() ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ์คํธ๋ฆผ์ ์ด์ ์ง์ ์ผ๋ก ๋๋๋ฆฌ๋ ค ํ ๋ ๊ฐ์ฅ ๋จผ์ ํด์ผ ํ ๊ฒ์ ํด๋น ์คํธ๋ฆผ ์ธ์คํด์ค๊ฐ reset() ๊ธฐ๋ฅ์ ์ง์ํ๋์ง ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ๊ฒ์ด๋ค. ์ด๋ฅผ ํ์ธํ๋ ๋ฉ์๋๊ฐ ๋ฐ๋ก markSupported ๋ฉ์๋๋ค.
์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ๋ ์คํธ๋ฆผ ์ธ์คํด์ค์ markSupported ํธ์ถ ๊ฒฐ๊ณผ true์ธ ๊ฒ์ ๋๋๋ฆฌ๊ธฐ๋ฅผ ์ง์ํ๋ค๋ ๊ฒ์ด๋ค. ๋ง์ฝ markSupported๊ฐ false๋ฅผ ๋ฐํํ๋ ์คํธ๋ฆผ ์ธ์คํด์ค์ mark๋ฅผ ํธ์ถํ๋ฉด ์๋ฌด์ผ๋ ์ผ์ด๋์ง ์๊ณ , reset์ ํธ์ถํ๋ฉดIOException์ด ๋ฐ์ํ๋ค.
public class StreamTest {
private File file = new File("test.txt");
@BeforeEach
void setUp() {
if (file.exists()) {
file.delete();
}
// ํ
์คํธ์ฉ ํ์ผ ์์ฑ
try ( FileOutputStream fos = new FileOutputStream(file)) {
fos.write("Hello Java IO".getBytes());
} catch ( IOException e) {
e.printStackTrace();
}
}
@Test
void file_input_stream_test() {
// FileInputStream์ผ๋ก ์ฝ๊ธฐ ์๋
try ( InputStream is = new FileInputStream(file)) {
// 1. mark/reset ์ง์ ์ฌ๋ถ ํ์ธ
// FileInputStream์ ๋ด๋ถ ๋ฒํผ๊ฐ ์์ผ๋ฏ๋ก false๋ฅผ ๋ฐํํฉ๋๋ค.
boolean supported = is.markSupported();
System.out.println("Mark Supported: " + supported); // ์ถ๋ ฅ: false
if (!supported) {
System.out.println("์ด ์คํธ๋ฆผ์ mark/reset์ ์ง์ํ์ง ์์ต๋๋ค.");
}
// 2. ๊ฐ์ ๋ก mark ํธ์ถ (์ง์ํ์ง ์์๋ ์์ธ๋ ๋ฐ์ํ์ง ์๊ณ ๋ฌด์๋จ)
is.mark(100);
// 3. 5๋ฐ์ดํธ ์ฝ๊ธฐ ("Hello")
byte[] buffer = new byte[5];
is.read(buffer);
System.out.println("Read Data: " + new String(buffer));
// 4. reset ์๋ -> IOException ๋ฐ์
try {
is.reset();
} catch (IOException e) {
System.out.println("IOException, ์์๋ ์๋ฌ: " + e.getMessage());
// ์ถ๋ ฅ: java.io.IOException: reset() not supported ๋ฑ
}
} catch (IOException e) {
e.printStackTrace();
}
}
}Java์ฝ๋ ์คํ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ๋ค.
Mark Supported: false
์ด ์คํธ๋ฆผ์ mark/reset์ ์ง์ํ์ง ์์ต๋๋ค.
Read Data: Hello
IOException, ์์๋ ์๋ฌ: mark/reset not supportedPlaintextFileInputStream๊ณผ ๊ฐ์ ๊ธฐ๋ณธ์ ์ธ ๋ ธ๋ ์คํธ๋ฆผ์ ๋๋๋ฆฌ๊ธฐ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง ์๋๋ค. ๋๋๋ฆฌ๊ธฐ๋ฅผ ์ํด์๋ ์ฝ์๋ ๋ฐ์ดํฐ๋ฅผ ์ด๋๊ฐ์ ๊ธฐ์ตํด๋ฌ์ผ ํ๋๋ฐ FileInputStream์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์๋ง์ ์ฌ์ฉ์์๊ฒ ์ ๋ฌํ๊ณ ์์ด๋ฒ๋ฆฌ๊ธฐ ๋๋ฌธ์ด๋ค.
mark(int readlimit) ๊ณ์ฝ
mark ๋ฉ์๋๋ ์ฝ๊ฒ ๋งํ์๋ฉด ๋ค์๊ณผ ๊ฐ์ ๋์์ ์ ์ํ๋ค.
์ง๊ธ ํ์ฌ ์์น์ ๋งํน(mark)์ ํด๋๊ณ ์์ผ๋ก ์ต๋ readlimit ๋ฐ์ดํธ๋งํผ ๋ ์ฝ์ ๋๊น์ง๋ mark๋ฅผ ์ ์งํด์ค. ๋ง์ฝ readlimit ๋ฐ์ดํธ๋งํผ ๋ ์ฝ์ผ๋ฉด ๊ทธ ๋๋ mark๋ฅผ ์ ๊ฑฐํด๋ ์ข์.
mark ๊ธฐ๋ฅ์ ์ง์ํ๋ ์คํธ๋ฆผ (์. BufferedInputStream, ByteArrayInputStream)์ ์ฌ์ฉํ ๋ mark ๋ฉ์๋์ ํ๋ผ๋ฏธํฐ์ธ readlimit์ ์ ํํ ์๋ฏธ๋ฅผ ๋ค์๊ณผ ๊ฐ๋ค.
- readlimit์ “๋งํฌ(mark)ํ ์์น๋ฅผ ๋ฌดํจํํ์ง ์๊ณ ์์ผ๋ก ์ฝ์ ์ ์๋ ์ต๋ ๋ฐ์ดํธ ์“๋ฅผ ์๋ฏธํ๋ค.
- mark(100)์ ํธ์ถํ๋ค๋ฉด ์คํธ๋ฆผ์ “์ต์ํ 100๋ฐ์ดํธ๋ฅผ ๋ ์ฝ์ ๋๊น์ง๋ ํ์ฌ ์์น๋ฅผ ๊ธฐ์ตํด ์ฃผ๊ฒ ๋ค“๋ผ๊ณ ์ฝ์ํ๋ ๊ฒ์ด๋ค.
readlimit ํ๋ผ๋ฏธํฐ๊ฐ ํ์ํ ์ด์ ๋ ๋ฉ๋ชจ๋ฆฌ(Buffer) ๋๋ฌธ์ด๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ์คํธ๋ฆผ์ ํ๋ฒ ์ฝ์ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ์ตํ์ง ์์ง๋ง markSupported๋ฅผ ์ง์ํ๋ ๊ฒฝ์ฐ mark๋ฅผ ํธ์ถํ๋ ์๊ฐ ์คํธ๋ฆผ ์ธ์คํด์ค๋ ํ์ฌ ์์น ์ดํ ๋ค์ด์ค๋ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ถ ๋ฒํผ์ ์ ์ฅํ๋๋ฐ ๋์ค์ reset์ ํธ์ถํ์ ๋ ์ ์ฅํด๋ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๊บผ๋ด์ฌ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ํ์ง๋ง ๋ฒํผ ๋ฉ๋ชจ๋ฆฌ ์์ ๋ฌดํ์ ๋ฐ์ดํฐ๋ฅผ ์์๋ ์ ์๊ธฐ ๋๋ฌธ์ readlimit ๊ฐ์ผ๋ก ๋ฒํผ์ ํฌ๊ธฐ๋ฅผ ์ ํํ๊ธฐ ์ํด์๋ค.
๋ด๊ฐ ์ผ๋งํผ์ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ์ตํ๊ณ ์์ด์ผ ํด?
readlimit์ 100๋ฐ์ดํธ๋ผ๊ณ ํ๋ฉด ์คํธ๋ฆผ์ 100๋ฐ์ดํธ๊น์ง ๋ฒํผ์ ์ ์ฅํ๊ณ 101๋ฐ์ดํธ๋ฅผ ์ฝ๋ ์๊ฐ ๊ณ์ฝ์ ๊นจ์ง๊ฒ ๋๋ค.
mark ๋ฉ์๋์ readlimit์ ์ค์ ํ ๋๋ ๊ฐ์ ๋๋ํ๊ฒ ์ก๋ ๊ฒ์ด ์์ ํ๋ค. readlimit์ “์ต์ํ ์ด๋งํผ์ ๋ณด์ฅํด๋ผ“๋ผ๋ ์๋ฏธ์ด๊ธฐ ๋๋ฌธ์ ์ฝ์ ๋ฐ์ดํธ ์๋ณด๋ค ๋๋ํ๊ฒ ์ก๋ ๊ฒ์ด ์ข๋ค. ๋ง์ฝ readlimit์ ์ด๊ณผํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ํ reset์ ํธ์ถํ๋ฉด IOException ์์ธ๊ฐ ๋ฐ์ํ ์ ์๋ค.
ByteArrayInputStream
byte[] ๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ์ฝ๋ ByteArrayInputStream์ ์ดํด๋ณด์. markSupported๋ฅผ ์ง์ํ๊ธฐ ๋๋ฌธ์ mark, reset์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ค์ ์ฝ์ ์ ์๋ค.
@Test
void byte_array_input_stream_test() {
String content = "header:byte array input stream test";
byte[] data = content.getBytes();
try ( ByteArrayInputStream bis = new ByteArrayInputStream(data) ) {
// 'header' ๋ถ๋ถ๋ง ๋จผ์ ์ฝ์ด์ ํ์ธ
byte[] buf = new byte[7];
bis.read(buf);
System.out.println("ํค๋ ํ์ธ: " + new String(buf));
// ํ์ฌ ์์น ๋งํน
// ByteArrayInputStream์ ์ด๋ฏธ ์์ค ์์ฒด๊ฐ buffer ์ญํ ์ ํ๋ฏ๋ก
// readlimit ํ๋ผ๋ฏธํฐ ๊ฐ์ด ์๋ฏธ๊ฐ ์๋ค. ์ฌ๊ธฐ์๋ 0์ ์
๋ ฅํ๋ค. (์ฌ์ค ์๋ฌด๊ฐ์ด๋ ์๊ด์ด ์์)
System.out.println("Marking!");
// 'header:' ๋ค์ ๋ถ๋ถ์ marking
bis.mark( 0 );
// ๋ค์ 7byte read
bis.read(buf);
System.out.println("header ๋ค์ 7๋ฌธ์: " + new String(buf));
// ์์น ๋ฆฌ์
System.out.println("Resetting!");
bis.reset();
byte[] allContents = bis.readAllBytes();
System.out.println("๋ด์ฉ ์ ์ฒด: " + new String(allContents));
}
catch ( IOException e ) {
// ByteArrayInputStream์์๋ ์ฌ์ค์ IOException์ด ๋ฐ์ํ์ง ์์ง๋ง
// InputStream ๊ท์ฝ์ ์์ธ ์ฒ๋ฆฌ์ ๋ํ ์ ์๋ ํ์ํจ
throw new RuntimeException( e );
}
}Java์ฝ๋ ์คํ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ๋ค.
ํค๋ ํ์ธ: header:
Marking!
header ๋ค์ 7๋ฌธ์: byte ar
Resetting!
๋ด์ฉ ์ ์ฒด: byte array input stream testPlaintext- ๋จผ์ ์ฒ์ 7byte์ฝ๋๋ค. (‘header:’)
- ํ์ฌ ์์น์ marking ํ๋ค. (ByteArrayInputStream์ ์์ค ์์ฒด๊ฐ ๋ฉ๋ชจ๋ฆฌ ์์ ์๊ธฐ ๋๋ฌธ์ readlimit ๊ฐ์ ์๋ฏธ์๋ค)
- ๊ทธ ๋ค์ 7byte๋ฅผ ์ฝ๋๋ค. (‘byte ar’)
- reset()์ ํธ์ถํ์ฌ ์์น ํฌ์ธํฐ๋ฅผ marking ํ๋ ์๋ฆฌ๋ก ์ฎ๊ธด๋ค.
- ์ ์ฒด๋ฅผ ์ฝ๋๋ค. -> marking ํ๋ ์์น๋ถํฐ ๋๊น์ง ์ฝ๋๋ค. (‘byte array input stream test’)
BufferedInputStream
BufferedInputStream ์ญ์ markSupported๋ฅผ ์ง์ํ๊ธฐ ๋๋ฌธ์ mark, reset์ ํ์ฉํ ์ ์๋ค. ByteArrayInputStream๊ณผ ๋ฌ๋ฆฌ BufferedInputStream์ mark readlimit ๊ฐ์ ์ค์ํ๋ค. ์ฐธ๊ณ ๋ก BufferedInputStream์ ๋ํดํธ ๋ฒํผ ํฌ๊ธฐ๋ 8192byte(8KB)๋ค. BufferedInputStream์ ๋ฒํผ ํฌ๊ธฐ๋ ์์ฑ์์์ ์ง์ ํ์ฌ ๋ณ๊ฒฝํ ์ ์๋ค. ๋ณ๋๋ก ์ง์ ํ์ง ์์ผ๋ฉด ๋ํดํธ ์ฌ์ด์ฆ๊ฐ ์ ์ฉ๋๋ค.
public class StreamTest {
private File file = new File("test.txt");
@BeforeEach
void setUp() {
if (file.exists()) {
file.delete();
}
// ํ
์คํธ์ฉ ํ์ผ ์์ฑ
try ( FileOutputStream fos = new FileOutputStream(file)) {
fos.write("header:buffered input stream test".getBytes());
} catch ( IOException e) {
e.printStackTrace();
}
}
@Test
void buffered_input_stream_test() {
try ( BufferedInputStream bis = new BufferedInputStream( new FileInputStream( file ) )) {
// ์ฒ์ 7byte read
byte[] buf = new byte[7];
bis.read(buf);
System.out.println("ํค๋ ํ์ธ: " + new String(buf));
// ์คํธ๋ฆผ์์ ์ฝ์ด๋ค์ผ ์ ์๋ ๋จ์ byte ๋งํผ readlimit์ผ๋ก ์ ์ฉํ๋ค.
// ๋ํดํธ ๋ฒํผ ํฌ๊ธฐ๊ฐ 8192 byte ์ด๋ฏ๋ก 8192 byte๋ฅผ ๋์ง ์๋ ๊ฒ์ด ์ข๋ค.
int available = bis.available();
if ( available > 8192 ) {
available = 8192;
}
// ํ์ฌ ์์น Marking!
System.out.println("Marking!");
bis.mark( available );
// ๋ค์ 7byte read
bis.read(buf);
System.out.println("header ๋ค์ 7๋ฌธ์: " + new String(buf));
// ์์น ๋ฆฌ์
System.out.println("Resetting!");
bis.reset();
byte[] allContents = bis.readAllBytes();
System.out.println("๋ด์ฉ ์ ์ฒด: " + new String(allContents));
}
catch ( IOException e ) {
throw new RuntimeException( e );
}
}
}Java์ํ ์ฝ๋๋ BufferedInputStream์ ๋ํดํธ ๋ฒํผ ํฌ๊ธฐ๋ฅผ ์ฌ์ฉํ์๋ค.
์คํ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ๋ค.
ํค๋ ํ์ธ: header:
Marking!
header ๋ค์ 7๋ฌธ์: buffere
Resetting!
๋ด์ฉ ์ ์ฒด: buffered input stream testPlaintextBufferedInputStream fill() ๋์
mark(readlimit)์ ์ง์ ํ ์ดํ์ BufferedInputStream ๋ฒํผ์ ๋ง์ง๋ง๊น์ง ์ฝ๊ฒ๋๋ฉด fill() ํจ์๊ฐ ํธ์ถ๋๋๋ฐ ๋ด๋ถ ๋์์ ๋ค์๊ณผ ๊ฐ๋ค.
์ฌ๋ผ์ด๋ฉ : ์์ชฝ ๊ณต๊ฐ ์ฌํ์ฉ
marking๋ ์์น(๋ฒํผ๋ด ์ธ๋ฑ์ค ์์น)๊ฐ > 0 ๊ฒฝ์ฐ์ ์ฌ๋ผ์ด๋ฉ ์ฒ๋ฆฌ๋ฅผ ํ๋ค.
if (markpos > 0) { /* can throw away early part of the buffer */
int sz = pos - markpos;
System.arraycopy(buffer, markpos, buffer, 0, sz);
pos = sz;
markpos = 0;
}Java์ฆ marking๋ ์์น๋ถํฐ ๋ฒํผ์ ๋๊น์ง ๋ณต์ฌํ์ฌ ๋ฒํผ์ ๋งจ ์์์๋ถํฐ ๋ฎ์ด์ฐ๊ณ marking ์์น๋ฅผ 0์ผ๋ก ๋ณ๊ฒฝํ๋ค.
์ด ๋๋ marking ๋ฌดํจํ๋ฅผ ํ์ง ์๋๋ค.
(marking ๋ ์์น๋ฅผ ๋ฌดํจํ ํ๋ค๋ ๊ฒ์ marking๋ ์์น ๊ฐ์ -1๋ก ์ค์ ํ์ฌ mark๋ฅผ ์์๋ ์ผ๋ก ๋ง๋๋ ๋์์ ๋งํ๋ค.)
์ ์ํ ์ฝ๋๋ฅผ ์ฝ๊ฐ ์์ ํ์ฌ ํ์ธํด ๋ณด์.
@Test
void buffered_input_stream_test() {
try ( BufferedInputStream bis = new BufferedInputStream( new FileInputStream( file ), 10 )) {
// ์ฒ์ 7byte read
byte[] buf = new byte[7];
bis.read(buf);
System.out.println("ํค๋ ํ์ธ: " + new String(buf));
// ํ์ฌ ์์น Marking! (readlimit: 5)
System.out.println("Marking!");
bis.mark( 5 );
// ๋ค์ 7byte read
bis.read(buf);
System.out.println("header ๋ค์ 7๋ฌธ์: " + new String(buf));
// ์์น ๋ฆฌ์
System.out.println("Resetting!");
bis.reset();
byte[] allContents = bis.readAllBytes();
System.out.println("๋ด์ฉ ์ ์ฒด: " + new String(allContents));
}
catch ( IOException e ) {
throw new RuntimeException( e );
}
}Javaํ
์คํธ๋ฅผ ์ํด BufferedInputStream์ ๋ฒํผ์ ํฌ๊ธฐ๋ฅผ 10์ผ๋ก ์ฃผ์ด ๋ฒํผ์ ๋๊น์ง ์ฝ๋๋ก ํ์ฌ fill์ ๋ฐ์์ํจ๋ค.
์ฒ์ read ํธ์ถ ํ ๋ฒํผ์ ์ํ๋ ๋ค์๊ณผ ๊ฐ๋ค.
header:buf (10byte)
^
|
marking (ํ์ฌ pos)Java์ดํ mark(5) ์ง์ ํ 7byte๋ฅผ ์ฝ์ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ๋ค. ๋ฒํผ์ marking ์์น ๋ถํฐ ์ฝ์ 3byte(‘buf’)๊ฐ ๋ฒํผ์ 0๋ฒ์งธ ์์น๋ก overwrite ๋๊ณ marking ์์น๋ฅผ 0์ผ๋ก ๋ณ๊ฒฝ ํ ๋ฒํผ์ ๋๋จธ์ง 7byte๋ ํ์ผ๋ก ๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด์ ์ฑ์ด๋ค.
buffered i
^
|
marking ์์นJava์ดํ reset()์ ํธ์ถํ๋ฉด ์ ์์ ์ผ๋ก marking ์์น๋ก ๋์๊ฐ ๋ค์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ฒ ๋๋ค. (marking ๋ฌดํจํ๊ฐ ๋์ง ์์์ผ๋ฏ๋ก)
Marking ๋ฌดํจํ
marking๋ ์์น๊ฐ > 0์ด ์๋ ๊ฒฝ์ฐ BufferedInputStream์ ๋ฒํผ์ ํฌ๊ธฐ๊ฐ readlimit ๊ฐ๋ณด๋ค ๋ ํฐ ๊ฒฝ์ฐ Marking์ ๋ฌดํจํ ๋๋ค. (markpos == 0 && buffer.length >= marklimit)
else if (buffer.length >= marklimit) {
markpos = -1; /* buffer got too big, invalidate mark */
pos = 0; /* drop buffer contents */
}Java์ด์ ๋ markpos == 0 ์ธ ๊ฒฝ์ฐ (์ฌ๋ผ์ด๋ฉ ์กฐ๊ฑด์ด ์๋๋ฏ๋ก > 0) ์ธ๋ฐ marking ์์น๊ฐ 0์ด๋ฏ๋ก ๋ฒํผ์ 0๋ฒ์งธ ์์น์ ๋ฎ์ด์ฌ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ์ด ๊ฒฝ์ฐ์๋ markpost๋ฅผ -1๋ก ์ง์ ํ์ฌ marking ๋ฌดํจํ๋ฅผ ์ ์ธํ๋ค.
(์ดํ reset() ํธ์ถ์ IOException์ ๋ฐ์ ์์ธ)
์ํ ์ฝ๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์์ ํ์ฌ ํ์ธํด ๋ณด์.
@Test
void buffered_input_stream_exception_test() {
try ( BufferedInputStream bis = new BufferedInputStream( new FileInputStream( file ), 10 )) {
// markpos == 0, readlimit(marklimit) == 5, buffer size == 10
bis.mark( 5 );
// 7 byte read
byte[] buf = new byte[7];
bis.read(buf);
System.out.println("ํค๋ ํ์ธ: " + new String(buf));
// ์ฌ๊ธฐ์ fill ํธ์ถ ๋ฐ์ (markpos == 0 && buffer size >= readlimit) ์ด๋ฏ๋ก marking ๋ฌดํจํ ๋ฐ์
byte[] allContents = bis.readAllBytes();
System.out.println("๋๋จธ์ง ์ฝ์: " + new String(allContents));
// ์์น ๋ฆฌ์
(marking ๋ฌดํจํ๋ก ์์ธ ๋ฐ์ํจ!!!!)
System.out.println("Resetting!");
bis.reset();
}
catch ( IOException e ) {
throw new RuntimeException( e );
}
}Java์ฝ๊ธฐ ์ ์ mark๋ฅผ ์ง์ ํ์ฌ marking ์์น๋ฅผ 0์ผ๋ก ์ค์ ํ์๋ค. ๋ฒํผ ํฌ๊ธฐ๋ 10byte, readlimit์ 5byte๋ค.
์ฒซ๋ฒ์งธ 7 byte๋ฅผ ์ฝ์ ํ ๋๋ฒ์งธ readAllBytes()๋ฅผ ํธ์ถํ์ฌ ๋๋จธ์ง๋ฅผ ๋ชจ๋ ์ฝ๋๋ก ํ์๋ค. ์ด ๊ณผ์ ์์ ๋ฒํผ์ ๋๊น์ง ์ฝ์ด์ผ ํ๋ฏ๋ก fill()์ด ๋ฐ์ํ๊ณ (markpos == 0 && buffer.length >= marklimit) ์กฐ๊ฑด์ ์ํด์ marking์ด ๋ฌดํดํ ๋์ด -1๋ก ๋ณ๊ฒฝ๋๋ค.
์ดํ reset()์ ํธ์ถํ๋ฉด marking์ด ๋ฌดํดํ ๋์๊ธฐ ๋๋ฌธ์ ๋์๊ฐ ์ ์์ด์ IOException ์์ธ๊ฐ ๋ฐ์ํ๋ค.
ํค๋ ํ์ธ: header:
๋๋จธ์ง ์ฝ์: buffered input stream test
Resetting!
java.io.IOException: Resetting to invalid mark
...
...Plaintext๋ฒํผ ํ์ฅ (Growing)
์ฌ๋ผ์ด๋ฉ๊ณผ Marking ๋ฌดํจํ ์กฐ๊ฑด์ ํด๋น๋์ง ์๋ ๊ฒฝ์ฐ BufferedInputStream์ ํฌ๊ธฐ๋ฅผ ๋์ ์ผ๋ก ํ์ฅํ๋ค.
(markpos == 0 && buffer.length < marklimit)
else { /* grow buffer */
int nsz = ArraysSupport.newLength(pos,
1, /* minimum growth */
pos /* preferred growth */);
if (nsz > marklimit)
nsz = marklimit;
byte[] nbuf = new byte[nsz];
System.arraycopy(buffer, 0, nbuf, 0, pos);
if (!U.compareAndSetReference(this, BUF_OFFSET, buffer, nbuf)) {
// Can't replace buf if there was an async close.
// Note: This would need to be changed if fill()
// is ever made accessible to multiple threads.
// But for now, the only way CAS can fail is via close.
// assert buf == null;
throw new IOException("Stream closed");
}
buffer = nbuf;
}Java๋ฒํผ์ ๋งจ ์์ผ๋ก ๋ฎ์ด์ธ ์๋ ์์ง๋ง (markpos == 0์ด๋ฏ๋ก) ์ฌ์ฉ์๊ฐ readlimit์ bufferํฌ๊ธฐ ๋ณด๋ค ๋ ํฌ๊ฒ ์ง์ ํ์ผ๋๊น ๋ ๋๋ ค์ฃผ์๋ ์๋ฏธ์ ๋์์ด๋ค. ์ด ๊ฒฝ์ฐ ์ญ์ marking ๋ฌดํจํ๋ ๋ฐ์ํ์ง ์๋๋ค.
marking ๋ฌดํจํ ์ํ ์ฝ๋๋ฅผ ์์ ํ์ฌ ๋์์ ํ์ธํด๋ณด์.
@Test
void buffered_input_stream_no_exception_test() {
try ( BufferedInputStream bis = new BufferedInputStream( new FileInputStream( file ), 10 )) {
// markpos == 0, readlimit == 40, buffer size == 10
bis.mark(40);
// 7 byte read
byte[] buf = new byte[7];
bis.read(buf);
System.out.println("ํค๋ ํ์ธ: " + new String(buf));
// ์ฌ๊ธฐ์ fill ํธ์ถ ๋ฐ์ (markpos == 0 && buffer size < readlimit) ์ด๋ฏ๋ก buffer ํ์ฅ์ด ์ผ์ด๋๋ค.
// readlimit size ๋งํผ.
byte[] allContents = bis.readAllBytes();
System.out.println("๋๋จธ์ง ์ฝ์: " + new String(allContents));
// ์์น ๋ฆฌ์
System.out.println("try Resetting!");
bis.reset();
System.out.println("Success Resetting!");
}
catch ( IOException e ) {
throw new RuntimeException( e );
}
}
Java์คํ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ๋ค.
ํค๋ ํ์ธ: header:
๋๋จธ์ง ์ฝ์: buffered input stream test
try Resetting!
Success Resetting!Plaintext๋ฐ์ดํฐ๋ฅผ ์ฝ๊ธฐ ์ ์ mark(40)์ ์ง์ ํ์ฌ markpos = 0, readlimit(marklimit) = 40์ด ๋๋๋ก ํ๋ค. (buffer size = 10)
๋๋ฒ์งธ readAllBytes() ํธ์ถ์ ์ญ์ fill() ํธ์ถ์ด ์ผ์ด๋๋ฉฐ (markpos == 0 && buffer.length < marklimit) ์กฐ๊ฑด์ ๋ฐ๋ผ์ ๋ฒํผ์ ํฌ๊ธฐ๊ฐ 10 -> 40byte๋ก ํ์ฅ ๋๋ค.
์ฒ์ read() ํธ์ถ ์ดํ ๋ฒํผ ์ํ๋ ๋ค์๊ณผ ๊ฐ๋ค.
[104, 101, 97, 100, 101, 114, 58, 98, 117, 102] --> 10byteJava๋๋ฒ์งธ readAllBytes() ํธ์ถ ์ดํ ๋ฒํผ ์ํ๋ ๋ค์๊ณผ ๊ฐ๋ค.
[104, 101, 97, 100, 101, 114, 58, 98, 117, 102, 102, 101, 114, 101, 100, 32, 105, 110, 112, 117, 116, 32, 115, 116, 114, 101, 97, 109, 32, 116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0] --> 40byteJava๋์ ์ผ๋ก ๋ฒํผ์ ํฌ๊ธฐ๊ฐ readlimit๋งํผ ํ์ฅ๋๋ฉด์ marking ๋ฌดํจํ๋ ๋ฐ์ํ์ง ์๋๋ค.
๊ฒฐ๋ก ์ ์ผ๋ก reset() ํธ์ถ์ marking์ด ๋ฌดํจํ ๋ ์ํ๋ฉด IOException ์์ธ๊ฐ ๋ฐ์ํ๋๋ฐ marking์ด ๋ฌดํจํ ๋๋ ์กฐ๊ฑด์ ๋ค์๊ณผ ๊ฐ๋ค.
marking์ด ๋ฌดํจํ ๋๋ ์กฐ๊ฑด (markpos = -1)
๋ฒํผ์ ๋๊น์ง ์ฝ๊ฒ ๋๋ฉด fill ๋์์ด ๋ฐ์
marking ์์น๊ฐ ๋ฒํผ์ 0 ์์น๊ณ buffer์ ํฌ๊ธฐ๊ฐ readlimt๋ณด๋ค ํฌ๊ฑฐ๋ ๊ฐ์ ๊ฒฝ์ฐ
(markpos == 0 && buffer.length < marklimit)
RandomAccessFile
InputStream์ด ์์ฐจ์ ์ธ ๋ฐ์ดํธ ํ๋ฆ์ ๋ค๋ฃฌ๋ค๋ฉด RandomAccessFile์ ํ์ผ ์์คํ
์ ๋ณธ์ง์ ์ธ ํน์ฑ์ธ ๋๋ค ์ ๊ทผ์ ๊ทธ๋๋ก ์ฌ์ฉํ ์ ์๋ ๊ฐ๋ ฅํ ๋๊ตฌ๋ค.
RandomAccessFile์ InputStream์ด๋ OutputStream์ ์์๋ฐ์ง ์๋๋ค. ๋์ DataInput๊ณผ DataOutput ์ธํฐํ์ด์ค๋ฅผ ๋ชจ๋ ๊ตฌํํ๋ค. ์ด๋ RandomAccessFile์ ํ์ผ์ ์ฝ๊ณ ์ฐ๋ ์๋ฐฉํฅ ํต๋ก์์ ์๋ฏธํ๋ค.
RandomAccessFile์ ํ์ผ ๋ฐ์ดํฐ๋ฅผ ๋ง์น ๊ฑฐ๋ํ ๋ฐ์ดํธ ๋ฐฐ์ด ์ฒ๋ผ ์ทจ๊ธํ๋๋ฐ ๋ฐฐ์ด์ ์ธ๋ฑ์ค์ ์ ๊ทผํ๋ฏ์ด ํ์ผ์ ์์์ ์์น๋ก ์ปค์(File Pointer)๋ฅผ ์ด๋ ์ํฌ ์ ์๋ค.
ํต์ฌ ๋ฉ์นด๋์ฆ
RandomAccessFile์ ๋ชจ๋ ๋์์ ํ์ผ ํฌ์ธํฐ๋ฅผ ์ค์ฌ์ผ๋ก ์ด๋ฃจ์ด์ง๋ค.
- getFilePointer(): ํ์ฌ ์ปค์์ ์์น(๋ฐ์ดํธ ์คํ์ )์ ๋ฐํํ๋ค.
- seek(long pos): ์ปค์๋ฅผ ํ์ผ์ pos ์์น๋ก ์ด๋์ํจ๋ค.
- length(): ํ์ผ์ ์ ์ฒด ํฌ๊ธฐ๋ฅผ ๋ฐํํ๋ค.
seek() ๋ฉ์๋๋ ์ด์์ฒด์ ์ ์์คํ ์ฝ(ex. lseek())์ ์ง์ ๋งคํ๋๋ค. ์ด๋ ๋งค์ฐ ๊ฐ๋ ฅํ์ง๋ง OS๋ ๋ฒจ์ ์ปจํ ์คํธ ์ค์์นญ ๋น์ฉ์ด ์กด์ฌํ๋ค. BufferedInputStream์ reset()๊ณผ RandomAccessFile์ seek()์ ์ด์ ์์น๋ก ๋์๊ฐ๋ค๋ ๊ฒฐ๊ณผ๋ ๊ฐ์ง๋ง ๋์ ๋ฐฉ์์๋ ํฐ ์ฐจ์ด๊ฐ ์๋ค.
| BufferedInputStream (reset) | RandomAccessFile (seek) | |
| ๋์์๋ฆฌ | ๋ฉ๋ชจ๋ฆฌ ์์ ๋ฐฐ์ด ์ธ๋ฑ์ค๋ง ๋ณ๊ฒฝ | OS์๊ฒ ํ์ผ ์คํ์ ๋ณ๊ฒฝ ์์ฒญ (system call) |
| ์๋ | ๋งค์ฐ ๋น ๋ฆ (๋๋ ธ์ด) | ์๋์ ์ผ๋ก ๋๋ฆผ (๋ฐ๋ฆฌ์ด ~ ๋ง์ดํฌ๋ก์ด) |
| ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ | ๋์(๋๋์๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ชจ๋ ํ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅ) | ๋งค์ฐ ๋ฎ์(๋ฐ์ดํฐ๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅํ์ง ์์) |
| ๋ฐ์ดํฐ ์์ค | ํ์ผ, ๋คํธ์ํฌ, ๋ฉ๋ชจ๋ฆฌ๋ฑ ๋ชจ๋ ์คํธ๋ฆผ | ์ค์ง ๋ก์ปฌ ํ์ผ ์์คํ ์ ํ์ผ๋ง ๊ฐ๋ฅ |
RandomAccessFile์ ๋ฐ์ดํฐ๋ฅผ ๋ฒํผ์ ๋ด์๋๊ณ ์ด๋ํ์ง ์๊ณ ์์คํ ์ฝ์ ์ด์ฉํ์ฌ ์์น๋ฅผ ์ด๋ํ๊ธฐ ๋๋ฌธ์ ๋์ฉ๋ ํ์ผ์์ ์์์ ์์น๋ก ๋๋์๊ฐ์ผ ํ ๋ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
์๋ ์ํ ์ฝ๋๋ ํ์ผ์ ๋ง์ง๋ง ์ง์ ๋ n byte๋งํผ ์ฝ๋ ์ฝ๋๋ค. ์ฒ์ ๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ํ์ ์์ด ์ปค์๋ฅผ ๋ฐ๋ก ๋ง์ง๋ง ๋ถ๋ถ์ผ๋ก ์ด๋ํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ์ ์๋ค.
@Test
@DisplayName( "RAF๋ฅผ ์ด์ฉํ์ฌ ํ์ผ์ ๋ง์ง๋ง 500 ๋ฐ์ดํธ ์ฝ๊ธฐ" )
void random_access_file_test() throws IOException {
ClassPathResource resource = new ClassPathResource("gc.log");
int lastNBytes = 500;
// ์ฝ๊ธฐ ์ ์ฉ์ผ๋ก ํ์ผ์ ์ฐ๋ค.
try( RandomAccessFile raf = new RandomAccessFile(resource.getFile(), "r") ) {
// ์ ์ฒด ํฌ๊ธฐ
long totalSize = raf.length();
System.out.println("ํ์ผ ์ ์ฒด ํฌ๊ธฐ: " + totalSize);
long startPos = totalSize - lastNBytes;
// lastNBytes๊ฐ ํ์ผ์ ์ ์ฒด ํฌ๊ธฐ๋ณด๋ค ํฌ๋ฉด ์ฒ์๋ถํฐ ์ฝ์
if ( startPos < 0 ) {
startPos = 0;
}
// ํ์ผ ํฌ์ธํฐ๋ฅผ startPos ์์น๋ก ์ด๋
raf.seek( startPos );
System.out.println("์ปค์ ์์น ์ด๋: " + raf.getFilePointer());
byte[] buf = new byte[lastNBytes];
// buf๊ฐ ๋ชจ๋ ์ฑ์์ง ๋๊น์ง ์ฝ์์ ๋ณด์ฅ
raf.readFully( buf );
System.out.println("---- ๋ง์ง๋ง " + lastNBytes + " ๋ฐ์ดํธ ๋ด์ฉ ----");
System.out.println( new String(buf) );
System.out.println("---- ๋ ----");
}
catch ( IOException e ) {
e.printStackTrace();
}
}Javatest/resources/gc.log ํ์ผ์ ๋ง์ง๋ง 500byte ๋ด์ฉ์ ์ฝ์ด ์ถ๋ ฅํ๋ ์ฝ๋๋ค. ์คํธ๋ฆผ ๋ฐฉ์์ ํ์ผ์ ์ฒ์๋ถํฐ ๋ง์ง๋ง๊น์ง ์ฝ์ด์ผ ํ์ง๋ง RandomAccessFile์ ๋ฐ๋ก ๋ง์ง๋ง 500 byte ์์น๋ก ๋ฐ๋ก ์ด๋ํ ์ ์๋ค.
์ด๋ค ๊ฑธ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์๊น?
๊ฐ๊ฐ์ ์ํฉ์ ๋ฐ๋ผ ์ด๋ค ํ์ผ ์ฒ๋ฆฌ ๋ฐฉ์์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์์ง ํ๋ก ์ ๋ฆฌํด๋ดค๋ค.
| ์ถ์ฒ ํด๋์ค | ์ค๋ช | |
| ์์ฐจ์ ์ธ ํ์ผ ์ฝ๊ธฐ | BufferedInputStream (with FileInputStream) | ๋ฒํผ๋ง์ ํตํ ์์คํ ์ฝ ์ต์ํ๋ก ๋์ ์ฑ๋ฅ ์ ๊ณต (default 8KB ๋ฒํผ ํ์ฉ) |
| ํ์ผ ํฌ๋งท ํ์ธ | BufferedInputStream (mark / reset) | ํ์ผ์ ์ฒซ ๋ฐ์ดํธ๋ฅผ ์ฝ์ด ํ์ผ ์๊ทธ๋์ฒ ํ์ธ ํ ๋ค์ ์ฝ๊ธฐ |
| ๋์ฉ๋ ํ์ผ ์์ ์ ๊ทผ | RandomAccessFile | ๋ฉ๋ชจ๋ฆฌ ์๋ชจ ์์ด ๋์คํฌ์ ์ด๋ ์์น๋ก๋ ์ด๋ ๊ฐ๋ฅ (seek) |
| ๋ฐ์ดํฐ ์์ | RandomAccessFile | ํ์ผ ์ค๊ฐ์ ํน์ ๋ฐ์ดํธ์ด์ ๋ํด์ ์์ ๊ฐ๋ฅ (์คํธ๋ฆผ์ ๋ถ๊ฐ) |
์คํธ๋ฆผ์์ ํ์ผ์ ์ฝ์ ํ์ ๋ค์ ์ด์ ์ผ๋ก ๋๋๋ ค์ ์ฝ์ด์ผ ํ๋ ๊ฒฝ์ฐ BufferedInputStream ์ด๋ ByteArrayInputStream ์ฌ์ฉ์ ๊ฒํ ํด ๋ณด๋ ๊ฒ์ด ์ข๋ค. ByteInputStream์ ๊ฒฝ์ฐ์๋ ํ์ผ ๋ด์ฉ ์ ์ฒด๋ฅผ ๋ก๋ฉํ๋ฉด ํ์ผ ๋ด์ฉ ์ ์ฒด๊ฐ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ผ๊ฐ๋ฏ๋ก ํ ์ฌ์ฉ๋์ด ๋์ด๋ ์ ์์ผ๋ฏ๋ก ์์ ํ์ผ ์ฒ๋ฆฌ์์๋ง ๊ณ ๋ คํ๊ธฐ ๋ฐ๋๋ค.
๋ํ ๋์ฉ๋ ํ์ผ์ด๋ ์ฆ์ ์์น ์ด๋์ด ํ์ํ ๊ฒฝ์ฐ์๋ RandomAccessFile์ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ์ข๊ฒ ๋ค.
์ฐธ๊ณ ๋ก Java NIO์ FileChannel์ ์ฌ์ฉํ๋ฉด ๋ฉ๋ชจ๋ฆฌ ๋งคํ(MappedByteBuffer)๋ฑ์ ํตํด ๋์ฉ๋ ํ์ผ์ ๋ ํจ์จ์ ์ผ๋ก ๋๋ค ์ก์ธ์ค ํ ์ ์๋ค. ์ด ๋ถ๋ถ์ ์ถํ ํฌ์คํ
์ผ๋ก ์ ๋ฆฌํด ๋ณด๋๋ก ํ๊ฒ ๋ค.
๋ง์ ๋์์ด ๋์๊ธธ ๋ฐ๋ผ๋ฉฐ..
๋.
์ฐธ๊ณ
https://www.baeldung.com/java-inputstream-reset-file-read