일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 프로그래머스
- 자바의정석
- 객체지향
- 멀티태스킹
- contiune
- 입출력
- 쿠키
- break 사용법
- hackerrank
- 리눅스
- continue 사용법
- 캡슐화
- 멀티프로세싱
- spring security
- 붕대 감기
- 오블완
- 프로그래머스 붕대 감기
- 티스토리챌린지
- 중첩 break
- spring security 설정
- CPU
- 혼공얄코
- 붕대 감기 자바
- 다형성
- 오버라이딩
- 자바의 정석
- java
- over()
- SQL Mapper
- 오버로딩
- Today
- Total
쉽게 쉽게
입출력I/O 본문
이 글은 '자바의 정석'의 내용을 기반으로 공부한 내용을 덧붙인 글입니다.
1. 입출력(I/O)이란?
I/O는 Input과 Output의 약자로 입력과 출력을 의미한다.
즉 컴퓨터 내부 또는 외부의 장치와 프로그램 간의 데이터를 주고받는 것을 말한다.
입출력의 예를 들면 키보드로 데이터를 입력받거나, System.out.println()을 이용해서 출력하는 것이 있다.
데이터를 주고 받으려면 두 대상을 연결하고 데이터를 전송할 수 있는 무언가가 필요한데 이것을 스트림(stream)
이라 한다.(데이터를 다뤘던 스트림과 이름만 같음)
입출력에서 스트림은 데이터를 운반하는 데 사용되는 연결통로다.
입력과 출력을 수행하려면 *입력 스트림과 출력스트림이 필요하다.
입력 스트림 : 대상으로부터 자료를 읽어들이는 스트림
출력 스트림 : 대상으로 자료를 출력하는 스트림
스트림은 먼저 보낸 데이터를 먼저 받게 되어 있으며 중간에 건너뜀 없이 연속적으로 데이터를 주고받는다.
이는 큐(queue)와 같은 FIFO(First In First Out)구조로 되어 있다고 생각하면 된다.
2. 바이트기반 스트림
InputStream
과 OutputStream
은 모든 바이트기반의 스트림의 조상이며 추상 클래스이다.
이를 입출력 대상에 맞게 구현한 클래스의 종류는 아래와 같다.
InputStream
과 OutputStream
에는 다음과 같은 메서드가 선언되어 있다.
InputStream의 메서드
abstract int read( )
스트림 데이터 1byte를 읽어와서 바이트값으로 반환한다.
반환값은 0~255의 아스키코드값(순수데이터인 실질적인 데이터)이기 때문에 문자로 나타내려면 char로 캐스팅해야한다.
더 이상 읽을 수 없을 때는 -1을 반환한다.
int read(byte b[])
byte[] b 만큼의 데이터를 읽어서 b에 저장하고 읽은 바이트 수를 반환한다.
int read(byte[] b, int off, int len)
최대 len개의 byte를 읽어서 배열 b의 지정된 위치(off)부터 저장한다. 실제로 읽어올 수 있는 데이터가 len개보다 적을 수 있다.
int available( )
읽을 수 있는 바이트 수를 반환한다.
해당 스트림에서 지연없이 읽어 들일 수 있는 바이트의 수를 반환한다.
long skip(long n)
InputStream에서 n 바이트 만큼 데이터를 건너뛰고 바이트 수를 반환한다.
void close( )
입력 소스를 닫음으로써 사용하고 있던 자원을 반환한다.
void mark(int readlimit)
현재 위치를 표시한다.
후에 reset()에 의해서 표시해 놓은 위치로 다시 돌아갈 수 있다.
readlimit은 되돌아 갈 수 있는 byte의 수를 의미한다.
boolean markSupported()
해당 InputStream에서 mark()로 지정된 지점이 있는지 여부를 체크한다.
void reset()
mark() 를 마지막으로 호출한 위치로 이동한다.
OutputStream의 메서드
abstract void write(int b)
주어진 값을 출력소스에 쓴다.
void write(byte[] b)
주어진 배열 b에 저장된 모든 내용을 출력소스에 쓴다.
void write(byte[] b , int off, int len)
주어진 배열 b에 저장된 내용 중 off번째부터 len 만큼을 읽어서 출력소스에 쓴다.
void close( )
입력 소스를 닫음으로써 사용하고 있던 자원을 반환한다.
void flush()
스트림의 버퍼에 있는 모든 내용을 출력소스에 쓴다.
flush()
는 버퍼가 있는 출력스트림의 경우에만 사용된다.
이때 프로그램이 종료될 때, 사용하고 닫지 않은 스트림을 JVM이 닫아주기는 하지만 꼭 close()를 호출해서 반드시 닫아주는 것이 좋다.
단 ByteArrayInputStream
과 같이 메모리를 사용하는 스트림과 System.in
, System.out
과 같은 표준 입츌력 스트림은 닫아주지 않아도 된다.
왜냐하면 바이트배열은 사용하는 자원이 메모리 밖에 없으므로 가비지컬렉터에 의해 자동적으로 자원을 반환하기 때문이다.
구현예제
스트림의 종류가 달라도 읽고 쓰는 방법은 동일하다.
예제1
InputStream in = System.in;
OutputStream out = System.out;
int idata = in.read(); // input 은 read 와 연결되어있기 때문에 in.read 를 사욯한다.
out.write(idata); // output 은 write 와 연결되어있기 때문에 out.write 를 사용한다
out.close(); // output 을 끝내는 매서드
예제2
inSrc를 outSrc로 복사
read()와 write()사용
byte[] inSrc = {0,1,2,3,4,5};
byte[] outSrc = null;
ByteArrayInputStream input = null;
ByteArrayOutputStream output = null;
input = new ByteArrayInputStream(inSrc);
output = new ByteArrayOutputStream();
int data = 0;
while((data = input.read()) != -1){
output.write(data);
}
outSrc = output.toByteArray();
System.out.println(Arrays.toString(inSrc)); //[0, 1, 2, 3, 4, 5]
System.out.println(Arrays.toString(outSrc));//[0, 1, 2, 3, 4, 5]
예제3
read()는 한번에 1 byte만 읽고 쓰므로 배열을 사용해 효율적으로 작업이 이뤄지도록
byte[] inSrc = {0,1,2,3,4,5};
byte[] outSrc = null;
byte[] temp = new byte[6];
ByteArrayInputStream input = null;
ByteArrayOutputStream output = null;
input = new ByteArrayInputStream(inSrc);
output = new ByteArrayOutputStream();
input.read(temp, 0 , temp.length); // 읽어온 데이터를 배열 temp에 담는다.
output.write(temp, 2 , 2); //temp[2]부터 2개의 데이터를 출력
outSrc = output.toByteArray();
System.out.println(Arrays.toString(inSrc)) ; //[0, 1, 2, 3, 4, 5]
System.out.println(Arrays.toString(temp));//[0, 1, 2, 3, 4, 5]
System.out.println(Arrays.toString(outSrc));//[2, 3]
3. 문자기반 스트림
앞서 InputStream
과 OutputStream
은 바이트기반 스트림이기 때문에 단 하나의 값밖에 입력받지 못하고, 따라서 출력도 하나밖에 못한다.
여러 개의 값을 출력하기 위해서 문자기반 스트림인 InputStreamReader
과 OutputStreamWriter
을 사용한다.
바이트기반 스트림과 문자기반 스트림은 사용하는 방법은 거의 같다.
바이트기반 스트림의 조상이 InputStream
과 OutputStream
것처럼 문자기반의 스트림에서는 Reader
와 Writer
가 같은 역할을 한다.
Reader
와 Writer
의 메서드는 byte배열 대신 char배열을 사용한다는 것을 제외하면 이전의 것과 거의 동일하다.
Reader의 메서드
int read()
입력소스로부터 하나의 문자를 읽어온다.
char의 범위인 0~65535범위의 정수를 반환한다.
더 이상 읽을 수 없을 때는 -1을 반환한다.
int read(char[] c)
배열 c 만큼의 데이터를 읽어서 c에 저장한다.
abstract int read(char[] c, int off, int len)
최대 len개의 문자를 읽어서 배열 c의 지정된 위치(off)부터 읽은만큼 저장한다.
읽어 온 데이터의 개수 또는 -1을 반환한다.
long skip(long n)
현재 위치에서 주어진 문자 수(n)만큼을 건너뛴다.
void close( )
입력 스트림을 닫음으로써 사용하고 있던 자원을 반환한다.
void mark(int readlimit)
현재 위치를 표시한다.
후에 reset()에 의해서 표시해 놓은 위치로 다시 돌아갈 수 있다.
readlimit은 되돌아 갈 수 있는 byte의 수를 의미한다.
boolean markSupported()
mark()와 reset()을 지원하는지를 알려준다.
void reset()
입력소스에서의 위치를 마지막으로 mark()가 호출되었던 위치로 되돌린다.
Writer의 메서드
Writer append(char c)
지정된 문자를 출력소스에 출력한다.
Writer append(CharSequence c)
지정된 문자열을 출력소스에 출력한다.
Writer append(CharSequence c, int start, int end)
지정된 문자열의 일부를 출력소스에 출력
void write(int b)
주어진 값을 출력소스에 쓴다.
void write(char[] c)
주어진 배열 c에 저장된 모든 내용을 출력소스에 쓴다.
abstract void write(char[] c , int off, int len)
주어진 배열 c에 저장된 내용 중 off번째부터 len만큼을 읽어서 출력소스에 쓴다.
void write(String str)
주어진 문자열(str)을 출력소스에 쓴다.
void write(String str, int off, int len)
주어진 문자열(str)의 일부를 출력소스에 쓴다.
(off번째 문자부터 len개 만큼의 문자열)
void close( )
입력 소스를 닫음으로써 사용하고 있던 자원을 반환한다.
void flush()
스트림의 버퍼에 있는 모든 내용을 출력소스에 쓴다.
구현예제
InputStream in = System.in;
InputStreamReader reader = new InputStreamReader(in); // InputStreamReader 사용하기 위해 객체 생성
OutputStream out = System.out;
OutputStreamWriter writer = new OutputStreamWriter(out); // OutputStreamWriter 사용하기 위해 객체 생성
char cdata[] = new char[2]; // 이제는 char 를 기본형으로 받을 수 있고, 2개 이상의 값을 배열을 통해 받아올수 있다.
reader.read(cdata);
int IcData = cdata[0]-'0'; // 배열이기 때문에 char 로 받은 값을 int로 변환하여 계산하고 싶은 경우 이처럼 사용해야한다.
writer.write("입력받은 값 : ");
writer.write(cdata);
writer.write("\n");
writer.write("입력받은 첫번째 값 + 10 : ");
writer.write(IcData+10+"\n"); // 입력받은 첫번째 값 +10
System.out.println("#######결과#######");
writer.close();
하지만 여기서 char [2]처럼
고정으로 설정하면 입력한 값이 몇 개든 2개만 출력하게 된다.
때문에 Buffer를 사용하여 효율을 높일 수 있다.
이를 활용한 것이 BufferedReader
와 BufferedWriter
이다.
3. 문자기반의 보조스트림(BufferedReader와 BufferedWriter)
BufferedReader와 BufferedWriter는 버퍼를 이용해서 입출력의 효율을 높일 수 있도록 해준다.
버퍼란?
버퍼는 입력받은 값을 버퍼에 저장해두었다가 버퍼가 가득 차거나 개행 문자가 나타나면 버퍼의 내용을 한 번에 전송하게 된다.
컴퓨터에 입력받는 값이 많으면 많을수록 Buffer를 사용하여 데이터를 입출력하는 게 Scanner를 통해 하나하나 출력하는 것보다 훨씬 빠르다.
BufferedReader의 자주 사용하는 메서드
readLine() : Reads a line of text.
String 타입으로 한 줄을 읽어온다
BufferedWriter의 자주 사용하는 메서드
flush() : Flushes the stream.
버퍼에 남은 값 출력 && 버퍼 초기화(비우기)
newLine() : Writes a line separator.
버퍼에서 사용하는 개행 메소드(줄바꿈)
구현예제
예제 1
InputStream in = System.in;
InputStreamReader reader = new InputStreamReader(in);
OutputStream out = System.out;
OutputStreamWriter writer = new OutputStreamWriter(out);
// 위의 4줄이 아래의 하나의 줄로 줄어든다.
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
}
예제2
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
String s = br.readLine(); // bufferedwriter 의 기본형은 String
int i = Integer.parseInt(s) +10; // String 을 int로 형변환 후 +10 !!
br.close(); // bufferedreader 도 입력을 마쳤다면 닫아주자
bw.write("입력받은 값 : "+ s); // 출력
bw.newLine(); // 개행 메소드
bw.write("입력받은 값 +10 : "+i+"\n"); // 이렇게 하니까 제대로 출력됨
bw.flush(); // 남은 값 출력 && 버퍼 초기화
bw.close(); // bufferedwriter 닫기
Java 입출력(I/O), 스트림(Stream), 버퍼(Buffer) 개념 및 사용법
4. File
파일은 기본적이면서도 가장 많이 사용하는 입출력 대상이다.
자바에서는 File클래스를 통해서 파일과 디렉토리를 다룰 수 있도록 한다.
(1) File의 생성자와 경로에 관한 메서드
File(String fileName)
주어진 문자열(fileName)을 이름으로 갖는 파일을 위한 File인스턴스를 생성한다.
File(String pathName, String fileName)
File(File pathName, String fileName)
파일의 경로와 이름을 따로 분리해서 지정할 수 있도록 한 생성자
File(URI uri)
지정된 uri로 파일을 생성
String getPath()
파일 또는 디렉토리의 *상대 경로 추출 (생성자로 준 경로 반환)
즉 파일의 경로를 String으로 반환한다.
String getAbsolutePath()
파일 또는 디렉토리의 *절대 경로 추출 (생성자 관계없이 절대 경로 반환)
즉 파일의 절대경로를 String으로 반환한다.
File getAbsoluteFile()
절대 경로를 가지는 File 객체 생성
String getParent()
현재 파일의 상위 경로 추출 (생성자에서 제공했을 경우)
즉 파일의 조상 디렉토리를 String으로 반환한다.
File getParentFile()
현재 파일의 상위 경로를 가진 File 객체 생성 (생성자에서 제공했을 경우)
즉 파일의 조상 디렉토리를 File로 반환한다.
구현예제
File f = new File("c:\\jdk1.8\\work\\ch15\\FileEx1.java");
String fileName = f.getName();
int pos = fileName.lastIndexOf(".");
System.out.println("경로를 제외한 파일이름" + fileName);
System.out.println("확장자 제외한 파일이름" + fileName.substring(0, pos));
System.out.println("확장자" + fileName.substring(pos+1));
System.out.println("경로를 포함한 파일이름(상대경로)" + f.getPath());
System.out.println("파일의 절대경로"+ f.getAbsolutePath());
System.out.println("파일이 속해있는 디렉토리"+ f.getParent());
//
경로를 제외한 파일이름FileEx1.java
확장자 제외한 파일이름FileEx1
확장자java
경로를 포함한 파일이름(상대경로)c:\jdk1.8\work\ch15\FileEx1.java
파일의 절대경로c:\jdk1.8\work\ch15\FileEx1.java
파일이 속해있는 디렉토리c:\jdk1.8\work\ch15
절대경로는 최상위 디렉토리부터 해당 파일까지 경유한 모든 경로를 전부 기입하는 방식이다.
상대경로는 현재 파일이 존재하는 디렉터리를 기준으로 해당 파일까지의 위치를 작성한 경로이다.
이동하는 방법은 아래의 것을 이용한다.
./ 현재 위치
../ 현재 기준, 상위 디렉토리 위치
상대경로 예제
c:\jdk1.8\work\ch15\FileEx1 // 현재 경로가 이렇고 내 위치가 `FileEx1`에 있다면
../는 ch15 디렉토리 공간으로 이동, ../../면 work디렉토리 공간으로 이동이 된다.
(같은 층에 있는 디렉토리 공간으로 이동한다고 생각하면 된다.)
FileEx1에서 ch14/FileEx2 경로를 불러온다면 ../ch14/FileEx2로 불러오면 된다.
(2) File의 메서드
boolean exists()
파일이 실제 존재하는지 판단
boolean isDirectory()
디렉토리인지 판단
boolean isFile()
파일인지 판단
boolean canRead()
파일이 읽기 가능한지 판단
boolean canWrite()
파일이 쓰기 가능한지 판단
boolean canExecute()
파일이 실행 가능한지 판단
boolean isHidden()
파일이 숨김파일인지 판단
int length()
파일의 길이(byte) 반환
boolean renameTo(File dest)
경로가 같으면 이름 변경, 경로가 다르면 이름 바뀌면서 해당 경로로 이동됨
boolean delete()
파일 삭제
boolean mkdir()
생성자에 넣은 경로에 맞게 폴더 생성
boolean createNewFile()
생성자에 넣은 경로 및 파일명에 맞게 파일 생성
boolean setReadable(treu/false)
읽기 권한 설정
boolean setWritable(true/false)
쓰기 권한 설정
boolean setExecutable(true/false)
실행 권한 설정
String[] list()
현재 경로의 파일 또는 디렉토리 목록 추출
File[] listFiles()
현재 경로의 파일 또는 디렉토리를 가지는 File 타입 배열 추출
구현예제
args에 파일이 정의되있다는 가정하에
public class o {
static int totalFiles = 0;
static int totalDirs = 0;
public static void main(String[] args){
if(args.length !=1){
System.out.println("USAGE: java o DIRECTORY");
System.exit(0);
}
File dir = new File(args[0]);
if(!dir.exists() || !dir.isDirectory()){
System.out.println("유효하지 않은 디렉토리");
System.exit(0);
}
printFileList(dir);
System.out.println();
System.out.println("총" + totalFiles + "개의 파일");
System.out.println("총" + totalDirs +"개의 디렉토리");
}
public static void printFileList(File dir){
System.out.println(dir.getAbsolutePath()+ "디렉토리");
File[] files = dir.listFiles();
ArrayList subDir = new ArrayList<>();
for(int i = 0; i< files.length; i++){
String filename = files[i].getName();
if(files[i].isDirectory()){
filename = "[" +filename + "]";
subDir.add(i+"");
System.out.println(filename);
}
int dirNum = subDir.size();
int fileNum = files.length - dirNum;
totalFiles += fileNum;
totalDirs += dirNum;
System.out.println(fileNum + "개의 파일" + dirNum +"개의 디렉토리");
System.out.println();
for(int i =0; i< subDir.size(); i++){
int index = Integer.parseInt((String)subDir.get(i));
printFileList(files[index]);
}
}
}
}
파일 메서드는 이정도 있으며, 추후에 파일 업로드와 다운로드 기능을 구현할 예정이다.
잘못된 내용이 있다면 지적부탁드립니다. 방문해주셔서 감사합니다. |
'개발공부 > Java' 카테고리의 다른 글
[Java] FILE 업로드(다중) (0) | 2024.04.28 |
---|---|
Map에 있는 값 형변환하기 (1) | 2023.11.19 |
스트림 (0) | 2023.03.31 |
람다식 (0) | 2023.03.29 |
쓰레드 (1) | 2023.03.28 |