Tistory View
안드로이드 byte배열을 String 한글 변환(+charsetDecoder)
What should I do? 2020. 12. 30. 04:15byte[]를 String으로
byte[] => String
String s = new String(b, "UTF_8"); // b => byte[]
String => byte[]
String str = "한글";
byte[] bin = str.getBytes( "UTF-8" );
넘길/넘어온 데이터에 따라 'UTF-8' or 'EUC-KR'을 넣어주면 된다.
이건 검색하면 쉽게 찾을 수 있다.
근데... 만약에 byte[]를 String으로 변환하는 과정에서 byte[]의 크기가 무지막지하게 크다면, 위의 방법은 그래 좋은 방법이 아니다. 사실상 텍스트 데이터는 그리 크지 않기에 그냥 처리해 위와 같이 처리하면 웬만하면 다 처리할 수 있다.
다시 하지만, 큰 데이터를 안 다룰 경우가 없지 않다는 것이 문제다.
ByteStream을 String으로
파일이나 네트워크상에서 넘어오는 byte들을 한글로 변환해야 하는 경우는 다음과 같이 하면 훨씬 효율적이다.
char[] buf = new char[4096];
StringBuilder builder = new StringBuilder();
//예제에서는 file을 사용하지만, 네트워크 스트림을 사용할 수 있다.
FileInputStream in = new FileInputStream(fileName);
InputStream is = new BufferedInputStream( in );
try {
InputStreamReader reader = new InputStreamReader(is, "UTF-8");
while( true ) {
// 읽기
int len = rdr.read(buf);
if( len < 0 ) {
break;
}
// 쓰기
// 이 예제에서는 builder를 사용하지만 필요하면
// 다른 파일스트림이나 네트워크 스트림으로 보내면 된다.
builder.append(buff, 0, len);
}
is.close();
is = null;
}
catch( Exception e ) {
e.printStackTrace();
}
finally {
if( is != null ) {
try { is.close(); } catch (Exception e) {}
}
}
CharsetDecoder
이 정도에서 끝날 문제였으면, 이 글을 쓰지도 않았다. 스트림마저 쓸 수 없는 경우가 있다. 보통 Callback방식에서 이런 문제가 있는 데, 파라미터로 byte데이터는 지속적으로 넘어오고, 이 것을 맨 위의 방법인
[ String s = new String(b, "UTF_8"); ] 를 사용하게 되면 변환이 제대로 일어나지 않는다. 이 맨 위의 방식은 데이터가 온전해야 하기 때문이다.
이런 경우에 쓰는 방법이다.
넘어온 데이터를 지속적으로 onCallback함수에 전달이 되게 되고, 데이터가 온전하지 않아도 CharsetDecoder가 알아서 버퍼링을 해가며 문제 없이 변환을 시켜주는 코드이다.
// utf-8
// 디코더 byte ----------> CharBuffer
private CharsetDecoder mDecoder = Charset.forName('UTF-8').newDecoder();
private ByteBuffer mByteBuffer = ByteBuffer.allocate( 4096 );
// 데이터가 넘어올 때마다 처리하는 함수
public void onCallback( byte[] dataBuf, int dataLen, StringBuilder out ) {
int dataOffset = 0; // current offset of dataBuf
int dataRemain = dataLen; // remain size of dataBuf
try {
while( dataRemain > 0 ) {
// 입력 데이터를 mByteBuffer에 밀어 넣는다.
int copyLen = mByteBuffer.remaining() < dataRemain ? mByteBuffer.remaining() : dataRemain;
if( copyLen <= 0 ) {
throw new Exception("mByteBuffer is not compacted or cleared");
}
mByteBuffer.put( dataBuf, dataOffset, copyLen );
dataOffset += copyLen;
dataRemain -= copyLen;
mByteBuffer.flip();
while (true) {
// decode함수는 변환시킬 수 있는 최대까지 변환을 한다.
// 마지막 파라미터 false는 입력이 종료되지 않았다는 뜻이다.
// 입력에 있는 모든 데이터를 처리하면 underflow를 리턴한다.
CoderResult res = mDecoder.decode(mByteBuffer, mCharBuffer, false);
if (res.isOverflow()) {
// 출력 버퍼가 꽉 찼다. 데이터를 저장하고 출력 버퍼를 비운다.
mCharBuffer.flip();
out.append( mCharBuffer.array(), 0, mCharBuffer.length() );
mCharBuffer.clear();
// 버퍼를 비웠으니 다시 디코드하게 한다.
continue;
} else if (res.isUnderflow()) {
// 인풋에 들어있는 데이터로는 변환작업이 끝났다.
작업을 끝낸다.
} else if (res.isMalformed()) {
Log.e(TAG, "isMalformed");
} else if (res.isUnmappable()) {
Log.e(TAG, "isUnmappable");
}
break;
}
mByteBuffer.compact();
}
}
catch( Exception e ) {
e.printStackTrace();
}
}
// decoder안에는 아직 출력하지 못한 데이터가 존재할 수 있다.
// 남은 자투리 데이터를 가지고 온다.
public void onFlush( String out ) {
// mByteBuffer is write-mode so switch to read-mode
mByteBuffer.flip();
while( true ) {
// 마지막 파라미터 true가 마지막 입력 데이터임을 나타낸다.
// 성공시 underflow가 리턴된다.
CoderResult res = mDecoder.decode( mByteBuffer, mCharBuffer, true );
if (res.isOverflow()) {
mCharBuffer.flip();
out.append(mCharBuffer.array(), 0, mCharBuffer.length() );
mCharBuffer.clear();
continue;
}
else if( res.isUnderflow() ) {
mCharBuffer.flip();
out.append(mCharBuffer.array(), 0, mCharBuffer.length() );
mCharBuffer.clear();
mDecoder.flush( mCharBuffer );
mCharBuffer.flip();
builder.append(mCharBuffer.array(), 0, mCharBuffer.length() );
}
else if (res.isMalformed()) {
Log.e( TAG, "isMalformed");
} else if (res.isUnmappable()) {
Log.e( TAG, "isUnmappable");
} else {
Log.d( TAG, "waht?" );
}
break;
}
}
만드는 데 꽤 시간이 들었다.. 머리를 좀 쓰느라..
위의 코드를 그대로 복사해서 지속적으로 byte[]로 넘겨주면 된다.
사용법
StringBuilder out = new StringBuilder();
byte[] buf = new byte[128];
InputStream in = null; // [your_file or network stream]
BufferedInputStream is = new BufferedInputStream( in );
try {
while (true) {
int nread = is.read(buf);
if( nread < 0 ) {
break;
}
onCallback( buf, nread, out );
}
onFlush( out );
}
catch( IOException e ) {
e.printStackTrace();
}
finally {
if( is != null ) {
try { is.close(); } catch (Exception e ) {}
}
}
Log.d( TAG, out.toString() );
위에서 사용된 ByteBuffer 동작방식이 궁금하면 다음의 링크를 이용하라.
위 소스코드 다운로드
Reference
'Android Develop > helper' 카테고리의 다른 글
안드로이드 Native에서 Choreographer 사용하기 (0) | 2021.04.07 |
---|---|
안드로이드 Choreographer 사용하기 (1) | 2021.03.31 |
Buffer(ByteBuffer, CharBuffer...) flip, compact, clear사용법 (0) | 2020.12.30 |
안드로이드 HttpUrlConnection POST 전송 #2 (3) | 2020.12.25 |
안드로이드 HttpUrlConnection POST 전송 #1 (0) | 2020.12.24 |
- Total
- Today
- Yesterday
- 텍스처
- ComputeShader
- 공유 컨텍스트
- 에어컨
- 애드핏
- 아끼는 법
- 예금
- OpenGL ES
- 전기요금
- 에어콘
- Android
- texture
- 경제보복
- 티스토리
- 애드센스
- 적금
- 전기료
- gpgpu
- 전기세
- 재테크
- 재태크
- OpenGLes
- 금리
- 컴퓨트쉐이더
- 안드로이드
- 사용료
- 컴퓨트셰이더
- TTS
- choreographer
- 블로그
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |