파일 읽고 쓰기 2

나고수·2022년 3월 29일
0

1일1공부

목록 보기
26/68
post-custom-banner

android에 파일을 저장 할 수 있는 곳은 내부저장소/외부저장소 가 있다.

  • 내부 저장소는 앱 마다 각각 제공되는 저장소이며, 앱이 삭제되면 같이 삭제된다. 다른 앱의 내부저장소에 접근할 수 없다.
  • 외부 저장소는 '공용공간/앱별공간'으로 나뉜다. 조금 복잡하므로 바로 블로그 링크를 참고

하지만 지금은 그냥 assets 폴더에 있는 csv 파일을 읽어볼 것 이다.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val am = resources.assets //resource Manager : Provides access to an application's raw asset files
        try {
            val inputStream = am.open("food_1.csv", AssetManager.ACCESS_BUFFER) 
            // accessMode를 이용해서 asset 읽기. 내용을 읽기 위해 inputStream 을 리턴 
            val br = BufferedReader((InputStreamReader(inputStream, Charset.forName("UTF-16"))))
            //리턴받은 InputStream(데이터가 전송되는 통로, 1byte 씩만 읽어옴. 한글 처리 x)의 byte를 "UTF-16"방식으로 처리 하겠다. - 한글 처리위해
            //괄호 안에서 처리한 inputstreamReader을 char 단위로 buffer을 이용해 읽겠다. - char로 처리 되어 있으므로 BufferdReader로 읽을 수 있음
            //(BufferdReader는 char 단위로 처리. BufferedInputStream은 byte 단위로처리)

            val list = br.use(BufferedReader::readLines)
            //br의 메소드인 readLines로 모든 라인을 읽어오자 
            //보다시피 한줄 한줄이 리스트 요소로 들어간다. 
            br.close()
            //stream을 닫고 memory leak 이 일어나지 않게 한다. 
        } catch (e: IOException) {
            e.printStackTrace();
        }
    }

AssetManager.open() 메소드의 accessMode

  • ACCESS_BUFFER : 빠르고 작은 읽기 (버퍼를 이용하니까 그런듯?)
  • ACCESS_RANDOM : chunk 단위로 읽기. 앞뒤를 탐색
  • ACCESS_STREAMING : 순서대로 읽기. 때때로 앞을 탐색
  • ACCESS_UNKOWN : 데이터를 어떻게 접근할지 모를 때

지금은 inputStream을 열고 , 파일을 읽고, inputStream을 닫을때 모두 하나의 try-catch 에서 실행했지만,
1. 파일을 열어 정상적으로 읽었으나 Stream을 닫을 때 에러 발생의 경우
2. 파일을 찾지 못했을 경우
3. 파일을 찾았으나 읽지 못했을 경우
4. 파일을 찾았은 읽지 못하여 fileStream을 닫으려고 했으나 닫지 못했을 경우
를 각각 try-catch 처리해줘야한다고 한다.

내/외부 저장소에 있는 파일 읽기

출처

참고 : FileReader 은 inputStreamReader을 상속받았다. 따라서, inputStreamReader처럼 char을 기준으로 읽는다.- bufferReader 이용
byte를 기준으로 읽고 싶다면, FileInputStream을 사용. - BufferedInputStream 이용

로컬(내 컴퓨터에 있는) 파일 읽기


fun main() {
    val br = BufferedReader(//char 단위로 처리.
        (InputStreamReader( //inputStream의 byte단위를 char단위로 변환시키는 중개자
            FileInputStream("파일이 있는 위치/파일이름.확장자"), //파일을 바이트 단위로 읽어서 inputStream 만듬
            Charset.forName("UTF-16")
        ))
    )

    //여기서 의문점이 생겼다.
    //FileInputStream(바이트 단위의 스트림)을 만들고 InputStreamReader로 byte >char 단위로 스트림을 변환하는
    //이중 처리를 하는 이유가 궁금했다.
    //FileReader를 이용하면 바로 char 단위로 파일을 읽는 inputStream을 만들 수 있는데 말이다.
    //하지만 직접 해보니..
    //FileReader는 인코딩을 지정할 수가 없다!(자바 11에서는 가능하다고함. 하지만 나는 1.8을 쓰고있지.)
    //그래서 굳이 이중처리를 해야함에도 불구하고,
    //FileInputStream로 바이트단위 but, 인코딩 지정가능 스트림 >> InputStreamReader로 char단위 스트림
    //이렇게 스트림을 만들어 주는 것 같다!!!

//    val br = BufferedReader((FileReader("파일위치/파일이름.확장자")))

    val list = br.use(BufferedReader::readLines)
    br.close()

    list.forEach {
        println(it)
    }
}

로컬에 파일 쓰기

private fun writeCSV() {
        try {
            val map = mapOf("키" to "맵")
            val bw =
                BufferedWriter(FileWriter(("파일위치/파일이름.확장자"), true))
            //fileWriter/BufferedWriter - char 단위로 파일 작성
            //append - true : 기존 파일 내용 냅두고 그 다음줄 부터 쓴다. false : 기존 파일 내용 날리고 덮어쓴다(새로쓴다)
            //바이트 단위로 파일을 쓰고싶다면 > FileOutputStream . BufferedOutputStream 이용
            val iterator = map.iterator()
            while (iterator.hasNext()) {
                val word = iterator.next()
                bw.write("${word.key},${word.value}")
                bw.newLine()
                bw.flush()
                //버퍼의 내용을 파일에 write 한다.
                //flush()를 호출하지 않는다면 내용이 버퍼에만 남고 파일에는 쓰이지 않는 상황이 됨.
            }
            bw.close()
            //close 호출은 스트림을 닫는 역할.
            // 스트림을 닫으면 그 스트림을 다시 이용하여 파일에 쓰는 것이 불가능
        } catch (e: Exception) {
            println(e)
        }
    }
profile
되고싶다
post-custom-banner

0개의 댓글