这里有几个因素在起作用:
- 文本文件没有用于描述其编码的内在元数据(关于尖括号税的所有讨论,XML 流行是有原因的)
- Windows 的默认编码仍然是 8 位(或双字节)“ ANSI ”字符集,值范围有限 - 以这种格式编写的文本文件不可移植
- 为了从 ANSI 文件中区分 Unicode 文件,Windows 应用程序依赖于文件开头是否存在字节顺序标记(严格来说并非如此 - Raymond Chen 解释道)。从理论上讲,BOM 可以告诉您数据的字节顺序(字节顺序)。对于 UTF-8,即使只有一个字节顺序,Windows 应用程序也依赖标记字节来自动确定它是 Unicode(尽管您会注意到记事本在其打开/保存对话框中有一个编码选项)。
- 说 Java 坏了是错误的,因为它不会自动编写 UTF-8 BOM。例如,在 Unix 系统上,将 BOM 写入脚本文件是错误的,并且许多 Unix 系统使用 UTF-8 作为其默认编码。有时您也不希望在 Windows 上使用它,例如将数据附加到现有文件时:
fos = new FileOutputStream(FileName,Append);
这是一种可靠地将 UTF-8 数据附加到文件的方法:
private static void writeUtf8ToFile(File file, boolean append, String data)
throws IOException {
boolean skipBOM = append && file.isFile() && (file.length() > 0);
Closer res = new Closer();
try {
OutputStream out = res.using(new FileOutputStream(file, append));
Writer writer = res.using(new OutputStreamWriter(out, Charset
.forName("UTF-8")));
if (!skipBOM) {
writer.write('\uFEFF');
}
writer.write(data);
} finally {
res.close();
}
}
用法:
public static void main(String[] args) throws IOException {
String chinese = "\u4E0A\u6D77";
boolean append = true;
writeUtf8ToFile(new File("chinese.txt"), append, chinese);
}
注意:如果文件已经存在并且您选择附加并且现有数据不是UTF-8 编码的,那么代码将创建的唯一东西就是一团糟。
这是Closer
此代码中使用的类型:
public class Closer implements Closeable {
private Closeable closeable;
public <T extends Closeable> T using(T t) {
closeable = t;
return t;
}
@Override public void close() throws IOException {
if (closeable != null) {
closeable.close();
}
}
}
此代码对如何根据字节顺序标记读取文件进行了 Windows 风格的最佳猜测:
private static final Charset[] UTF_ENCODINGS = { Charset.forName("UTF-8"),
Charset.forName("UTF-16LE"), Charset.forName("UTF-16BE") };
private static Charset getEncoding(InputStream in) throws IOException {
charsetLoop: for (Charset encodings : UTF_ENCODINGS) {
byte[] bom = "\uFEFF".getBytes(encodings);
in.mark(bom.length);
for (byte b : bom) {
if ((0xFF & b) != in.read()) {
in.reset();
continue charsetLoop;
}
}
return encodings;
}
return Charset.defaultCharset();
}
private static String readText(File file) throws IOException {
Closer res = new Closer();
try {
InputStream in = res.using(new FileInputStream(file));
InputStream bin = res.using(new BufferedInputStream(in));
Reader reader = res.using(new InputStreamReader(bin, getEncoding(bin)));
StringBuilder out = new StringBuilder();
for (int ch = reader.read(); ch != -1; ch = reader.read())
out.append((char) ch);
return out.toString();
} finally {
res.close();
}
}
用法:
public static void main(String[] args) throws IOException {
System.out.println(readText(new File("chinese.txt")));
}
(System.out 使用默认编码,因此它是否打印任何合理的内容取决于您的平台和配置。)