18-Input&Output



입출력

입출력/ 네트워크

외부환경으로부터 데이터를 가져오는 것은 입력(Input), 프로그램에서 외부로 내보내는 일은 출력(Output)이다.

  • 자바에서 데이터는 스트림을 통해 입출력된다.
java.io.50여개 클래스

영문자, 숫자, 몇 개의 특수문자는 모든 컴퓨터마다 동일 표현, 그러나 한글 등의 다국어는 컴퓨터마다 표현 방식이 다르다. 가령 UNICODE는 모든 문자를 2바이트로 표현, 전세계어를 코딩으로 표현.

  1바이트(이미지, 동영상, 음향 등) 2바이트 (문자기반)
입력 최상위 클래스: InputStream 최상위 클래스: Reader
  메소드 : read() close () 메소드 : read() close ()
출력 최상위 클래스: OutputStream 최상위 클래스 :Writer
  메소드: write() close() 메소드: write() close
  • File - 파일과 디렉토리 정보 제공 클래스
    이 파일은 입출력 기능은 없다. (어떤 타입인지 파악, 입출력을 보조하는 기능)
    파일 시스템 = 파일과 디렉토리 정보 제공 메소드
    ex) a.txt의 바이트? 저장경로? 마지막 수정 시각? 등등의 정보 제공. 파일이 읽기 전용인지, 쓰기 전용인지, 실행 가능한지 보는 것.
    FileInputStream FileReader
    FileOutputStream FileWriter
    .txt, .gif, .jpg, .mp3 .txt

    InputStream의 하위 클래스

    • class FileInputStream
      • 입력을 하는데 파일로 입력
    • class BufferedInputStream
      • 메모리에 무언가를 저장해놨다가 가져올 때 쓰는 방법
    • class DataInputStream
      • 자바의 데이터타입으로 바꿔서 읽어오는 인풋스트림.
abstract class InputStream{
  abstract void read();
  abstract void read(byte b[]); // 여러 종류로 사용 가능 
  .
  .
  void close(){ /*읽은 게 끝난 것을 알려주는 메소드*/ } // 입력 스트림 소멸
}

class FileInputStream extends InputStream
class BufferedInputStream extends InputStream
class DataInputStream extends InputStream
  //1바이트씩 공통 입력
  
read(); // 이 메소드는 입력할 값을 질정

데이터를 읽는 과정을 끝내면 생성했던 스트림은 소멸해야한다. 이런 목적으로 정의된 메소드는 close이다.



콘솔 입출력


  • System.in: 입력 스트림

    System.in은 키보드 객체이고 java.io.InputStream은 모니터 객체이다.

    System.in.read()가 키보드, 즉 바이트를 읽는 것 (한글x, 자바 데이터 타입x)

    System.out: 출력 스트림 - java.io.PrintStream(OutputStream하위) 보여지는 것. 콘솔창 출력.

    • 콘솔로부터 데이터를 입력받을 때
    InputStream is = System.in; // 내부에 read() 메소드가 여러개 있음.
    		        byte b[] = new byte[100];
      		        
    		    	int cnt = is.read(b); //키보드로 1바이트씩 입력, 입력된 값을 b 배열에 저장 (한글은 2바이트, 나머진 1바이트) 
      
    //b를 읽어오는 방식: 
    		    	String intputString = new String(b, 0, cnt); //한글 한 문자를 2바이트씩 표현, 영문 1문자 1바이트. (b, 0, cnt) > 의 값을 0부터 cnt까지만 갖고온다. 
      		    			
    		    	System.out.println(intputString);
    //그러나 다음과 같은 과정은, byte를 사용하고 이를 String 타입으로 바꿔줘야 한다. 
    

    read()메소드는 abstract으로 정의되어 있다.

    read메소드의 반환형, 이 값을 저장하는 변수의 자료형이 int인 이유는, read 메소드는 더 이상 읽을 데이터가 없으면 -1을 반환하기 때문이다. byte형이 표현할 수 있는 데이터의 수는 00000000~11111111까지이고, 더 이상 읽을 데이터가 존재하지 않는 상황에서 반환되는 -1을 표현할 수 없다. 따라서 반환형과 반환되는 값의 저장을 위한 변수를 int형으로 선언한다.

  • System.out: 출력 스트림

  • System.err: 에러 스트림



Scanner 클래스

정수를 입력해서 취급하고싶다거나, 자바 데이터 타입을 변환해서 입출력을 하고싶을때


java.util.Scanner
  • 입출력을 편리하게 도와주는 class

    //키보드 객체를 스캐너 객체로 다시 만들어주는 방식. 
    Scanner s = new Scanner(System.in); // 키보드를 나타내는 표현을 매개변수로 입력
    // 키보드형태를 중간에 Scanner 타입으로 변환해서 읽어온다.
      
    //밑의 두 개는 한글 타입이 가능 
    sc.next() 
    sc.nextLine() 
    
  • Scanner 클래스에서 제공하는 메소드

    • boolean nextBoolean() : boolean 자료를 읽는다.

    • byte nextByte() : 한 바이트 자료를 읽는다.

    • short nextShort() : short 자료형을 읽는다.

    • int nextInt() : int 자료형을 읽는다.

    • long nextLong() : long 자료형을 읽는다.

    • float nextFloat() : float 자료형을 읽는다.

    • double nextDouble() : double 자료형을 읽는다.

    • String nextLine() : 문자열 String을 읽는다.


예제1

package io;

import java.util.Scanner;

public class ScannerTest {

	public static void main(String[] args) {
		System.out.println("정수 1 입력");
	    Scanner key = new Scanner(System.in);
	    int first = key.nextInt();
	    
	    System.out.println("정수 2 입력");
	    int second = key.nextInt();
	    System.out.println(first + second);
	    
	    key.nextLine();
	    
	    System.out.println("한글 문자열 입력");
	    String word = key.nextLine();
	    System.out.println(word);
		

	}


예제2

package io;

import java.util.ArrayList;
import java.util.Scanner;

class Employee2{
	int id;
	String name;
	double salary;
	Employee2(int id, String name, double salary){
		this.id = id;
		this.name = name;
		this.salary = salary;
	}
	 
	public String toString() {
		return id + "|" + name +  "|" + salary;
	}

	
}
public class MenuTest {

	public static void main(String[] args) {
		
	    Scanner key = new Scanner(System.in);
	    ArrayList<Employee2> list = new ArrayList<Employee2>();//제네릭 클래스 ArrayList 
	    
	    
	    Employee2 e = null;
    
	    while(true) {
			System.out.println("===다음과 같은 메뉴 이용 가능합니다===\n" + 
					"1.사원등록\n" + 
					"2.사원정보조회\n" + 
					"3.사원정보수정\n" + 
					"4.사원탈퇴\n" + 
					"5.프로그램종료");
			int menu = key.nextInt();
			
			
			if(menu == 1) {
				System.out.println("사번 입력: ");
			    int id = key.nextInt();
				System.out.println("이름 입력: ");
			    String name = key.next();
			    System.out.println("급여 입력: ");
			    double salary = key.nextDouble();
			    e = new Employee2(id, name, salary);
			    //ArrayList 정의, 등록했던 사원을 ArrayList 가장 끝에 저장
			    list.add(e);
			   
			    System.out.println(list.size() + " 명의 사원 등록을 마쳤습니다.");
				
			}else if(menu == 2) {
				System.out.println("사원정보 조회를 선택하셨습니다.");
			    for(Employee2 em : list ) {
			    	System.out.println(em);
			    }
				
			}else if(menu == 3) {
				System.out.println("사원정보 수정을 선택하셨습니다.");
				//수정 사번 입력: 100 
				//수정항목 입력: name 박수정 
				//          : salary 3000 현재 급여 + 3000
				
			}else if(menu == 4) {
				 System.out.println("사원탈퇴 조회를 선택하셨습니다.");
				 //퇴사한 사번 입력: 100
				 //100 사번; list에서 삭제 
			}else if(menu == 5) {
				 System.out.println("프로그램 종료합니다.");
				 break;
			}else {
				System.out.println("해당 기능의 메뉴는 존재하지 않습니다.");
			}
	    }

	}

}



File

입출력 기능은 없고, 입출력을 보조하는 기능을 한다.

FileInputStream FileReader
FileOutputStream FileWriter
.txt, .gif, .jpg, .mp3 .txt
  • 특징

    • File 생성
    // ( )안에 있는 파일 객체를 만들겠다. 
      
    File f1 = new File("c:/test/a/A.java"); // - 절대경로 
    File f2 = new File("c:/test/a"); // 디렉토리 - 절대경로 
    File f3 = new File("A.java"); 
    // 어떤 경로도 작성하지 않을 시, 기본으로 자바 프로젝트 내  폴더로 간주 - 상대경로 (기준 경로가 있고 그것부터 어디에 있다. 이 경우 기준 경로가 java 프로젝트에 있음.)
    File f4 = new File("."); 
    // 현재경로 = 기준경로 = 현재 디렉토리인 java project상대경로)
    File f4 = new File(".."); 
    // 현재의 상위 디렉토리 경로
      
    f1.isFile(); //true
    f2.isFile(); //false 디렉토리이기 때문에 
    

    예제 1) 파일

    import java.io.File;
    import java.io.IOException;
    import java.text.SimpleDateFormat;
      
    public class FileClassTest {
      
    	public static void main(String[] args) {
      		
    	// File f = new File(".");
    	//File f = new File("src/FileClassTest.java"); 
    	//File f = new File("C:/kdigital/workspace/day9/src/FileClassTest.java"); 
    	File f = new File(args[0]);
    	if(f.exists()) {//존재
    		 try {
    			 System.out.println("경로 =" +  f.getCanonicalPath());
    		 }catch(IOException e) {
    				 e.printStackTrace();
    		 }
    		if(f.isFile()) {//파일명이냐
    			System.out.println(" 읽기여부=" + f.canRead());
    			System.out.println(" 쓰기여부=" + f.canWrite());	
    			System.out.println(" 총길이(byte)=" + f.length());
    			System.out.println(" 최종수정시각=" + f.lastModified());//long
    			//4 자리년도 2자리월 2자리일 24시간:2자리분:2자리초
    			//SimpleDateFormat
    			SimpleDateFormat sdf = new SimpleDateFormat
    					("yyyy년도 MM월 dd일 HH:mm:ss");
    			String dateString = 
    					sdf.format(new java.util.Date(f.lastModified()) );
    			System.out.println(" 최종수정시각=" + dateString);
    		}
    		else {//디렉토리명이나
    			String[] details = f.list();//세부목록타입String->File
    			for(String one : details) {
    				File ff = new File(args[0]+"/"+one);
    				// c:/"Intel"
    				String result = "";
    				if(ff.isFile()) {
    					result = " : file";
    				}else {// 디렉토리이거나 존재하지 않거나 
    					result = " : dir";
    				}
    				System.out.println(one + " : " + ff.exists() + result);
      				
    				// 이름 ": file / dir 표시"
      				
    			}
    		}
    	}
    	else {//미존재
    		System.out.println(args[0] + " 이름의 파일이나 디렉토리는 존재하지 않습니다.");
    	}
      
    	}
      
    }
    



    • 메소드

      boolean 타입

      exsits() - boolean

      File f = new File("me.txt"); 
      exists() // 현재 기준 폴더에 me.txt가 있는지 없는지- 불리언으로 알려줌 
      

      isFile() - boolean

      isDirectory() - boolean

      ​ 파일인지 디렉토리인지 물어보는 것

      canRead() / canWrite() - boolean

      ​ 파일 입력, 파일 출력의 여부를 알아보기 위해 사용하는 메소드

      • win 파일 생성시 읽기 가능 (linux - 파일 생성시 읽기 불가능)


      int(정수) 타입

      length() : 파일의 총 바이트 수 (int 타입)

      lastModified : 수정 최종 시각 (long타입), 1/1000초 단위로 나타낸다.


      파일 저장 명, 경로 명

      getName(): 파일명

      getParent() / getXXXPath() : 파일 저장 경로명


      파일 생성 삭제

      createNewFile()

      mkdir()

      delete()



  • FileInputStream : 1바이트(이미지, 동영상, 음향 …)
  • FileReader : 2바이트(문자=텍스트)
char c = 'a' //2바이트
  • 객체 생성/ 메소드이름은 동일하다
FileInputStream fi = new FileInputStream("a.txt|a.gif|a.mp3|a.avi"); //디렉토리가 들어갈 수 없음. 파일만 가능 
//아래도 위와 같이 작동. 
File f = new File("a.txt")
FileInputStream fi = new FileInputStream(f);

fi.read(); // int 타입을 리턴, 입력 데이터는 1바이트 읽어옴 
fi.read(); // 읽을 거 없을 때, -1을 리턴 
//파일을 끝까지 읽어라
while(fi.read() == -1){
  
}
fi.close(); // 파일 입력 종료를 알려준다. 
  • 경로가 없을 시: 자바 프로젝트 경로를 상대경로로 취한다.
  • close() 메소드의 필요성 : 파일은 하나지만 그 파일을 실행하고 있는 프로그램은 여러 개일 수 있다. 사용하는 파일들간의 순서를 지키라는 것, 파일 입력 종료를 알려준다. read(), 그리고 close()
//reader도 input과 마찬가지로 파일을 읽는다. 대신 2바이트씩 읽어준다. 코딩 방법은 같고, 텍스트를 읽을 때는 reader를 사용ㅎ나다. 
FileReader fr = new FileReader("a.txt")



파일 출력

Output

FileOutputStream fo = new FileOutputStream("a.txt");

//아래는 위와 같다. 
File f= new File("a.txt");
FileOutputSTrem fo = new FileOutputStream(f);

fo.write(97); // 'a'를 출력하라 
fo.write(65); // 'A'를 출력하라 
fo.write() // 한글은 1바이트로 표현이 되지 않음. 

  fo.close(); // 출력이 끝이 났음을 알리는 메소드


Writer

FileWriter fo = new FileWriter("a.txt");

//아래는 위와 같다. 
File f= new File("a.txt");
FileWriter fo = new FileWriter(f);

fo.write(97); // 'a'를 출력하라 
fo.write(65); // 'A'를 출력하라 
fo.write(2000) // 문자 출력 가능
  
  fo.close(); // 출력이 끝이 났음을 알리는 메소드


  • 파일 네 개의 클래스 공통
    • 객체 생성시 파일명 / File 클래스 객체가 필요하다.
    • 입/출력 데이터가 1바이트인지 2바이트인지를 파악하고 그에 맞는 클래스/메소드를 사용한다.
    • 입출력이 끝나면 close() 메소드 호출이 필요하다.


파일 복사해보기

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopyTest {

	public static void main(String[] args) {
		//사용자에게 명령형 매개변수를 두 개 입력
	//a.txt b.txt  > a.txt 입력파일이 존재하지 않을 때? - 오류 생김 
	//src/FileCopyTst.java를 b.txt에 출력하라  > 입력 파일은 있는데, b.txt 출력파일이 존재하지 않을때? >b.txt 생성해줌 - 출력  
		//src/FileClassTest.java b.txt > b.txt 출력파일이 존재한다면, 기존의 파일내용은 어떻게 처리할 것인가? > 기존 내용은 삭제 후 출력 
		//src/FileClassTest.java b.txt > b.txt 출력파일이 존재한다면, true인 경우 > 기존 내용 유지
		try {
		FileInputStream fi = new FileInputStream(args[0]);
		FileOutputStream fo = new FileOutputStream(args[1], true); // 기존의 어떤 내용이 있다 하면, 삭제하지 않
		while(true) {
			int result = fi.read(); //1바이트 읽음
			fo.write(result); // int 데이터에서 1바이트 출력 
			if(result == -1) { break; }
		}
		    fi.close();
		    fo.close();
		
		}catch(IOException e) {//FileNotFoundException도 IOException의 일부, 원인 출력 
			e.printStackTrace();
		}
	}

}

  • 입력 파일이 존재하지 않고 출력을 할 경우: 오류
  • 입력된 파일이 존재하고 출력 파일이 존재하지 않을 경우: 파일 생성후 출력
  • 출력된 파일이 이미 존재한 상태에서 입력된 파일을 출력할 경우: false의 값이면 기존 내용 삭제 후 출력, true의 값이면 기존 내용 유지하며 이어서 출력


응용1)출력한 파일에 번호로 순서 매기기

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;

public class FileCopyTest3 {

	public static void main(String[] args) {
		
	    FileReader fi = null; // 선언, 변수는 try 안에서 생
	    FileWriter fo = null;
		try {
			// 라인번호 : 왼쪽 라인 시작 추가 
			// 라인번호는 int타입, 파일의 한 줄은 string  
         fi = new FileReader("src/FileCopyTest3.java");
         Scanner sc = new Scanner(fi);
         int linenum = 1;
        
         fo = new FileWriter("linecopy.txt"); 
         while(sc.hasNextLine()){// == true //다음 라인으로 읽을 게 있느냐? 즉 파일이 끝인가 아닌가? 
         String line = sc.nextLine(); // 한 줄을 읽어줌 \n이 나오면 라인이 끝났음을 판단, 그 이전 문자까지 입력한다 
         //System.out.println(linenum++ + ":" + line ); // 콘솔출력 
        
         fo.write(linenum++ + ":" + line + "\n");
         }
         
		
		}catch(IOException e) {
			e.printStackTrace();
		}finally { 
			try {
			fi.close();
			fo.close();
			}catch(IOException e) { }
			
		}
	}

}


file에서의 Scanner

표준입력데이터를 데이터 타입으로 변환해서 입력!

FileReader fr = new FileReader("a.txt");
Scanner sc = new Scanner(fr) 
  //파일 데이터를 데이터타입으로 변환, 입력 
  
 이자바 100 3.55
  sc.next();
  sc.nextInt();
  sc.nextDouble();
//반복문도 가능 



보조 스트림

스트림: 입출력 데이터의 논리적 용어, 보조스트림은 데이터를 입출력하는데 중간의 보조적인 역할을 해준다.

ex) Scanner: 자체가 파일을 갖고 있는 것이 아님. System.in, out 같은 소스 스트림과 다름.

** 그냥 Scanner를 쓰면 된다 !



종합 예제

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;

class Employee2{
	int id;
	String name;
	double salary;
	Employee2(int id, String name, double salary){
		this.id = id;
		this.name = name;
		this.salary = salary;
	}
	 
	public String toString() {
		return id + "|" + name +  "|" + salary;
	}

	
}
public class MenuTest {

	public static void main(String[] args) {
		
	    Scanner key = new Scanner(System.in);
	  //  ArrayList<Employee2> list = new ArrayList<Employee2>();
	  //  파일이나 db -- > 영구 저장
	    
	    Employee2 x = null;
	    while(true) {
			System.out.println("===다음과 같은 메뉴 이용 가능합니다===\n" + 
					"1.사원등록\n" + 
					"2.사원정보조회\n" + 
					"3.사원정보수정\n" + 
					"4.사원탈퇴\n" + 
					"5.프로그램종료");
			int menu = key.nextInt();
			
			
			if(menu == 1) {
				System.out.println("사번 입력: ");
			    int id = key.nextInt();
				System.out.println("이름 입력: ");
			    String name = key.next();
			    System.out.println("급여 입력: ");
			    double salary = key.nextDouble();
			    x = new Employee2(id, name, salary);
			    //ArrayList 정의, 등록했던 사원을 ArrayList 가장 끝에 저
		//	  list.add(x);
			 //day9/employee.txt 파일에 e객체의 id, name, salary 변수 내용을 1줄에 출
		
		try {
			FileWriter fo = new FileWriter("employee.txt", true);
			fo.write(x.toString() + "\n");
			fo.close();} 
		catch(IOException e) {	e.printStackTrace();}
		
			     
			    
			   
			 //   System.out.println(list.size() + " 명의 사원 등록을 마쳤습니다.");
				
			}else if(menu == 2) {
				System.out.println("사원정보 조회를 선택하셨습니다.");
		/*	    for(Employee2 em : list ) {
			    	System.out.println(em);
			    }
			 */	   
		        // 파일에 저장했던 employee 객체만큼 입력 
			    // 형태는 id|name|salary의 1.5배 형식으로 콘솔 출력 
		try {
			 FileReader fr = new FileReader("employee.txt");
			 Scanner sc = new Scanner(fr);
			 while(sc.hasNextLine()) {
			 String line = sc.nextLine();
			 //line의 문자열 내용을 분리(|)
			 //pattern 사용, 
			 String items[] = line.split("\\|");
			 String id = items[0];
			 String name = items[1];
			 double salary = Double.parseDouble(items[2]);
			 System.out.println(id + "|" + name + "|" + salary*1.5);
			 
			 
			 }
		sc.close();
		fr.close();
		} catch(IOException e) { e.printStackTrace();}
		
		
			}else if(menu == 3) {
				System.out.println("사원정보 수정을 선택하셨습니다.");
//수정 사번 입력: 100 
//수정항목 입력: name 박수정 으로 변경
// employee.txt 파일에서 한 라인씩 읽어서 읽은 것을 분리 (\\|) 분리된 것 중에 첫 번째 요소를 찾아서, 두번째 요소, 이름 변경 
//모든 라인을 다시 employee.txt로 재저장. 
//FileWriter fw = new FileWriter("employee.txt");// true를 주지 않는다. 재저장할거니까 기존 것은 없애도 됨
	/*	try {	
		    FileReader fr = new FileReader("employee.txt");
			Scanner sc = new Scanner(fr);
			System.out.println("사번 입력: ");
			int id = key.ntLine();
			
			 String line = sc.nextLine();
			 String items[] = line.split("\\|");
			 String id2 = items[0];
			 String name = items[1];
			 double salary = Double.parseDouble(items[2]);
			if(id == 100) { 
				String name1 = key.nextLine();
				FileWriter fo = new FileWriter("employee.txt");
				name = name1;
				fo.write(x.toString() + "\n");
				fo.close();
				
				} else { System.out.println("다시 입력"); }
			 x = new Employee2(id), name, salary);
		}catch(IOException e){ }			
*/		
			System.out.println("수정 사번 입력: ");
			String inputId = key.next();

			System.out.println("수정 항목 입력: ");
		     key.next(); // name 을 읽어오는 것 
		     String inputName = key.next();
		     
		     try {
				 FileReader fr = new FileReader("employee.txt");
				 Scanner sc = new Scanner(fr);
				 
				ArrayList<String> list = new ArrayList<String>();
				
			while(sc.hasNextLine()) {
				 String line = sc.nextLine();
				 String items[] = line.split("\\|");
				 String salary = items[2];
				 String id = items[0];
				 String name = items[1];
				 if(id.equals(inputId)){
				   name = inputName;
				 }
			
				 //System.out.println(id + "|" + name + "|" + salary);
				 list.add(id + "|" + name + "|" + salary);
				 }
			
			sc.close();
			fr.close();
			
			FileWriter fw = new FileWriter("employee.txt");
			for(String one : list) {
				fw.write(one+ "\n");
			}
			fw.close();
			} catch(IOException e) { e.printStackTrace();}
		     
		
			
			
	    
			
			
				
			}else if(menu == 4) {
				 System.out.println("사원탈퇴 조회를 선택하셨습니다.");
				 //퇴사한 사번 입력: 100
				 //100 사번; list에서 삭제 
				 //분리해서 id까지는 만들어놓고, 그 때 같을 때, list에서 삭제, 그리고 다시 저장, 출력. 
			}else if(menu == 5) {
				 System.out.println("프로그램 종료합니다.");
				 break;
			}else {
				System.out.println("해당 기능의 메뉴는 존재하지 않습니다.");
			}
	    }

	 

	}

	private static ArrayList<Employee2>[] ArrayList() {
		// TODO Auto-generated method stub
		return null;
	}

	private static int Integer(String id2) {
		// TODO Auto-generated method stub
		return 0;
	}

}