2010-12-08 29 views
14

Tôi có một thủ tục được lưu trữ Java để lấy bản ghi từ bảng bằng cách sử dụng đối tượng Resultet và tạo tệp csv.Làm thế nào để thêm một UTF-8 BOM trong java

BLOB retBLOB = BLOB.createTemporary(conn, true, BLOB.DURATION_SESSION); 
retBLOB.open(BLOB.MODE_READWRITE); 
OutputStream bOut = retBLOB.setBinaryStream(0L); 
ZipOutputStream zipOut = new ZipOutputStream(bOut); 
PrintStream out = new PrintStream(zipOut,false,"UTF-8"); 
out.write('\ufeff'); 
out.flush(); 
zipOut.putNextEntry(new ZipEntry("filename.csv")); 
while (rs.next()){ 
    out.print("\"" + rs.getString(i) + "\""); 
    out.print(","); 
} 
out.flush(); 
zipOut.closeEntry(); 
zipOut.close(); 
retBLOB.close(); 
return retBLOB; 

Nhưng tệp csv được tạo không hiển thị đúng ký tự tiếng Đức. Cơ sở dữ liệu Oracle cũng có giá trị NLS_CHARACTERSET của UTF8.

Vui lòng đề xuất.

+1

Chỉ trong trường hợp bạn chưa từng gặp vấn đề này trước đây, lưu ý rằng tiêu chuẩn Unicode không yêu cầu hoặc khuyên bạn nên sử dụng BOM với UTF-8. Nó không phải là bất hợp pháp, hoặc, nhưng không nên được sử dụng bừa bãi. Xem [tại đây] (http://unicode.org/faq/utf_bom.html#BOM) để biết chi tiết, bao gồm một số nguyên tắc về thời gian và địa điểm sử dụng. Nếu bạn đang cố gắng xem tệp csv trong Windows, đây có thể là một sử dụng hợp lệ của BOM. –

+0

Có, chúng tôi đang cố gắng để xem các csv trong Windows, nhưng csv tạo ra vẫn cho thấy nhân vật bị cắt xén cho các ký tự Đức. Đây có phải là cách đúng để thiết lập BOM không? – Fadd

+0

Vâng, đúng vậy. Tiêu chuẩn Unicode đề xuất ** chống lại ** bằng cách sử dụng BOM (không thực sự) với UTF-8. – tchrist

Trả lời

7

Để viết BOM bằng UTF-8, bạn cần PrintStream.print(), không phải PrintStream.write().

Ngoài ra nếu bạn muốn có BOM trong tệp csv của mình, tôi đoán bạn cần in BOM sau putNextEntry().

+0

Không phải tất cả PrintStream đều thiếu sót do chúng loại bỏ tất cả các lỗi có thể xảy ra trên luồng, bao gồm lỗi I/O, hệ thống tệp đầy đủ, gián đoạn mạng và mã hóa không khớp? Nếu điều này không đúng, bạn có thể vui lòng cho tôi biết cách làm cho chúng đáng tin cậy (vì tôi muốn sử dụng chúng) không? Nhưng nếu nó là sự thật, bạn có thể vui lòng giải thích khi nào nó có thể thích hợp để sử dụng một phương pháp đầu ra để ngăn chặn các mối quan tâm đúng đắn? Đây là một câu hỏi nghiêm trọng, bởi vì tôi không hiểu tại sao điều này được thiết lập là rất nguy hiểm. Cảm ơn bạn vì bất kỳ thông tin chi tiết nào. – tchrist

+0

@tchrist - đúng là PrintStreams triệt tiêu các lỗi. Tuy nhiên ... 1) chúng không bị loại bỏ hoàn toàn - bạn có thể kiểm tra xem có lỗi nào không. 2) Có những trường hợp bạn không cần biết về lỗi. Một trường hợp không thể chối cãi là khi bạn đang gửi các ký tự đến luồng đang ghi vào bộ đệm trong bộ nhớ. –

+0

@tchrist Tôi đoán, tất cả điều này là do sử dụng các ngoại lệ đã kiểm tra. Thông thường, bạn chỉ cần ném vào bất kỳ lỗi và được hạnh phúc. Bạn có thể tạo một "PrintStream'" hiện có an toàn bằng cách gói từng cuộc gọi và thêm 'checkError' và ném điều kiện. Nhưng thông tin về ngoại lệ bị mất. Vì vậy, có, 'PrintStream' là một crap vô vọng. – maaartinus

7

Tôi nghĩ rằng out.write('\ufeff'); thực ra phải là out.print('\ufeff');.

Theo the javadoc, phương thức write(int) thực sự ghi một byte ... mà không cần bất kỳ mã hóa ký tự nào. Vì vậy, out.write('\ufeff'); ghi byte 0xff. Ngược lại, phương thức print(char) mã hóa ký tự dưới dạng một hoặc byte bằng cách sử dụng mã hóa của luồng và sau đó ghi các byte đó.

+0

Không phải là cách an toàn duy nhất để thực hiện đầu ra được mã hóa trong Java là sử dụng 'OutputStreamWriter (OutputStream out, CharsetEncoder enc)' hiếm hoi của hàm khởi tạo, chỉ một trong bốn phương thức có đối số 'CharsetEncoder' rõ ràng, và không bao giờ sử dụng 'PrintStream' mà bạn đã đề xuất ở đây? – tchrist

+0

@tchrist - 1) Số 2) Tôi không * đề xuất * PrintStream. Tôi chỉ đơn giản nói làm thế nào để làm những gì OP yêu cầu làm bằng cách sử dụng PrintStream ông đã sử dụng. 3) Trong trường hợp này PrintStream nên được an toàn bởi vì nó được theo sau bởi các hành động khác sẽ gây ra ghi vào dòng cơ bản (socket) và ném một ngoại lệ nếu PrintStream trước viết đã âm thầm thất bại. –

49
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(...), StandardCharsets.UTF_8)); 
out.write('\ufeff'); 
out.write(...); 

Điều này ghi chính xác 0xEF 0xBB 0xBF vào tệp, biểu diễn UTF-8 của BOM.

+1

Mã này nhạy cảm với mã hóa nền tảng mặc định. Trên Windows, tôi đã kết thúc bằng 0x3F được ghi vào tệp. Cách chính xác để lấy BufferedWriter là: 'BufferedWriter out = new BufferedWriter (new OutputStreamWriter (new FileOutputStream (tệp), StandardCharsets.UTF_8))' –

0

Trong trường hợp của tôi nó hoạt động với mã:

PrintWriter out = new PrintWriter(new File(filePath), "UTF-8"); 
out.write(csvContent); 
out.flush(); 
out.close(); 
3

Chỉ trong trường hợp người sử dụng PrintStream s, bạn cần phải làm điều đó một chút khác nhau. Trong khi một Writer sẽ làm một số phép thuật để chuyển đổi một byte đơn thành 3 byte, một PrintStream yêu cầu tất cả 3 byte UTF-8 BOM riêng lẻ:

// Print utf-8 BOM 
    PrintStream out = System.out; 
    out.write('\ufeef'); // emits 0xef 
    out.write('\ufebb'); // emits 0xbb 
    out.write('\ufebf'); // emits 0xbf 

Ngoài ra, bạn có thể sử dụng các giá trị hex cho những người trực tiếp:

PrintStream out = System.out; 
    out.write(0xef); // emits 0xef 
    out.write(0xbb); // emits 0xbb 
    out.write(0xbf); // emits 0xbf 
Các vấn đề liên quan