dom4j的乱码问题

原创 2011年01月20日 21:22:00

1)背景

长期运行的爬虫程序(抓取xml)突然出了问题。xml的乱码导致无法验证通过

2)乱码是怎么产生的

发现不同的网站返回的xml编码不一致,有的是gb2312,有的utf-8。
爬虫程序将urlConnection.getInputStream() 的字节流传递给了SAXReader来构造Document
可惜SAXReader还不够强悍,由于只是获取了字节流,但不知道编码方式,于是SAXReader采用了系统默认的编码方式对对待字节流,问题就出在这里。

3) 未指定编码,SAXReader如何处理字节流

  • org.gjt.xpp.sax2.Driver.paser(InputSource source)

             if(encoding == null)
               reader = new InputStreamReader(stream);

  • java.io.InputStreamReader   

            sd = StreamDecoder.forInputStreamReader(in, this, (String)null)
           编码方式为空

  • sun.nio.cs.StreamDecoderforInputStreamReader

               (InputStream in,Object lock,String charsetName)
               if (csn == null)
                        csn = Charset.defaultCharset().name();
               获取默认编码方式

  • java.nio.charset.Charset.defaultCharset()

           java.security.PrivilegedAction pa =
           new GetPropertyAction("file.encoding");
          String csn = (String)AccessController.doPrivileged(pa);
          Charset cs = lookup(csn);
          if (cs != null)
           defaultCharset = cs;
          else
           defaultCharset = forName("UTF-8");
          首先参考-Dfileencoding,如果没有就是系统默认字符编码,还找不到就是“UTF-8”
          如果 在eclipse中运行程序,eclipse会指定-Dfileencoding, 值就是你得文件编码

4)如何确定xml编码方式

           参考com.sun.syndication.io.XmlReader

  • 查看文件第一行,看是后有<?xml .... encoding="xx" ...?>
  • 查看http response header中是否含有 Content-Type text/xml; charset=xx   
  • 探测BOM (UTF-8 签名)

            取头3个字节
            UTF_16BE:0xFE  0xFF
            UTF_16LE:0xFF   0xFE
            UTF_8: 0xEF 0xBB 0xBF

            实际通过测试发现:
            //utf-16BE、utf-16LE、utf-16,utf-8编码差别
            System.out.println(Arrays.toString("<".getBytes("utf-16BE"))); :[0, 60]
            System.out.println(Arrays.toString("<".getBytes("utf-16LE")));  :[60, 0]
            System.out.println(Arrays.toString("<".getBytes("utf-16")));      :[-2, -1, 0, 60]
            System.out.println(Arrays.toString("<".getBytes("utf-8")));  :[60]
            //能识别BOM?
            byte[] b1=new byte[]{-2,-1,0,60};
            System.out.println(new String(b1,"UTF-16BE")); //  <
            System.out.println(new String(b1,"UTF-16")); //  <
             
            byte[] b1=new byte[]{-1,-2,60,0};
            System.out.println(new String(b1,"UTF-16LE")); //  ?<   
            System.out.println(new String(b1,"UTF-16")); //  <

            byte[] b1=new byte[]{-17,-69,-65,60};
            System.out.println(new String(b1,"UTF-8")); //  ?<   
           
           上面红色代表错误 ,绿色代表正确
            可见java中的BOM纯粹是为UTF-16 big endian 或者little endian准备,基本上已不具备识别UTF-16BE、UTF-16LE、UTF-16、UTF-8功能        
     

  • 猜测

          取头4个字节,看是否匹配<?xm
          UTF_16BE: 0x00 0x3C 0x00 0x3F
          UTF_16BE: 0x3C 0x00 0x3F 0x00
          UTF_8: 0x3C 0x3F 0x78 0x6D

      

5)修正方式

采用第一种

  • 使用PushbackInputStream封装预读少量数据(200)
  • 退回读取的字节(PushbackInputStream.unread)保持输入字节流的完整
  • 使用正则抓取数据的第一行,获取encoding
  • 构造new InputStreamReader(pis,encoding),传给XmlReader以免不知道采用何种编码解析

 

6)  org.dom4j.Document.asXML()的bug

      经过上面的步骤输入正确了,Document也成功解析了,为什么      org.dom4j.Document.asXML() 仍然乱码?
     
     看看代码:
     public String asXML() {
       try {
           ByteArrayOutputStream out = new ByteArrayOutputStream();
           XMLWriter writer = new XMLWriter(out, outputFormat);
           writer.write(this);
           return out.toString();
       }
       catch (IOException e) {
           throw new RuntimeException("IOException while generating textual representation: " + e.getMessage());
       }
    }

  6.1) 问题在哪?

    out.toString()

  • java.io.ByteArrayOutputStream.toString()

             return new String(buf, 0, count);

  • java.lang.String(byte bytes[], int offset, int length)

            char[] v  = StringCoding.decode(bytes, offset, length);

  • java.lang.StringCoding.decode(byte[] ba, int off, int len) 

             String csn = Charset.defaultCharset().name();
             try {
               return decode(csn, ba, off, len);
             } catch (UnsupportedEncodingException x) {
               warnUnsupportedCharset(csn);
             }    
      采用了系统默认编码来输出导致问题

    6.2)如何修正?

    需要看看XMLWriter采用了何种编码写入document

  •  org.dom4j.io.XMLWriter(OutputStream out, OutputFormat format)    

        this.writer = createWriter(out, format.getEncoding());
       采用了format.getEncoding()

        知道了写入时的编码就好说了 
        out.toString(outputFormat.getEncoding());
  

   7)小结

   当程序处理字节流的时候,必须想办法知道字节的编码方式,否者就会出问题

Dom4j中的中文编码问题

一、“中文问题没商量”之Dom4j中的编码问题    本文主要讲述的是Dom4j在把Document保存到文件过程中出现的一个中文问题,本文跟《80前》一文一样,以Spring项目无关,请“春迷...

dom4j生成xml文件,解析后出现中文乱码问题

背景(可以忽略)项目在本地写完了,和队友测试都通过了,最后部署到云端,云端服务器是Windows Server 2008 R2 Standard操作系统,部署到云端后再次和队友测试一下,就出现了问题。...

dom4j 中文乱码问题

在用 dom4j 以 utf8 编码格式生成 xml 文档后,发现该 xml 文档包含中文的部分异常,无法读取。随后被逼无奈,只好使出猥琐招数,直接将要写入 xml 的字符串重新以 utf8 格式编码...
  • Dancen
  • Dancen
  • 2011年12月05日 23:40
  • 5641

Xml—dom4j解析以及写入xml文档时的乱码问题

Xml—dom4j解析以及写入xml文档时的乱码问题

Dom4j方式解析XML乱码问题详解

1.  引起乱码的原因 现象:将内存中的Document对象保存到持久化设备生成XML文件后,XML文件无法正常打开,出现乱码。 表面原因:因为XML文件的真正格式(即XML文件保存在持久化设备上...
  • MyTroy
  • MyTroy
  • 2014年11月30日 17:06
  • 1372

xml文档解析之dom4j的增删改查 ,乱码问题

基于Dom解析和SAX解析这两种解析思想,出现了许多解析API,其中dom4j可以使用dom解析的方式高效的解析xml文档。dom4j解析在使用时需要导入第三方jar包,dom4j的开发包可以在网上找...

黑马程序员---关于使用dom4j写入数据时的乱码问题分析

------- Java、.Net、Android培训期待与您交流!------- 1、在使用dom4j 对xml 文档进行解析时,通常会遇到把更新后的文档内容在写入到硬盘的xml 文档中,这时就存...

dom4j向xml文件中写数据乱码问题解决

这是原先利用dom4j向xml文件中写数据的代码 public void save(Student e) { // TODO Auto-generated method stub Docume...

jDom 和dom4j 输出的中文乱码的解决方案

自己碰到的问题: 原错误代码:   view plaincopy to clipboardprint? Document document = Docu...

关于dom4j中使用FileWriter构造XMLWriter导致生成的xml中文乱码总结。

本人新手一枚,谈不上总结,只能算是自学过程中对于问题的分析和理解吧。       先谈谈我之前不太清楚的乱码, 在eclipse中,通过    System.out.println(Charset.d...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:dom4j的乱码问题
举报原因:
原因补充:

(最多只允许输入30个字)