java 导出乱码 java生成文件乱码_java写入文件中文乱码问题

最近需要从Java中输出UTF-8编码的XML文件,遇到了两次中文乱码问题。一是奇数个汉字出现乱码,二是写入文件的实际编码与XML声明的编码不符。经过几番折腾,终于解决这两个问题,也对Java的字符编码加深了了解。

问题重现

以下面XML为例:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<test>一一一</test>
1
2
<?xmlversion="1.0"encoding="UTF-8"standalone="no"?>
<test>一一一</test>

生成XML代码如下:

Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element elem = doc.createElement("test");
elem.setTextContent("一一一");
doc.appendChild(elem);
DOMSource source = new DOMSource(doc);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
Transformer transformer = TransformerFactory.newInstance().newTransformer();
StreamResult result = new StreamResult(bytes);
Properties properties = transformer.getOutputProperties();
properties.setProperty(OutputKeys.INDENT, "yes");
properties.setProperty(OutputKeys.ENCODING, "UTF-8");
properties.setProperty("{http://xml.apache.org/xslt}indent-amount", "2");
transformer.setOutputProperties(properties);
transformer.transform(source, result);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Documentdoc=DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Elementelem=doc.createElement("test");
elem.setTextContent("一一一");
doc.appendChild(elem);
DOMSourcesource=newDOMSource(doc);
ByteArrayOutputStreambytes=newByteArrayOutputStream();
Transformertransformer=TransformerFactory.newInstance().newTransformer();
StreamResultresult=newStreamResult(bytes);
Propertiesproperties=transformer.getOutputProperties();
properties.setProperty(OutputKeys.INDENT,"yes");
properties.setProperty(OutputKeys.ENCODING,"UTF-8");
properties.setProperty("{http://xml.apache.org/xslt}indent-amount","2");
transformer.setOutputProperties(properties);
transformer.transform(source,result);

至此,XML已存储至bytes中。

接下来我想XML保存至文件,想当然地写了以下代码:

String xmlStr = bytes.toString();
FileWriter writer = new FileWriter("test.xml");
writer.write(xmlStr);
writer.close();
1
2
3
4
5
StringxmlStr=bytes.toString();
FileWriterwriter=newFileWriter("test.xml");
writer.write(xmlStr);
writer.close();

不料得到的XML文件中文部分出现乱码,且XML标签的一个字符'

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<test>丿؀丿/test>
1
2
<?xmlversion="1.0"encoding="UTF-8"standalone="no"?>
<test>丿؀丿/test>

该问题只出现在中文字符为奇数个时,原因见参考链接1。

经过一番修改,写出如下代码:

String xmlStr = bytes.toString("UTF-8");
FileWriter writer = new FileWriter("test.xml");
writer.write(xmlStr);
writer.close();
1
2
3
4
5
StringxmlStr=bytes.toString("UTF-8");
FileWriterwriter=newFileWriter("test.xml");
writer.write(xmlStr);
writer.close();

得到另一种乱码:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<test>һһһ</test>
1
2
<?xmlversion="1.0"encoding="UTF-8"standalone="no"?>
<test>һһһ</test>

经观察,该文件实际为GB编码,因Notepad++根据XML的encoding属性自动识别并显示为UTF-8造成乱码,将该文件以GB编码显示正常。

搜索得到原因,Java的FileWriter以平台默认编码写入文件。于是修改代码如下:

String xmlStr = bytes.toString("UTF-8");
FileOutputStream fos = new FileOutputStream("test.xml");
OutputStreamWriter writer = new OutputStreamWriter(fos, "UTF-8");
writer.write(xmlStr);
writer.close();
1
2
3
4
5
StringxmlStr=bytes.toString("UTF-8");
FileOutputStreamfos=newFileOutputStream("test.xml");
OutputStreamWriterwriter=newOutputStreamWriter(fos,"UTF-8");
writer.write(xmlStr);
writer.close();
或
String xmlStr = bytes.toString("UTF-8");
FileOutputStream fos = new FileOutputStream("test.xml");
fos.write(bytes.toByteArray());
fos.close();
1
2
3
4
5
StringxmlStr=bytes.toString("UTF-8");
FileOutputStreamfos=newFileOutputStream("test.xml");
fos.write(bytes.toByteArray());
fos.close();

至此得到无乱码的XML文件。

问题分析

Transformer得到的bytes为UTF-8的二进制数据,若使用toString()会使用系统默认编码对此数据进行编码并转为字符串(Unicode存储),造成奇数个汉字乱码问题。使用toString(“UTF-8”)指定编码格式即可解决此问题。

使用FileWriter将字符串写入文件时仍会将字符串(Unicode存储)转换为系统默认编码写入文件,这就造成文件实际编码格式与XML中声明的编码格式不同。使用OutputStreamWriter指定编码格式,或者将UTF-8的二进制数据用FileOutputStream直接写入文件,即可解决此问题。