如何在Java中添加UTF-8 BOM?
我有一个Java存储过程,它使用如何在Java中添加UTF-8 BOM?,java,character-encoding,oracle10g,byte-order-mark,Java,Character Encoding,Oracle10g,Byte Order Mark,我有一个Java存储过程,它使用Resultset对象从表中获取记录,并创建一个CS Vfile BLOB retBLOB = BLOB.createTemporary(conn, true, BLOB.DURATION_SESSION); retBLOB.open(BLOB.MODE_READWRITE); OutputStream bOut = retBLOB.setBinaryStream(0L); ZipOutputStream zipOut = new ZipOutputStream
Resultset
对象从表中获取记录,并创建一个CS Vfile
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;
但是生成的CSV文件没有显示正确的德语字符。Oracle数据库还有一个NLS\U字符集
值UTF8
请建议。要在UTF-8中编写BOM,您需要
PrintStream.print()
,而不是PrintStream.write()
另外,如果您想在
csv
文件中包含BOM,我想您需要在putnextery()
之后打印BOM。我认为输出。写入('\ufeff')
实际上应该是out.print('\ufeff')代码>
根据这个例子,write(int)
方法实际上写入一个字节。。。没有任何字符编码。所以out.write('\ufeff')代码>写入字节0xff
。相反,print(char)
方法使用流的编码将字符编码为一个或多个字节,然后写入这些字节
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(...), StandardCharsets.UTF_8));
out.write('\ufeff');
out.write(...);
这将正确地将0xEF 0xBB 0xBF写入文件,该文件是BOM的UTF-8表示形式。在我的示例中,它与代码一起工作:
PrintWriter out = new PrintWriter(new File(filePath), "UTF-8");
out.write(csvContent);
out.flush();
out.close();
为了防止人们使用PrintStream
s,您需要稍微改变一下。虽然编写器
可以将单个字节转换为3个字节,但打印流
单独需要UTF-8 BOM的所有3个字节:
// Print utf-8 BOM
PrintStream out = System.out;
out.write('\ufeef'); // emits 0xef
out.write('\ufebb'); // emits 0xbb
out.write('\ufebf'); // emits 0xbf
或者,您可以直接使用十六进制值:
PrintStream out = System.out;
out.write(0xef); // emits 0xef
out.write(0xbb); // emits 0xbb
out.write(0xbf); // emits 0xbf
您将此添加到CSV字符串的第一个
String CSV = "";
byte[] BOM = {(byte) 0xEF,(byte) 0xBB,(byte) 0xBF};
CSV = new String(BOM) + CSV;
这对我来说很有用。这里有一个简单的方法可以将BOM表头附加到任何文件上:
private static void appendBOM(File file) throws Exception {
File bomFile = new File(file + ".bom");
try (FileOutputStream output = new FileOutputStream(bomFile, true)) {
byte[] bytes = FileUtils.readFileToByteArray(file);
output.write('\ufeef'); // emits 0xef
output.write('\ufebb'); // emits 0xbb
output.write('\ufebf'); // emits 0xbf
output.write(bytes);
output.flush();
}
file.delete();
bomFile.renameTo(file);
}
为了防止您以前没有遇到过这种情况,请注意Unicode标准不要求或建议使用带有UTF-8的BOM。它也不是违法的,但不应该被滥用。有关详细信息,请参阅,包括有关何时何地使用它的一些指导原则。如果您试图在Windows中查看csv文件,这可能是BOM的有效使用。是的,我们正在尝试在Windows中查看csv,但生成的csv仍然显示德国字符的乱码字符。这是设置BOM表的正确方法吗?是的,没错。Unicode标准建议不要在UTF-8中使用所谓的BOM(实际上并非如此)。@tchrist:在处理仅包含ASCII字符的软件和协议时,建议不要使用BOM。如果OP知道他正在使用的Windows软件将使用BOM来检测文件是否实际以UTF-8编码(我们不关心它是否为BOM,我们关心它是否允许某些软件检测编码是否为UTF-8)。另外请注意,如果您有一个到UTF-8的BOM表,并且一些软件出现故障,那么这些软件将被破坏,因为UTF-8开头的BOM表是完全有效的。Excel 2003严格要求BOM采用UTF-8编码的CSV文件。否则,多字节字符将无法读取。难道不是所有的打印流都有根本性的缺陷,因为它们会丢弃流中可能发生的所有错误,包括I/O错误、完整文件系统、网络中断和编码不匹配吗?如果这不是真的,你能告诉我如何使他们可靠(因为我想使用他们)?但是,如果这是真的,您能解释一下什么时候使用一种抑制正确性问题的输出方法是合适的吗?这是一个严肃的问题,因为我不明白为什么这会如此危险。感谢您提供的任何见解。@tchrist-PrintStreams确实可以抑制错误。然而。。。1) 它们并没有完全被丢弃——您可以检查是否发生了错误。2) 有些情况下,您不需要了解错误。一种无可争议的情况是,当您向正在写入内存缓冲区的流发送字符时。@tchrist我想,这都是由于使用了选中的异常造成的。通常情况下,你只要犯下任何错误,就会感到高兴。通过包装每个调用并添加checkError
并有条件地抛出,可以使现有的PrintStream
变得“安全”。但是关于异常的信息丢失了。因此,是的,PrintStream
是一个毫无希望的垃圾。在Java中进行编码输出的唯一安全方法不是使用罕见的OutputStreamWriter(OutputStream out,CharsetEncoder enc)
作为构造函数的一部分,它是四个构造函数中唯一具有显式CharsetEncoder
参数的,而且从不使用你在这里推荐的PrintStream
?@tchrist-1)No.2)我不推荐PrintStream。我只是简单地说,如何使用OP已经在使用的PrintStream执行OP要求的操作。3) 在这种情况下,PrintStream应该是安全的,因为它后面跟着其他操作,这些操作将导致对底层流(套接字)的写入,并在以前的PrintStream写入操作以静默方式失败时引发异常。此代码对默认平台编码敏感。在Windows上,我最终将0x3F写入文件。获取BufferedWriter的正确方法是:BufferedWriter out=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(文件),StandardCharsets.UTF_8))