국비필기노트/Java

자바(Java)_ 입력과출력, 파일(File)클래스, 문자스트림, 바이트스트림,파일 Input & Output저장,

개발..너... 2022. 4. 29. 14:13

▶입력과 출력

 

입력과 출력이란 프로그램에서만 사용을 하는 것이 아니라 아주 광범위한 범위에서 일어나는 하나의 현상이다.사용자의 키보드입력, 마우스터치, 전투기 조종사의 핸들조종까지 입력을 볼 수 있으며 그로인한 결과물이 나오는 것이 하나의 출력이 될 수 있다.

 

즉, 입력은 정보가 들어오는 것이고 출력은 정보가 나가는 것이라 할 수 있다.

▶파일의 입력과 출력

 

파일의 입력과 출력을 배우는 최종의 이유는 데이터를 프로그램으로 작성을 하고 그 데이터를 파일에 저장을 하였다가 다음번 프로그램을 실행할 때 그 데이터를 가져오기 위함이다.

 

예를 들자면 회원관리이라는 프로그램을 만들경우 우린 여러 회원정보를 입력을 하고 프로그램이 종료될 때 입력한 데이터가 날라가지않게 다른 파일에 저장을 시킨다. 이는 프로그램과 파일을 연결시켜 다시 프로그램을 껐다가 켜도 예전에 작성해둔 메모리가 삭제되지 않게 하기 위함이다.

 

파일에 데이터를 저장하는 것은 비 휘발성 메모리에 저장하는 것이다.

즉, 프로그램이 끝나고도 데이터를 다시 읽을 수 있도록 파일을 저장하는 방법을 우리는 배우는 것이다.

 

▶파일(File) 클래스

 

File 클래스는 파일 또는 폴더에 대한 정보를 제공하는 클래스이다.

파일 클래스는 파일 크기, 파일 속성, 파일 이름를 제공하며 파일 생성 및 삭제 기능을 제공하며 더 나아가 디렉토리 생성, 디렉토리에 존재하는 파일 리스트를 얻어내는 기능까지 제공한다.

 

C: \photo\food.jpg 파일에 대한 객체 생성 예시
    File file = new File("C:\photo\food.jpg");
    File file = new File("C: / photo", "food.jpg");

 

File 클래스의 객체는 정보를 조회하고자 하는 파일이나 폴더의 경로에 대한 문자열을 생성자 파라미터로 전달하는 형태로 만들 수 있다. 이 때 파라미터로 전달되는 경로가 실제로 존재하지 않더라도 File객체의 생성이 가능해진다. 

 

▶경로 설정하기

 

경로(path)는 url에서 도메인과 파라미터 또는 북마크 사이에 나타나는 어떠한 디렉토리와 파일을 식별하는 어떠한 목적지, 파일을 위치하고 있는 하나의 주소체계를 path라고 한다.

 

이 경로에는 크게 2가지의 종류가 있는데

상대경로와 절대경로이다.

 

상대경로: 저와 조카는 10살차이가 난다.

절대경로: 조카는 10살입니다.

 

절대경로는 full경로를 나타내고 상대경로는 본인의 상황과 위치에 따라 경로가 달라지게된다.

 

자바는 운영체제 상관없없지만 운영체제에 따라 상황을 변경해야하는 언어를 사용한다면 운영체제간의 호환성 유지를 위하여 "" 를 사용한다.

경로 문자열을 사용할 때 윈도우 기반에서는 역슬래시(\)를 사용하지만 가급적 다른 운영체제와의 호환성을 위해서 슬래시( / )를 사용하는 것이 좋다.

 

리눅스 기준의 conf, 윈도우 기준의 hosts는 http 설정을 메인 설정파일이다. 

이 설정파일들에 접근하는 절대경로와 상대경로를 알아보자

 

절대경로

 

작업 디렉토리와 관계없이 절대적인 위치를 의미하는 경로

리눅스기준: httpd.conf
       /etc/httpd/conf/httpd.conf
윈도우예시: hosts
      C:/windows/System32/drivers/etc/hosts

 

상대경로

 

작업 디렉토리를 기준으로 상대적인 위치를 의미하는 경로

리눅스 기준: httpd.conf
     ./conf/httpd/conf
윈도우 기준: hosts
    ../drivers/etc/hosts

 

( ./ ) 가 본인이 있는 파일을 적은 것으로 우리가 언제 어떤 파일에 있을지를 몰라 ( ./ )로 표현했다. 

상대경로로 기본설정파일을 찾고싶다면 해당 부분을 현재파일위치로 바꾸면된다.

 

▶파일 및 디렉토리 생성 및 삭제 메소드

 

 

리턴타입 메소드 설명
boolean createNewFile() 새로운 파일을 생성
boolean mkdir() 새로운 디렉토리를 생성
boolean mkdirs() 경로상에 없는 모든 디렉토리를 생성
boolean delete() 파일 또는 디렉토리 삭제

 

▶경로 설정 예시

 

 

경로 설정을 하기 전에 먼저 File클래스를 만들어 본인이 어느 위치에 있는지 먼저 알려줘야한다. 

File 클래스를 import한 후, src < file < Main01.java 로 한칸씩 내려온다.

 

절대경로나 상대경로를 생성자 파라미터로 전달하는 것이며, 이클립스에서 상대경로를 사용할 경우, 현재 프로젝트가 기준이 된다.

우리는 해당경로로 f1이라는 객체를 만들었다.

이 f1이라는 객체를 가지고 여러 기능을 확인해보자.

 

 

isFile(): 전달된 경로가 파일인지 검사한다.

is Directory() : 전달된 경로가 디렉토리인지 검사한다.

 

파일인지 아닌지를 검사하는 것이기에 결과값은 boolean으로 처리된다.

파일이 존재한다면 true/ 파일이 없거나 존재하지 않는다면 무조건 false이다.

 

우리는 f1이라는 객체를 파일로 저장을 했기에 File이냐고 묻는 isFile에는 true를 Directory냐고 묻는 isDirectory에는 false가 출력되었다.

 

 

isHidden(): 숨김파일 검사

 

 

파일이 노출되지 않기 위해 이를 숨길 수 있는데 해당 경로의 파일이 숨김 형태인지를 검사한다. 

우리가 Main01파일을 만들 때 숨김설정을 하지 않았기에 이 결과값은 False가 나온다.

 

 

getAbsolutePath(): 절대경로값 추출

 

 

해당 파일의 절대경로를 출력할 수 있으며 파란블럭을 파일탐색기에 복사 붙혀넣기하면 Main01.java파일이 만들어져있는 것을 확인할 수 있다.

 

 

exists() : 파일 존재여부 검사

 

 

 

우리가 File 클래스로 만든 파일은 실제 파일이 없어도 객체가 생성이 되기에 객체 생성 후 파일이나 디렉토리가 실제로 존재하는지 확인하기 위해 사용된다.

 

exists()는 예를들어 이미지를 다운로드 받을 때 처음 파일을 다운로드 받으면 다운로드 파일이 하나 생성이 되면서 그 안에 이미지가 저장되지만, 두번째 받는다면 다운로드파일은 생성되어있으니 그 작업은 패스하고 바로 해당 파일안에 다운로드받은 이미지가 저장된다. 이럴때 사용하는 것이 exist(), 이를 통해 다운로드파일의 존재여부를 확인한 후 파일을 하나 더 생성할지 안할지를 판단한다.

 

 

mkdirs(): 경로에 따른 디렉토리 생성

 

 

우리가 가상의 a라는 폴더 밑에 b라는 폴더 밑에 c라는 폴더 밑에 target이라는 폴더를 만들었다고해보자. 

a,b,c는 실제로 존재하지 않는 폴더이나 File클래스는 실제파일이 없어도 객체가 생성이 되기에 무리없이 객체는 생성이 될 것이다. 그러나 여전히 실제파일은 존재하지 않는다.

 

이 때 mkdirs()를 사용한다면 해당 경로를 따라 실제 파일을 생성시켜준다.

 

 

a,b,c는 원래 없는 폴더였으나 mkdir로 실제 폴더가 생성되었고 exists()로 확인하면 mkdir전엔 false가 이후엔 true로 바뀌는 것도 볼 수 있을 것이다.

 

getName() : 마지막 "/" 이후 단어를 리턴

getParent() : 처음부터 마지막 "/" 직전까지 리턴

 

 

즉, getName은 마지막 폴더를, getParent는 마지막 폴더를 제외한 모든 파일을 출력해주는 것이다.

 

 

delete(): 파일 삭제

 

파일을 삭제하고 결과값을 boolean으로 출력시켜준다.

 

▶문자 인코딩

 

문자나 기호들의 집합을 컴퓨터에서 저장하거나 통신에 사용할 목적으로 부호화하는 방법

 UTF-8 : 8비트 이진수로 표현하는 방법

 

▶스트림(Stream)

 

스트림은 순서가 있는 데이터의 연속적인 흐름이라고 할 수 있다.

 

일반적인 데이터는  int i , int b 을 사용하여 즉각적으로 i와 b라는 이름을 가지는 "주소"를 가지고 원하는 데이터를 읽어오지만 파일은 "스트림"으로 관리가 된다.

 

입출력에서 Stream이란 디바이스의 입출력 방식이 character 단위이던 block단위이든 관계없이 "1 바이트"씩 "연속"적으로 첫번째 시작부터 끝날때 까지 흐름을 읽어주는 것이며 추상화된 상태를 의미한다. 

 

입출력 장치는 개별적인 특성이 있으므로 읽고, 쓰는 단위가 각각 다르지만 스트림은 이러한 일련의 과정을 추상화하여 모든 디바이스를 Character단위로만 사용하도록 한다.

 

즉, 입출력 디바이스의 특성을 무시하고 하나의 단일한 입출력 인터페이스를 다룰 수 있도록 하는 것이 stream이다. 

 

▶입출력 스트림

 

출처:&nbsp;https://www.youtube.com/watch?v=gEjEtGT9BSM

 

입출력 스트림에는 크게 입출력과 문자스트림으로 나누어있지만 관련 클래스들은 각각 InputStream(입력/읽기), OutputStream(출력/저장)과 관련된 인터페이스를 상속받기 때문에 모두 동일한 메서드를 가지고있다. 

 

입출력 스트림은 바이트 스트림이라고도 부르는데 바이트는 binary(이진데이터)로 즉, 이미지나 실행 파일들을 저장하는 것이다.

 

그러나 문자도 엄밀히 따지면 바이너리를 저장하는 것으로 크게보면 입출력과 차이가 없다. 그러나 디테일하게 들어가면 문자 스트림은 바이너리로 저장이 되어있던 데이터를 문자로 변환하여 입출력 스트림보다 한단계 더 해석함에 이 둘은 차이가 있다.

 

컴퓨터는 'A'라는 문자를 저장하는데 메모리에 A라는 문자를 그대로 저장하는 것이 아니라 A의 아스키코드값 65로 저장을 한다. 그래서 입출력 스트림이나 문자스트림이나 저장이 된 코드의 값을 보면 서로 동일하지만 출력할 때를 보면 문자 스트림은 65라는 아스키코드를 확인하고 'A'를 출력하게 된다.

 

문자스트림은 실제 저장되어있는 값은 65인데 'A'를 보여주는 것이다.

 

프로그래머는 상황에 따라서 적절한 클래스를 사용하기만 하면 동일한 방법으로 스트림을 사용할 수 있다.

 

▶바이트스트림

 

바이트 스트림은 바이트 단위로 입출력하는 클래스로 바이트 스트림클래스들은 추상클래스인 InputStream OutputStream에서 파생된다. 

바이트 스트림 클래스 이름에는 InputStream(입력)과 OutputStream(출력)이 붙는다.

 

▶문자 스트림(Character stream)

 

문자 스트림은 문자 단위로 입출력 하는 클래스이다.

이들은 모두 기본 추상 클래스인 Reader와 Write클래스에서 파생된다.

문자 스트림 클래스 이름에는 Reader(입력)과 Writer(출력)가 붙는다.

 

 

 

▶파일 저장 과정: Output(파일 내보내기)

 

 - 저장을 위한 빈 파일 생성하기
-> OutputStream의 객체를 생성한다.
   - 파일에 내용 쓰기
-> 저장할 내용을 byte배열로 변환한다.
-> 변환된 배열을 OutputStream의 write() 메서드에게 파라미터로 전달
   - 파일 닫기
-> OutputStream 객체로 close()메서드를 호출하여 스트림을 닫는다.
   - 파일 저장시에 유의사항
-> OutputStream은 각각의 단계마다 예외처리를 강제적으로 요구한다.
-> try ~ catch 블록이 형성되는 과정에서 변수의 유효성 범위에 대한
처리에 유의해야 한다.


▶파일 Output 과정 예시

 

 

설명해보자면

D드라이브 안의 test 파일안에 kjh_test.txt 파일을 생성할 것이고

그 안에는 "가나다라마바사abcdefg" 로 저장할 것이다.

그런데 이 과정에서 인코딩이라는 컴퓨터 언어로 변환되는 작업이 있어야하는데 이를 utf-8이라는 방법으로 맞춰주는 것이다. 

 

그런데 사용자가 UTF-8로 입력을 한 것인데 만약 실수로 UTF-9로 입력을 한다면?

 

이를 방지하기 위해 getBytes() 메서드는 존재하지 않는 인코딩 형식에 대한 지정을 방지하기 위하여 예외처리를 강제적으로 요구한다.

 

 

그렇게 UTF-8에대한 try catch문이 자동완성이 되었다.

 

 

PATH 역시 잘못될 수 있다. 

우리는 저장할 파일 경로를 "D: /test/kjh_test.txt"로 실제 경로를 입력하였으나 오타가 있을 수 있고 실제로 보면 실체가 없는 파일일 수도 있다. 

이를 방지하기 위해 이 역시 try catch문으로 예외를 잡아준다.

 

 

우리는 buffer라고 하는 변수에다가 "가나다라마바사abcdefg"라는 내용을 getBytes로 쪼갠 내용을 가지고 있었고 이를 out.write를 통해 다시 밖으로 꺼내올 것이다. 

그런데 try catch문도 잘 작성을 했고 정상적으로 입력을 했으나 에러가 났다. 왜 에러가 나는 것일까?

 

이유는 try catch문 안에 out.write(buffer)를 입력을 해서이다. 

try catch문은 if문과 for문과 마찬가지로 try안에 선언이 되면 지역변수가 된다. 그러므로 try 안에서만 사용을 할 수 있기 때문에 try 밖에선 buffer는 없는 변수가 된다. 

그렇기에 두번째 try문에서 사용을 할 수 없는 것이다.

 

그럼 결국 변수의 유효성때문에 나타나는 에러로 

 

 

buffer를 try 밖으로 빼내준다.

그러나 여전히 Error가 남아있는데 

파일을 쓰면서  되면서 강제로 종료가 된다던지 하는 등의 의도치않은 에러가 발생이 될 수 있기 때문에 

 

 

Write만의 try catch문을 추가로 설정해준다.

단, 여기서 해야할 것이 하나 더 있는데! OutputStream out을 사용하면 반드시 finally를 붙혀서 닫아주어야하는 것이다.

 

 

그렇게 마지막 finally를 설정하여 out.close()를 입력해주었는데 다시 에러가 난다.

 

 

inputStream하면서도 어떠한 예외가 발생이 될 수 있는것이고 close를 할 때도 어떠한 예외가 발생이 될 수 있기 때문에 out역시 finally에 대한 예외처리를 해준다.

 

▶파일 저장 과정: Input(파일 읽기)

 

   - 파일을 읽기 위한 InputStream 객체 생성하기
   - 읽은 내용을 담기 위한 빈 byte 배열 생성하기
-> 파일의 용량 크기 만큼 배열의 사이즈를 지정해야 한다.
   - 파일의 내용 읽기
-> 읽은 내용을 담기 위한 byte배열을 InputStream 객체의 read()메서드에게
   파라미터로 전달한다.
   - 사용이 완료된 스트림 닫기
-> InputStream 객체의 close() 메서드를 호출하여 스트림 닫는다.
   - 읽은 내용을 문자열로 변환하기
-> byte배열을 String 클래스의 생성자에게 전달하여 문자열로 변환한다.
-> 이 과정에서 어떤 인코딩을 사용할지를 함께 설정해야 한다.

 

▶파일 Input 과정