2016-06-13 6 views
0

나는 Java의 ByteBuffer.clear()가 실제로 ByteBuffer의 모든 데이터를 지우지 않기 때문에 항상 StringBuilder.append() 문자열을 사용할 때마다 최종 결과는 항상 나머지 모든 문자를 ByteBuffer, 마지막으로 쓰는 오래된 데이터이므로이 문제를 해결하는 방법은 무엇입니까?Java ByteBuffer 데이터 지우기

int byteRead = -1; 
int readCount = 0; 
int BUFFER_SIZE = 256; 
StringBuilder sb = new StringBuilder(); 
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE); 
ReadableByteChannel readableByteChannel = Channels.newChannel(is); 
while ((byteRead = readableByteChannel.read(buffer)) > 0 && readCount < 68) { 
    sb.append(new String(buffer.array(), "UTF-8")); 
    buffer.clear(); 
    readCount++; 
} 
+0

Eh? 모든 데이터를 지우는 것이 정확히 무엇을위한 것입니다. 문제는 전체 배열에서 문자열을 직접 작성하여 읽기 길이를 무시한다는 것입니다. – EJP

답변

4

다른 답변에 의해 이미 지적했듯이 read 메서드로 업데이트되는 버퍼의 위치를 ​​고려해야합니다. 당신의 특별한 경우에, arrayOffset()은 항상 0이됩니다,하지만 당신은 더 나은 당신이 버퍼에 무언가를 변경할 때 중단하지 않는, 방식으로 코드를 작성하는 것이

while ((byteRead = readableByteChannel.read(buffer)) > 0 && readCount < 68) { 
    sb.append(new String(buffer.array(), 
     buffer.arrayOffset(), buffer.arrayOffset()+buffer.position(), "UTF-8")); 
    buffer.clear(); 
    readCount++; 
} 

참고 : 그래서 올바른 코드는 같다 할당 코드.

하지만이 코드는 깨졌습니다. 다중 바이트 UTF-8 시퀀스를 읽으면 해당 시퀀스의 첫 번째 바이트가 한 번의 작업에서 읽히고 나머지 바이트는 다음 바이트에서 읽혀 질 수 있습니다. 이러한 불완전한 시퀀스로 인해 String 인스턴스를 만들려고하면 잘못된 문자가 생성됩니다. 그 외에도 이러한 String 인스턴스를 만들고 있는데, 그 내용을 StringBuilder에 복사하는 것은 매우 비효율적입니다.

그래서, 제대로 할, 당신은 같은 것을 수행해야합니다

int readCount = 0; 
int BUFFER_SIZE = 256; 
StringBuilder sb = new StringBuilder(); 
CharsetDecoder dec=StandardCharsets.UTF_8.newDecoder(); 
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE); 
CharBuffer cBuffer= CharBuffer.allocate(BUFFER_SIZE); 
ReadableByteChannel readableByteChannel = Channels.newChannel(is); 
while(readableByteChannel.read(buffer) > 0 && readCount < 68) { 
    buffer.flip(); 
    while(dec.decode(buffer, cBuffer, false).isOverflow()) { 
     cBuffer.flip(); 
     sb.append(cBuffer); 
     cBuffer.clear(); 
    } 
    buffer.compact(); 
    readCount++; 
} 
buffer.flip(); 
for(boolean more=true; more;) { 
    more=dec.decode(buffer, cBuffer, true).isOverflow(); 
    cBuffer.flip(); 
    sb.append(cBuffer); 
    cBuffer.clear(); 
} 

주, 방법 모두 자신의 위치와 한계를 사용하여 ReadableByteChannelCharsetDecoder 과정 버퍼. 당신이해야 할 일은 flipcompact을 정확하게 shown in the documentation of compact으로 사용하는 것입니다.

NIO 기능이 아니기 때문에 유일한 예외는 Stringbuilder에 추가됩니다. 여기서 우리는 Stringbuilder.append 연산이 버퍼의 모든 문자를 소비한다는 것을 알고 있으므로 clear()을 사용해야합니다.

이 코드는 임의의 수인 read 이후에 멈추기 때문에 여전히 (어쩔 수없는) 특정 오류 조건을 처리하지 않으므로 다중 바이트 UTF-8 시퀀스의 중간 부분을 자르면 항상 가능할 수 있습니다.


그러나 이것은 매우 복잡한 논리는 이미 JRE에 의해 구현 된 당신이 바이트의 특정 숫자 후 절단의 아이디어를 포기하는 경우, 당신이 활용할 수 : 이제

int readCount = 0; 
int BUFFER_SIZE = 256; 
StringBuilder sb = new StringBuilder(); 
CharBuffer cBuffer= CharBuffer.allocate(BUFFER_SIZE); 
ReadableByteChannel readableByteChannel = Channels.newChannel(is); 
Reader reader=Channels.newReader(readableByteChannel, "UTF-8"); 
while(reader.read(cBuffer) > 0 && readCount < 68) { 
    cBuffer.flip(); 
    sb.append(cBuffer); 
    cBuffer.clear(); 
    readCount++; 
} 

이 코드는 바이트가 아닌 256 × 68문자 인으로 제한하지만, UTF-8 인코딩 된 데이터의 경우에는 이전에 분명히 신경 쓰지 않았던 멀티 바이트 시퀀스가있을 때만 차이가납니다. 당신은 분명히 처음에 InputStream이 있기 때문에

마지막으로, 당신은 전혀 ReadableByteChannel 우회 필요가 없습니다

int readCount = 0; 
int BUFFER_SIZE = 256; 
StringBuilder sb = new StringBuilder(); 
CharBuffer cBuffer = CharBuffer.allocate(BUFFER_SIZE); 
Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8); 
while(reader.read(cBuffer) > 0 && readCount < 68) { 
    cBuffer.flip(); 
    sb.append(cBuffer); 
    cBuffer.clear(); 
    readCount++; 
} 

이 "NIO 코드가없는"처럼 보일 수 있습니다,하지만 Reader들입니다 여전히 NIO를 사용하여 문자 데이터를 읽는 표준 방식. 대체품이 없다. method Reader.read(CharBuffer)은 NIO의 첫 번째 릴리스에서는 누락되었지만 Java 5에서는 수령되었습니다.

+0

BufferReader를 사용하지 않는 이유는 무엇입니까? ReadableByteChannel과 Reader vs BufferReader의 어느 성능이 더 좋습니까? –

+0

왜'BufferedReader'를 사용합니까? 'BufferedReader'는 단일 char을 읽거나'BufferedReader' 자신의 버퍼보다 ​​작은 버퍼를 반복적으로 사용하는 경우에만 의미가 있습니다. 즉, 그것은 말이되지 않습니다. 버퍼가 너무 작 으면 더 큰 버퍼를 사용하십시오. 그리고 만약 당신이 퍼포먼스에 관심이 있다면, 당신은 단일 char read 메소드를 사용하지 않는다. 'BufferedReader'의 유일하게 남아있는 유용한 기능은 라인의 처리를 허용하는 것인데, 여기서는 무의미합니다. – Holger

+0

충분히 널리 알려지지 않은 것 같지만 충분히 큰 버퍼를 넘겨 주면'InputStream'과'Reader'의'Buffered ...'변종은 아무 것도하지 않을 것입니다. 버퍼가 자신의 버퍼보다 ​​조금 작 으면 불필요한 복사로 인해 성능이 더욱 악화 될 수 있습니다. 결과적으로,'Channel' API는 채널과 같이 버퍼링 된 채널 변형을 제공하지 않으므로 항상 자체 버퍼를 제공해야합니다. – Holger

0

사용 position()는 현재 버퍼 위치를 얻을 Arrays.copyOf와 배열의 일부를 얻을 수 있습니다 : 심지어

sb.append(new String(Arrays.copyOf(buffer.array(), 0, buffer.position()), "UTF-8")); 

이하를 사용하는 경우 : 귀하의 경우가 될 것이다

Arrays.copyOf(buffer.array(), 0, buffer.position()); 

을 적절한 문자열 생성자 :

sb.append(new String(buffer.array(), 0, buffer.position(), "UTF-8")); 

slice() : sb.append (새 문자열 (buffer.slice(). 배열(), "UTF-8"));

BTW. "UTF-8" 대신 StandardCharsets.UTF_8을 사용하는 것이 더 좋습니다.

+0

어떤 성능이 가장 좋습니까? –

+0

@AndyChan 마지막 하나 –