자바 : 문자열과 ByteBuffer 간 변환 및 관련 문제


81

소켓 연결에 Java NIO를 사용하고 있으며 프로토콜은 텍스트 기반이므로 SocketChannel에 쓰기 전에 문자열을 ByteBuffer로 변환하고 들어오는 ByteBuffer를 다시 문자열로 변환 할 수 있어야합니다. 현재 다음 코드를 사용하고 있습니다.

public static Charset charset = Charset.forName("UTF-8");
public static CharsetEncoder encoder = charset.newEncoder();
public static CharsetDecoder decoder = charset.newDecoder();

public static ByteBuffer str_to_bb(String msg){
  try{
    return encoder.encode(CharBuffer.wrap(msg));
  }catch(Exception e){e.printStackTrace();}
  return null;
}

public static String bb_to_str(ByteBuffer buffer){
  String data = "";
  try{
    int old_position = buffer.position();
    data = decoder.decode(buffer).toString();
    // reset buffer's position to its original so it is not altered:
    buffer.position(old_position);  
  }catch (Exception e){
    e.printStackTrace();
    return "";
  }
  return data;
}

이것은 대부분의 경우 작동하지만 이것이이 전환의 각 방향을 수행하는 데 선호되는 (또는 가장 간단한) 방법인지 또는 시도 할 다른 방법이 있는지 질문합니다. 때때로, 겉보기에 무작위로, 호출 encode()decode()던져 것입니다 java.lang.IllegalStateException: Current state = FLUSHED, new state = CODING_END나는 새의 ByteBuffer 객체에게 변환이 수행 될 때마다 사용하고 경우에도 예외 또는 유사한. 이러한 방법을 동기화해야합니까? 문자열과 ByteBuffer 사이를 변환하는 더 좋은 방법이 있습니까? 감사!


예외의 전체 스택 추적을 보는 데 도움이됩니다.
Michael Borgwardt

답변:


53

아웃 확인 CharsetEncoderCharsetDecoderAPI 설명 - 당신은 따라야 메소드 호출의 특정 순서를 이 문제를 방지 할 수 있습니다. 예를 들면 다음과 CharsetEncoder같습니다.

  1. reset이전에 사용하지 않은 경우 방법을 통해 인코더를 재설정하십시오 .
  2. encode추가 입력을 사용할 수있는 한 메서드를 0 번 이상 호출하고 falseendOfInput 인수를 전달 하고 입력 버퍼를 채우고 호출 사이에 출력 버퍼를 비 웁니다.
  3. encode마지막으로 메소드를 호출하고 trueendOfInput 인수를 전달하십시오. 그리고
  4. flush인코더가 내부 상태를 출력 버퍼로 플러시 할 수 있도록 메서드를 호출합니다 .

그건 그렇고, 내 동료 중 일부는 ASCII 만 사용한다는 지식으로 각 문자를 바이트로 직접 변환하지만 NIO에 사용하는 것과 동일한 접근 방식입니다.


2
대단히 감사합니다. 매우 도움이되었습니다! 나는 그것을 허용하도록 설계하지 않았지만 동시에 내 변환 함수를 호출하는 여러 스레드가 있음을 발견했습니다. charset.newEncoder (). encode () 및 charset.newDecoder (). decode ()를 호출하여 동시성 문제를 방지하기 위해 매번 새 인코더 / 디코더를 사용하거나 해당 개체에서 불필요하게 동기화해야하는지 확인하여 문제를 해결했습니다. 제 경우에는 의미있는 데이터를 공유하지 않습니다. 또한 몇 가지 테스트를 실행 한 결과 매번 newEncoder () / newDecoder () 사용시 측정 가능한 성능 차이가 발견되지 않았습니다!
DivideByHero

3
문제 없어요. 매번 새로운 인코더 / 디코더를 생성하지 않아도되지만 ThreadLocal을 사용하여 스레드 안전을 유지하고 필요에 따라 스레드 당 전용 인코더 / 디코더를 느리게 생성 할 수 있습니다.
Adamski

1
작동 할 수 있습니까? new String (bb.array (), 0, bb.array (). length, "UTF-8")
벤텍

36

상황이 바뀌지 않는 한

public static ByteBuffer str_to_bb(String msg, Charset charset){
    return ByteBuffer.wrap(msg.getBytes(charset));
}

public static String bb_to_str(ByteBuffer buffer, Charset charset){
    byte[] bytes;
    if(buffer.hasArray()) {
        bytes = buffer.array();
    } else {
        bytes = new byte[buffer.remaining()];
        buffer.get(bytes);
    }
    return new String(bytes, charset);
}

일반적으로 buffer.hasArray ()는 사용 사례에 따라 항상 true 또는 false입니다. 실제로 어떤 상황에서도 실제로 작동하기를 원하지 않는 한 필요하지 않은 브랜치를 최적화하는 것이 안전합니다.


14

Adamski의 답변은 좋은 것이며 일반 인코딩 방법을 사용할 때 인코딩 작업의 단계를 설명합니다 (입력 중 하나로 바이트 버퍼 사용).

그러나 문제의 메서드 (이 토론에서)는 encode- encode (CharBuffer in) 의 변형입니다 . 전체 인코딩 작업을 구현 하는 편리한 방법입니다. . (PS의 Java 문서 참조를 참조하십시오)

문서에 따라 인코딩 작업이 이미 진행중인 경우이 메서드를 호출하면 안됩니다. (다중 스레드 환경에서 정적 인코더 / 디코더 사용).

개인적으로 나는 커버 아래의 모든 단계를 수행하여 부담을 덜어주기 때문에 (보다 일반적인 인코딩 / 디코딩 방법보다) 편리한 방법 을 사용하는 것을 좋아합니다 .

ZenBlender와 Adamski는 이미 의견에서이를 안전하게 수행 할 수있는 여러 가지 옵션을 제안했습니다. 여기에 모두 나열 :

  • 각 작업에 필요할 때 새 인코더 / 디코더 개체를 만듭니다 (많은 개체로 이어질 수 있으므로 효율적이지 않음). 또는,
  • ThreadLocal을 사용하여 각 작업에 대해 새 인코더 / 디코더를 만들지 않도록합니다. 또는,
  • 전체 인코딩 / 디코딩 작업을 동기화합니다 (프로그램에 대해 일부 동시성을 희생하지 않는 한 선호되지 않을 수 있음)

추신

자바 문서 참조 :

  1. 인코딩 (편의) 방법 : http://docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer%29
  2. 일반 인코딩 방법 : http://docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer,%20java.nio.ByteBuffer,%20boolean% 29
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.