Làm thế nào để chuyển đổi một Reader sang InputStream và một Writer thành OutputStream?


87

Có cách nào dễ dàng để tránh đối phó với các vấn đề về mã hóa văn bản không?

Câu trả lời:


45

Bạn thực sự không thể tránh đối phó với các vấn đề mã hóa văn bản, nhưng có các giải pháp hiện có trong Apache Commons:

Bạn chỉ cần chọn bảng mã mà bạn chọn.


7
FYI: mã ReaderInputStream có một lỗi trong cách nó đọc byte (nó sẽ không hoạt động cho tất cả các mã hóa). Bằng chứng: bất hợp phápargumentexception.blogspot.com/2009/05/… Có một lỗi mở: Problem.apache.org/bugzilla/show_bug.cgi?id=40455
McDowell

1
Bạn có thể tìm thấy các lớp học trong thư viện commons-io của Apache: commons.apache.org/proper/commons-io
AlikElzin-kilaka

@McDowell, lỗi bạn đã đề cập là trong quá trình triển khai của Apache Ant, không phải trong commons-io, vì vậy nó không liên quan đến câu trả lời này.
La Mã

94

Nếu bạn đang bắt đầu với một Chuỗi, bạn cũng có thể làm như sau:

new ByteArrayInputStream(inputString.getBytes("UTF-8"))

7
Việc ReaderInputStreamtriển khai tốt sẽ yêu cầu ít bộ nhớ hơn - không cần thiết phải lưu trữ tất cả các byte trong một mảng cùng một lúc.
Piotr Findeisen

3
Tôi thích giải pháp này vì nó hoạt động khi bạn cần mã kiểm tra đơn vị chấp nhận đầu vào trên (ví dụ) đầu vào tiêu chuẩn.
Kedar Mhaswade

43

Vâng, một Trình đọc xử lý các ký tự và một InputStream xử lý các byte. Mã hóa chỉ định cách bạn muốn biểu diễn các ký tự của mình dưới dạng byte, vì vậy bạn thực sự không thể bỏ qua vấn đề này. Để tránh các vấn đề, ý kiến ​​của tôi là: chọn một bộ ký tự (ví dụ: "UTF-8") và gắn bó với nó.

Về cách thực sự làm điều đó, như đã được chỉ ra, " tên rõ ràng cho các lớp này là ReaderInputStreamWriterOutputStream . " Đáng ngạc nhiên là " chúng không được bao gồm trong thư viện Java " mặc dù các lớp 'đối lập', InputStreamReaderOutputStreamWriter bao gồm.

Vì vậy, rất nhiều người đã nghĩ ra cách triển khai của riêng họ, bao gồm cả Apache Commons IO . Tùy thuộc vào các vấn đề cấp phép, bạn có thể sẽ có thể đưa thư viện commons-io vào dự án của mình hoặc thậm chí sao chép một phần của mã nguồn (có thể tải xuống tại đây ).

Như bạn có thể thấy, tài liệu của cả hai lớp đều nói rằng "tất cả các bảng mã bộ ký tự được hỗ trợ bởi JRE đều được xử lý chính xác".

NB Một bình luận về một trong những câu trả lời khác ở đây đề cập đến lỗi này . Nhưng điều đó ảnh hưởng đến lớp Apache Ant ReaderInputStream ( ở đây ), không phải lớp Apache Commons IO ReaderInputStream.


19

Cũng lưu ý rằng, nếu bạn đang bắt đầu với Chuỗi, bạn có thể bỏ qua việc tạo StringReader và tạo InputStream trong một bước bằng cách sử dụng org.apache.commons.io.IOUtils từ Commons IO như sau:

InputStream myInputStream = IOUtils.toInputStream(reportContents, "UTF-8");

Tất nhiên bạn vẫn cần phải suy nghĩ về mã hóa văn bản, nhưng ít nhất quá trình chuyển đổi đang diễn ra trong một bước.


4
Về cơ bản new ByteArrayInputStream(report.toString().getBytes("utf-8")), phương pháp này bao gồm việc phân bổ hai bản sao bổ sung của báo cáo trong bộ nhớ. Nếu báo cáo lớn, nó là xấu. Hãy xem câu trả lời của tôi.
Oliv

8

Sử dụng:

new CharSequenceInputStream(html, StandardCharsets.UTF_8);

Cách này không yêu cầu chuyển đổi trả trước đến Stringvà sau đó byte[], sẽ phân bổ nhiều bộ nhớ heap hơn, trong trường hợp báo cáo lớn. Nó chuyển đổi thành byte ngay lập tức khi luồng được đọc, ngay từ StringBuffer.

Nó sử dụng CharSequenceInputStream từ dự án Apache Commons IO.



5

Tên rõ ràng cho các lớp này là ReaderInputStream và WriterOutputStream. Thật không may, chúng không được bao gồm trong thư viện Java. Tuy nhiên, google là bạn của bạn.

Tôi không chắc rằng nó sẽ giải quyết được tất cả các vấn đề về mã hóa văn bản, vốn là điều gây ác mộng.

Có một RFE, nhưng nó bị đóng, sẽ không sửa được.


1
bug.openjdk.java.net/browse/JDK-4103785 có nhận xét "chúng tôi có một API công khai để mã hóa bộ ký tự ... không có lý do thuyết phục nào để thêm các lớp này" - vậy cách thực hiện điều này trong Java 7 mà không cần bổ sung thư viện, mười hai năm xuống đường?
Piotr Findeisen

5

Bạn không thể tránh các vấn đề về mã hóa văn bản, nhưng Apache commons-io

Lưu ý rằng đây là các thư viện được đề cập đến trong câu trả lời của Peter trên koders.com, chỉ là các liên kết đến thư viện thay vì mã nguồn.


4

Bạn đang cố gắng viết nội dung của a Readerthành an OutputStream? Nếu vậy, bạn sẽ có thời gian dễ dàng hơn OutputStreamtrong việc gói OutputStreamWritervà viết các chars từ Readerđến Writerthay vì cố gắng chuyển đổi người đọc thành InputStream:

final Writer writer = new BufferedWriter(new OutputStreamWriter( urlConnection.getOutputStream(), "UTF-8" ) );
int charsRead;
char[] cbuf = new char[1024];
while ((charsRead = data.read(cbuf)) != -1) {
    writer.write(cbuf, 0, charsRead);
}
writer.flush();
// don't forget to close the writer in a finally {} block

1

Cảnh báo khi sử dụng WriterOutputStream - không phải lúc nào nó cũng xử lý việc ghi dữ liệu nhị phân vào tệp một cách chính xác / giống như một luồng đầu ra thông thường. Tôi đã gặp sự cố với điều này mà tôi đã mất một thời gian để theo dõi.

Nếu bạn có thể, tôi khuyên bạn nên sử dụng luồng đầu ra làm cơ sở của mình và nếu bạn cần viết chuỗi, hãy sử dụng trình bao bọc OUtputStreamWriter xung quanh luồng để thực hiện. Việc chuyển đổi văn bản thành byte đáng tin cậy hơn nhiều so với cách khác, đó có thể là lý do tại sao WriterOutputStream không phải là một phần của thư viện Java tiêu chuẩn



-1

Để đọc một chuỗi trong một luồng chỉ sử dụng những gì java cung cấp.

InputStream s = new BufferedInputStream( new ReaderInputStream( new StringReader("a string")));

6
ReaderInputStream nằm trong Apache Commons IO.
Will Beason
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.