반응형

Java에서 파일 복사, Object Serialization, byte 배열 문자열 변환은 입출력 코드를 작성할 때 자주 만나는 기본 작업이다. 이 글은 FileInputStream과 FileOutputStream, ObjectOutputStream, ObjectInputStream, EUC-KR 변환, 배열과 List 차이를 함께 모아 둔 예제 메모다.

예제들은 오래된 Android와 Java 코드 흐름을 기준으로 작성되어 있어, 핵심은 스트림을 열고 닫는 순서와 저장할 데이터 형식을 분명히 구분하는 데 있다.

 

핵심 정리

파일 복사는 입력 스트림에서 byte 버퍼로 읽고 출력 스트림에 쓰는 흐름으로 이해할 수 있다. Object Serialization은 객체를 ObjectOutputStream으로 파일에 저장하고 ObjectInputStream으로 다시 읽어 오는 방식이다. List 같은 객체를 그대로 저장할 수 있어 간단한 캐시에는 편하지만, 클래스 구조가 바뀌거나 호환성을 길게 가져가야 하는 데이터에는 주의가 필요하다. JNI에서 byte 배열을 받아 문자열로 바꿀 때는 원본 인코딩을 정확히 알아야 한다. 원문 예시처럼 EUC-KR이나 MS949 계열 데이터를 다룰 때는 기본 문자셋에 맡기지 말고 명시적으로 인코딩을 지정하는 편이 안전하다. 배열과 List의 차이는 크기 변경 가능 여부로 먼저 구분하면 이해하기 쉽다.

  • 파일 복사는 입력 스트림에서 읽고 출력 스트림에 쓰는 흐름으로 구성된다.
  • 버퍼를 사용하면 파일을 한 번에 모두 메모리에 올리지 않고 복사할 수 있다.
  • 스트림은 작업이 끝난 뒤 반드시 닫아야 한다.
  • ObjectOutputStream은 객체를 파일로 저장할 때 사용할 수 있다.
  • ObjectInputStream은 저장된 객체를 다시 읽어 올 때 사용한다.
  • 직렬화 파일은 클래스 변경과 호환성 문제를 고려해야 한다.
  • byte 배열을 문자열로 바꿀 때는 EUC-KR 같은 원본 인코딩을 명시한다.
  • 배열은 크기가 고정되고 List는 동적으로 크기를 바꿀 수 있다.

원문은 Java와 Android 작업 중 필요했던 짧은 코드 조각을 한곳에 모은 메모입니다. 보강문에서는 파일 복사, 객체 직렬화, 문자 인코딩, 배열과 List를 각각 어떤 상황에서 보는지 분리했습니다. 최신 코드에서는 더 간결한 API를 선택할 수도 있지만, 스트림과 직렬화의 기본 동작을 이해하는 데는 원문의 예제가 여전히 유용합니다.

자바랑 안드로이드랑 내용이 많아서 분리함 :)

file

파일복사하기

의외로 간단하지 않더라?


protected void copy(File src, File dst) throws IOException {
    InputStream in = new FileInputStream(src);
    OutputStream out = new FileOutputStream(dst);

    // Transfer bytes from in to out
    byte[] buf = new byte[4096];
    int len;
    while ((len = in.read(buf)) > 0) {
        out.write(buf, 0, len);
    }
    in.close();
    out.close();
}
copy(new File("src.xml"), new File("tar.xml"));
           

object serialization

List<String> 파일로 저장하고 빼기

저장하기



try{
    String cur_path = _activity.getFilesDir().getAbsolutePath();
    FileOutputStream fileOut = new FileOutputStream(cur_path + "/list_cache.txt");
    ObjectOutputStream out = new ObjectOutputStream(fileOut);
    out.writeObject(_strListAll);
    out.close();
    fileOut.close();
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

로딩하기


try {
    String cur_path = _activity.getFilesDir().getAbsolutePath();
    FileInputStream fileIn = new FileInputStream(cur_path + "/list_cache.txt");
    ObjectInputStream in = new ObjectInputStream(fileIn);
    _strListAll = (List<String>)in.readObject();
    in.close();;
    fileIn.close();
} catch (FileNotFoundException ee) {
    e.printStackTrace();
} catch (IOException ee) {
    e.printStackTrace();
} catch (ClassNotFoundException ee) {
    e.printStackTrace();
}

String

byte array

* euc-kr buffer를 jni를 통해서 가져온다음에 String으로 변환하는 작업을 하였다.

* 아래 코드 참조하자


    //java side
    public native byte[] SvBadukDBClientGetTitle();
    byte[] btitle = SvBadukDBClientGetTitle();
    String title;
    try {
        title = new String(btitle, "EUC-KR");
    }
    catch (UnsupportedEncodingException e){}

    //c side
    jbyteArray as_byte_array(JNIEnv* env, char* buf, int len) {
        jbyteArray array = env->NewByteArray (len);
        env->SetByteArrayRegion (array, 0, len, reinterpret_cast<jbyte*>(buf));
        return array;
    }

    //TODO: check jbytearray possible memory-leak issue
    jbyteArray Java_com_sevity_jungsuk_BadukPanView_SvBadukDBClientGetTitle
            (JNIEnv* env, jobject thiz)
    {
        g_env = env;
        g_thiz = thiz;

        char msg[4096] = {};
        SvBadukDBClient_GetTitle((char*)msg);

        //jbyteArray result = as_byte_array(env, msg, strlen(msg));
        //return result;
        jbyteArray result = as_byte_array(env, msg, strlen(msg));
        return result;
    }

인코딩

* MS949관련 여기서 확인

* 파일 인코딩 변환 코드 여기서 확인

배열

* 미리 개수를 알 때는 String[] strArr = {"맹구", "땡칠이"} 이렇게 하면되는데..

* 또는 String[] strArr = new String[10]; 이렇게 하면 되는데.. 배열은 크기가 정해져 있다.

* 자세한건 여기 참조..

* 동적으로 개수를 조정하는건 위의 방식으로 안되는듯 하다.

리스트

* 동적으로 개수를 조정하려면 리스트를 써야 한다.

* 여기를 참조하자.

* ArrayList<String> pitches = new ArrayList<String>(); 이런식으로 하거나

* List<String> pitches = new ArrayList<String>(); 이런식으로 하면된다.

* 위 둘 중에 아래가 좀 더 바람직(?) 한 것 같고 이유는 여기를 참조하자.

assert

* 어이없게도 android studio에서 assert가 잘 안먹는다.

* 일단 커서 갖다대로 변환하면 자동으로 if..throw 문으로 번역해주긴 하는데..

* 아무래도 java용 라이브러리 하나 구축해야겠다; SVASSERT 먹도록 해야할 것 같다.

반응형

+ Recent posts