Java: Chuyển đổi chuỗi sang và đi từ ByteBuffer và các vấn đề liên quan


81

Tôi đang sử dụng Java NIO cho các kết nối socket của mình và giao thức của tôi là dựa trên văn bản, vì vậy tôi cần có thể chuyển đổi Chuỗi thành ByteBuffers trước khi ghi chúng vào SocketChannel và chuyển đổi ByteBuffers đến trở lại Strings. Hiện tại, tôi đang sử dụng mã này:

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;
}

Điều này hầu hết đều hoạt động, nhưng tôi đặt câu hỏi liệu đây có phải là cách ưa thích (hoặc đơn giản nhất) để thực hiện từng hướng chuyển đổi này hay không, hay có cách nào khác để thử không. Đôi khi, và dường như ngẫu nhiên, các lệnh gọi đến encode()decode()sẽ ném ra một java.lang.IllegalStateException: Current state = FLUSHED, new state = CODING_ENDngoại lệ hoặc tương tự, ngay cả khi tôi đang sử dụng đối tượng ByteBuffer mới mỗi khi thực hiện chuyển đổi. Tôi có cần đồng bộ hóa các phương pháp này không? Có cách nào tốt hơn để chuyển đổi giữa Strings và ByteBuffers không? Cảm ơn!


Nó sẽ giúp xem toàn bộ dấu vết ngăn xếp của ngoại lệ.
Michael Borgwardt

Câu trả lời:


53

Kiểm tra mô tả CharsetEncoderCharsetDecoderAPI - Bạn nên tuân theo một chuỗi các lệnh gọi phương thức cụ thể để tránh sự cố này. Ví dụ, cho CharsetEncoder:

  1. Đặt lại bộ mã hóa thông qua resetphương pháp này, trừ khi nó chưa được sử dụng trước đó;
  2. Gọi encodephương thức không hoặc nhiều lần, miễn là có thể có thêm đầu vào, chuyển falsecho đối số endOfInput và lấp đầy bộ đệm đầu vào và xóa bộ đệm đầu ra giữa các lần gọi;
  3. Gọi encodephương thức một lần cuối cùng, chuyển truecho đối số endOfInput; và sau đó
  4. Gọi flushphương thức để bộ mã hóa có thể chuyển bất kỳ trạng thái bên trong nào vào bộ đệm đầu ra.

Nhân tiện, đây là cách tiếp cận tương tự mà tôi đang sử dụng cho NIO mặc dù một số đồng nghiệp của tôi đang chuyển đổi trực tiếp mỗi ký tự thành một byte theo kiến ​​thức họ chỉ sử dụng ASCII, mà tôi có thể tưởng tượng là nhanh hơn.


2
Cảm ơn rất nhiều, điều đó rất hữu ích! Tôi phát hiện ra rằng tôi đã có nhiều luồng gọi các hàm chuyển đổi của mình đồng thời, mặc dù tôi đã không thiết kế nó để cho phép điều đó. Tôi đã khắc phục sự cố này bằng cách gọi charset.newEncoder (). Encode () và charset.newDecoder (). Decode () để đảm bảo rằng tôi đang sử dụng bộ mã hóa / bộ giải mã mới mỗi lần để tránh các sự cố đồng thời hoặc không cần thiết phải đồng bộ hóa trên các đối tượng đó, mà không chia sẻ dữ liệu có ý nghĩa trong trường hợp của tôi. Tôi cũng đã chạy một số thử nghiệm và không tìm thấy sự khác biệt về hiệu suất có thể đo lường khi sử dụng newEncoder () / newDecoder () mỗi lần!
ChiaByHero

3
Không vấn đề gì. Bạn có thể tránh phải tạo bộ mã hóa / giải mã mới mỗi lần nhưng vẫn đảm bảo an toàn cho chuỗi bằng cách sử dụng ThreadLocal và lười biếng tạo bộ mã hóa / giải mã chuyên dụng cho mỗi chuỗi khi cần thiết (đây là những gì tôi đã làm).
Adamski

1
Điều này có thể hoạt động? new String (bb.array (), 0, bb.array chiều dài (), "UTF-8".)
bentech

36

Trừ khi mọi thứ thay đổi, bạn tốt hơn hết

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);
}

Thông thường, buffer.hasArray () sẽ luôn đúng hoặc luôn sai tùy thuộc vào trường hợp sử dụng của bạn. Trên thực tế, trừ khi bạn thực sự muốn nó hoạt động trong bất kỳ trường hợp nào, còn không thì bạn nên tối ưu hóa nhánh mà bạn không cần.


14

Câu trả lời của Adamski là một câu trả lời hay và mô tả các bước trong hoạt động mã hóa khi sử dụng phương pháp mã hóa chung (lấy bộ đệm byte làm một trong các đầu vào)

Tuy nhiên, phương pháp được đề cập (trong cuộc thảo luận này) là một biến thể của mã hóa - mã hóa (CharBuffer in) . Đây là một phương pháp tiện lợi thực hiện toàn bộ hoạt động mã hóa . (Vui lòng xem tài liệu tham khảo java trong PS)

Theo tài liệu, phương thức này không nên được gọi nếu một hoạt động mã hóa đang diễn ra (đó là những gì đang xảy ra trong mã của ZenBlender - sử dụng bộ mã hóa / giải mã tĩnh trong môi trường đa luồng).

Cá nhân tôi thích sử dụng các phương pháp tiện lợi (thay vì các phương pháp mã hóa / giải mã tổng quát hơn) vì chúng giúp giảm bớt gánh nặng bằng cách thực hiện tất cả các bước bên dưới.

ZenBlender và Adamski đã đề xuất nhiều cách tùy chọn để thực hiện việc này một cách an toàn trong các nhận xét của họ. Liệt kê tất cả chúng ở đây:

  • Tạo một đối tượng bộ mã hóa / giải mã mới khi cần thiết cho mỗi hoạt động (không hiệu quả vì nó có thể dẫn đến một số lượng lớn đối tượng). HOẶC LÀ,
  • Sử dụng ThreadLocal để tránh tạo bộ mã hóa / giải mã mới cho mỗi hoạt động. HOẶC LÀ,
  • Đồng bộ hóa toàn bộ hoạt động mã hóa / giải mã (điều này có thể không được ưu tiên trừ khi hy sinh một số đồng thời là ổn cho chương trình của bạn)

PS

tài liệu tham khảo java:

  1. Phương thức mã hóa (tiện lợi): http://docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer%29
  2. Phương pháp mã hóa chung: http://docs.oracle.com/javase/6/docs/api/java/nio/charset/CharsetEncoder.html#encode%28java.nio.CharBuffer,%20java.nio.ByteBuffer,%20boolean% 29
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.