【发布时间】:2020-03-09 02:17:44
【问题描述】:
我正在尝试将字节写入 windows-1252 字符集中的文件。下面的示例将浮点数的原始字节写入文件,这与我在实际程序中所做的类似。
在给出的示例中,我将 1.0f 的原始十六进制写入 test.txt。由于 1.0f 的原始十六进制是 3f 80 00 00,我希望得到 ?€(NUL)(NUL),正如我在 Windows 1252 Wikipedia article 中看到的那样,0x3f应该对应'?',0x80应该对应'€',0x00 是 'NUL'。在我真正尝试写入文件之前,一切都很好;那时,我在控制台上得到一个 java.nio.charset.UnmappableCharacterException ,在程序因该异常停止后,该文件只有一个“?”在里面。完整的控制台输出在下面的代码下方。
看起来 Java 认为代码点 0x80 在 windows-1252 代码页中不可映射。然而,这似乎不对——所有代码点都应该映射到该代码页中的实际字符。问题肯定出在代码点 0x80 上,好像我尝试使用 0.5f (3f 00 00 00) 很高兴写 ?(NUL)(NUL )(NUL) 到文件中,并且不抛出异常。尝试其他代码页似乎也不起作用。查看 Java 语言 here 支持的关键编码,只有 UTF 系列不会给我例外,但由于它们的编码,它们在实际文件中没有给我代码点 0x80。
我将尝试只使用字节来代替,这样我就不必担心字符串编码,但是有人能告诉我为什么下面的代码给了我它的异常吗?
代码:
import java.io.IOException;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
public class CharsetTest {
public static void main(String[] args) {
float max = 1.0f;
System.out.println("Checking " + max);
String stringFloatFormatHex = String.format("%08x", Float.floatToRawIntBits(max));
System.out.println(stringFloatFormatHex);
byte[] bytesForFile = javax.xml.bind.DatatypeConverter.parseHexBinary(stringFloatFormatHex);
String stringForFile = new String(bytesForFile);
System.out.println(stringForFile);
String charset = "windows-1252";
try {
Writer output = Files.newBufferedWriter(Paths.get("test.txt"), Charset.forName(charset));
output.write(stringForFile);
output.close();
} catch (IOException e) {
System.err.println(e.getMessage());
e.printStackTrace();
}
}
}
控制台输出:
Checking 1.0
3f800000
?�
Input length = 1
java.nio.charset.UnmappableCharacterException: Input length = 1
at java.nio.charset.CoderResult.throwException(CoderResult.java:282)
at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:285)
at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)
at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)
at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)
at java.io.BufferedWriter.close(BufferedWriter.java:265)
at CharsetTest.main(CharsetTest.java:21)
【问题讨论】:
-
查看
String(byte[])构造函数。不确定您的默认字符集是什么,但可能发生的情况是:第二个字符无法映射,因此将变成�,即 unicode 替换字符,在 Windows-1252 中无法编码。 -
使用
System.out.println("Default Charset=" + Charset.defaultCharset());,看起来我的默认字符集是UTF-8。我使用的是 Windows 10。 -
\0x80不是有效的 utf-8 序列,因此被替换为 �. -
哦,我明白了!你是对的;问题出在指令
String stringForFile = new String(bytesForFile);上,位于DatatypeConverter下方。当我在不提供字符集的情况下构造字符串时,它默认为 UTF-8,这是行不通的。但是,它只会在写入文件时抛出异常。 -
是的,因为无法在 windows-1252 中编码,所以会抛出异常。发生在无法以某些字符集编码的所有字符上,这是一件好事。唯一的问题是 String 构造函数默默地替换无效序列是否是一件好事。但现在已经不可能改变了。
标签: java character-encoding windows-1252