2012-08-07 15 views
7

Tôi cần chuyển đổi nội dung của InputStream thành chuỗi. Khó khăn ở đây là mã hóa đầu vào, cụ thể là Latin-1. Tôi đã thử một số phương pháp tiếp cận và đoạn mã với String, getBytes, char [], vv để có được mã hóa thẳng, nhưng không có gì dường như làm việc.Chuyển nội dung Latin-1 của InputStream thành UTF-8 Chuỗi

Cuối cùng, tôi đã đưa ra giải pháp làm việc bên dưới. Tuy nhiên, mã này có vẻ hơi dài dòng với tôi, ngay cả đối với Java. Vì vậy, câu hỏi ở đây là:

Có cách tiếp cận đơn giản và thanh lịch hơn để đạt được những gì được thực hiện ở đây không?

private String convertStreamToStringLatin1(java.io.InputStream is) 
     throws IOException { 

    String text = ""; 

    // setup readers with Latin-1 (ISO 8859-1) encoding 
    BufferedReader i = new BufferedReader(new InputStreamReader(is, "8859_1")); 

    int numBytes; 
    CharBuffer buf = CharBuffer.allocate(512); 
    while ((numBytes = i.read(buf)) != -1) { 
     text += String.copyValueOf(buf.array(), 0, numBytes); 
     buf.clear(); 
    } 

    return text; 
} 

Trả lời

7

Thứ nhất, một vài lời chỉ trích về cách tiếp cận bạn đã thực hiện. Bạn không nên sử dụng NIO CharBuffer khi bạn chỉ muốn một char[512]. Bạn không cần phải clear bộ đệm mỗi lần lặp lại.

int numBytes; 
final char[] buf = new char[512]; 
while ((numBytes = i.read(buf)) != -1) { 
    text += String.copyValueOf(buf, 0, numBytes); 
} 

Bạn cũng nên biết rằng chỉ constructing a String với những lập luận sẽ có tác dụng tương tự, như các nhà xây dựng quá sao chép dữ liệu.

Nội dung của bản sao con được sao chép; sửa đổi tiếp theo của mảng ký tự không ảnh hưởng đến chuỗi mới được tạo ra.


Bạn có thể sử dụng một động ByteArrayOutputStream mọc một bộ đệm bên trong để chứa tất cả các dữ liệu. Sau đó, bạn có thể sử dụng toàn bộ byte[] từ toByteArray để giải mã thành một String.

Lợi thế là trì hoãn giải mã cho đến khi kết thúc tránh các đoạn mã giải mã riêng lẻ; trong khi đó có thể hoạt động cho các bộ ký tự đơn giản như ASCII hoặc ISO-8859-1, nó sẽ không hoạt động trên các sơ đồ nhiều byte như UTF-8 và UTF-16. Điều này có nghĩa là dễ dàng hơn để thay đổi mã hóa ký tự trong tương lai, vì mã không yêu cầu phải sửa đổi.

private static final String DEFAULT_ENCODING = "ISO-8859-1"; 

public static final String convert(final InputStream in) throws IOException { 
    return convert(in, DEFAULT_ENCODING); 
} 

public static final String convert(final InputStream in, final String encoding) throws IOException { 
    final ByteArrayOutputStream out = new ByteArrayOutputStream(); 
    final byte[] buf = new byte[2048]; 
    int rd; 
    while ((rd = in.read(buf, 0, 2048) >= 0) { 
    out.write(buf, 0, rd); 
    } 
    return new String(out.toByteArray(), 0, encoding); 
} 
+0

Cảm ơn bạn đã bình luận quan trọng. Giải pháp đầu tiên của bạn giống như những gì tôi đang tìm kiếm. Tuy nhiên, tôi có thể thấy điểm của bạn với giải pháp thứ hai của bạn mà rất nhiều địa chỉ các trường hợp chung. Tôi đoán đây cũng là lý do tại sao kích thước bộ đệm là 2048 byte trong ví dụ của bạn? – cyroxx

+0

Bộ đệm 2048 byte chỉ là sở thích cá nhân; bạn có thể sử dụng bất cứ điều gì cung cấp một sự cân bằng hợp lý cho thời gian chạy và mức tiêu thụ bộ nhớ. – oldrinb

1

Tôi không thấy cách đơn giản hơn nhiều. Tôi đã làm điều này một chút khác nhau một lần .. nếu bạn đã có một String, bạn có thể làm điều này:

new String(originalString.getBytes(), "ISO-8859-1"); 

Vì vậy, một cái gì đó như thế này cũng có thể làm việc:

BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 
StringBuilder sb = new StringBuilder(); 
String line = null; 
while ((line = reader.readLine()) != null) { 
    sb.append(line + "\n"); 
} 
is.close(); 
return new String(sb.toString().getBytes(), "ISO-8859-1"); 

EDIT: Tôi cần thêm, này thực sự chỉ là một giải pháp thay thế cho giải pháp đã làm việc của bạn. Khi nói đến việc chuyển đổi các luồng trong Java, nó sẽ không đơn giản hơn nhiều, vì vậy hãy tìm nó. :)

+0

Có rất nhiều cải tiến ở đây. Thứ nhất, điều này sẽ không tạo ra các văn bản chính xác trong trường hợp không có terminator dòng được tìm thấy bởi 'reader.readLine'; nó sẽ nối thêm một dấu «\ n' không có ở đó. Ngoài ra, 'BufferedReader' sẽ tự động sử dụng mã hóa hệ thống mặc định. Bạn nên xây dựng ['InputStreamReader'] (http://goo.gl/mhzP1) bằng cách sử dụng' StandardCharsets.ISO_8859_1', vì vậy bạn chỉ có thể sử dụng 'StringBuilder.toString' trong một bước để có được chuỗi được giải mã chính xác. – oldrinb

+1

Giới thiệu về \ n: Tôi có sự cải thiện nhờ đó, tôi đã không thực sự chú ý đến việc chuyển đổi InputStream-> String, nó chỉ là để hoàn thành ví dụ. Cách xử lý mã hóa khác nhau vẫn là imho ok, có nhiều cách khác nhau ở Rome. ;-) Nhưng như tôi đã nói nó chỉ là một sự thay thế. Bất kỳ tiện ích như commonsIO làm sạch mã, làm cơ bản giống nhau mặc dù và phụ thuộc vào một thư viện bổ sung. Làm cho tinh thần nếu bạn sử dụng nó thường xuyên hơn .. một vấn đề của sự lựa chọn cá nhân. – Blacklight

0

Nếu bạn không muốn plumb nó cho mình bạn có thể có một cái nhìn tại commons apache io dự án, IOUtils.toString(InputStream input, String encoding) mà dường như để làm những gì bạn muốn. Tôi đã không thử phương pháp đó bản thân mình nhưng java doc tiểu bang "Lấy nội dung của một InputStream như một String bằng cách sử dụng mã hóa ký tự được chỉ định."

0

Guava Gói IO thực sự tốt đẹp theo cách này.

Files.toString(yourFile, CharSets.ISO_8859_1) 

hoặc từ một dòng

new String(ByteStreams.toByteArray(stream), CharSets.ISO_8859_1) 
0

Tôi chỉ phát hiện ra rằng this answer cho câu hỏi Read/convert an InputStream to a String có thể được áp dụng cho vấn đề của tôi, xin vui lòng xem mã dưới đây. Dù sao, tôi rất nhiều đánh giá cao những câu trả lời bạn đã đưa ra cho đến nay.

private String convertStreamToString(InputStream is, String charsetName) { 
    try { 
     return new java.util.Scanner(is, charsetName).useDelimiter("\\A").next(); 
    } catch (java.util.NoSuchElementException e) { 
     return ""; 
    } 
} 

Vì vậy, để mã hóa từ Latin-1, gọi nó là như thế này:

String message = convertStreamToString(is, "8859_1"); 
+0

Bạn nên biết rằng 'Máy quét' nội bộ biên dịch một' mẫu 'regex cho dấu phân tách. Phương pháp này thực sự thú vị và tiện lợi, nhưng cũng có thể không được khuyến khích. – oldrinb

+0

Tôi muốn hiểu thêm về điều này: Vấn đề với mẫu đó là gì? Không phải là nó khá nhẹ? – cyroxx

+0

Nó chỉ có vẻ giống như một giải pháp thú vị nhưng lạm dụng của 'Scanner'. Trong câu trả lời bạn đã liên kết, họ đặt nó rất tốt ... một thủ thuật * Máy quét 'ngu ngốc '*. – oldrinb