Java 编码问题导致Xerces UTF8Reader中格式错误的ByteSequenceException

Java 编码问题导致Xerces UTF8Reader中格式错误的ByteSequenceException,java,xml,character-encoding,xml-parsing,xerces,Java,Xml,Character Encoding,Xml Parsing,Xerces,我遇到了带有XML文件的com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException。我使用调试器逐步检查Xerces代码,并缩小了出现这种情况的区域。我能够确定,通过删除文档中的“智能引用”字符,文档变得可解析 该文档没有DTD。记事本++将其标记为“ANSI为UTF-8”。Firefox将其定位为“西部”。我记得在大学里的一次不太惊险的演讲中,UTF-8被设计成与单字节编码系统向后兼容。我还看到,字节序列E

我遇到了带有XML文件的
com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException
。我使用调试器逐步检查Xerces代码,并缩小了出现这种情况的区域。我能够确定,通过删除文档中的“智能引用”字符,文档变得可解析

该文档没有DTD。记事本++将其标记为“ANSI为UTF-8”。Firefox将其定位为“西部”。我记得在大学里的一次不太惊险的演讲中,UTF-8被设计成与单字节编码系统向后兼容。我还看到,字节序列E2809D实际上是“右双引号”的代表,但尽管我看不到编码问题,但我认为存在一个问题

我从Xerces收到的异常消息是3字节UTF-8序列的
无效字节3。
它是从的第435行的
无效字节(3,3,b2)
调用中抛出的。当我试图完全理解这个方法的逻辑时,我的大脑开始从我的耳朵里融化,所以我可能遗漏了一些东西,但正如我上面提到的字节3(0x90)。根据UTF-8表,上述序列中至少有一个是有效的

以下是十六进制编辑器中出现双引号的文件段:

我尝试了以下方法:

  • 通过Charset.forName(“UTF-8”)强制使用UTF-8加载字符串
  • 添加DTD
  • 在Notepad++中打开文件,并通过其UI将其编码为UTF-8
  • 上述各项的各种组合,有时重复
指示为无效的字节似乎是63(0x3F?)

我还尝试将此智能引号字符添加到以前可解析的文档中。正如预期的那样,它使解析器抛出相同的异常

堆栈跟踪:

com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: Invalid byte 3 of 3-byte UTF-8 sequence.
    at com.sun.org.apache.xerces.internal.impl.io.UTF8Reader.invalidByte(UTF8Reader.java:687)
    at com.sun.org.apache.xerces.internal.impl.io.UTF8Reader.read(UTF8Reader.java:435)
    at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.load(XMLEntityScanner.java:1753)
    at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.skipChar(XMLEntityScanner.java:1426)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2815)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:606)
    at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:117)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:510)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:848)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:777)
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
    at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:243)
    at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:347)
    at javax.xml.parsers.DocumentBuilder.parse(DocumentBuilder.java:121)

更新: 我仍然需要找到一种方法来安全地将其转换为字符串。我使用记事本++将文件编码为UTF-8。下面的代码成功地将字节加载到字符串中(当在Eclipse中调试时,我可以看到读取字符串中的XML),但是现在我得到了带有不同参数的畸形字节序列异常。这一次,我可以发布代码并使用:

File file = new File("ccd.xml");

byte[] ccdBytes = org.apache.commons.io.FileUtils.readFileToByteArray(file);
String ccdString = new String(ccdBytes, Charset.forName("UTF-8"));

CDAUtil.load(new ByteArrayInputStream(IOUtils.toByteArray(ccdString))); //method that's doing the parsing
堆栈跟踪:

Exception in thread "main" com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: Invalid byte 1 of 1-byte UTF-8 sequence.
    at com.sun.org.apache.xerces.internal.impl.io.UTF8Reader.invalidByte(UTF8Reader.java:687)
    at com.sun.org.apache.xerces.internal.impl.io.UTF8Reader.read(UTF8Reader.java:557)
    at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.load(XMLEntityScanner.java:1753)
    at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.skipChar(XMLEntityScanner.java:1426)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2815)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:606)
    at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:117)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:510)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:848)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:777)
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
    at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:243)
    at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:347)
    at javax.xml.parsers.DocumentBuilder.parse(DocumentBuilder.java:121)
    at org.openhealthtools.mdht.emf.runtime.resource.impl.FleXMLLoadImpl.load(FleXMLLoadImpl.java:55)
    at org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl.doLoad(XMLResourceImpl.java:180)
    at org.eclipse.emf.ecore.resource.impl.ResourceImpl.load(ResourceImpl.java:1494)
    at org.openhealthtools.mdht.uml.cda.util.CDAUtil.load(CDAUtil.java:268)
    at org.openhealthtools.mdht.uml.cda.util.CDAUtil.load(CDAUtil.java:250)
    at org.openhealthtools.mdht.uml.cda.util.CDAUtil.load(CDAUtil.java:238)
但是,

CDAUtil.load(new FileInputStream(new File("ccd.xml")));

works

您没有告诉我们如何将文件传递给Xerces。你可以用不同的方法做,得到不同的结果。您可以阅读关于xml编码问题的更详细解释

我建议你做以下一件事:

  • 使用notedpad++打开文件,如果缺少,则将
    添加为第一行
  • 在Notepad++中,将其转换为UTF-8(不带bom)(应该在“格式”菜单中,但我使用的是Notepad++的意大利语版本,所以我猜测菜单翻译)
  • 保存文件
  • 在Java中,将其作为InputStream打开,即将InputStream传递给xml解析器,而不是作为Reader子类
  • 这应该可以解决问题,如果您可以通过将文件传递给解析器的代码,那么就更容易找到问题

    这些步骤解决了这个问题,因为只有在使用InputStream(即字节流)时,解析器才会考虑xml中带有编码声明的第一行。如果读取字节流,则需要一个编码声明来指定如何将字节转换为字符

    如果您正在传递字符串,那么第一行是无用的,因为您正在传递一个字符流,并且不需要编码

    如果要使用字符串,必须将文件作为InputStream读取并转换为指定字符集的读取器(类似于
    InputStreamReader InputStreamReader=new InputStreamReader(xmlFileInputStream,“UTF-8”);


    我猜是因为您没有指定字符集,而Java选择了您的操作系统(Windows-1252)。

    只有在输入文件中出现实际的UTF-8编码错误时,我才能获得该错误消息。因此,我假设文件中的某个地方存在实际错误,您无法找到

    以下是我的测试代码:

    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    import org.w3c.dom.Document;
    
    public class ParseAXml {
      public static void main(String argv[]) throws Exception {
        String xmlFile = argv[0];
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse(xmlFile);
        System.out.println("Parsed Successfully");
      }
    }
    
    当我向它传递一个正确的文件(包含您所拥有的智能引号)时,我会收到预期的
    解析成功的消息。这是我的常规测试文件:

    $ hexdump -C tmp.xml
    00000000  3c 3f 78 6d 6c 20 76 65  72 73 69 6f 6e 3d 22 31  |<?xml version="1|
    00000010  2e 30 22 20 65 6e 63 6f  64 69 6e 67 3d 22 55 54  |.0" encoding="UT|
    00000020  46 2d 38 22 3f 3e 0a 3c  74 68 69 6e 67 3e 3c 61  |F-8"?>.<thing><a|
    00000030  3e 54 68 69 73 20 e2 80  9c 71 75 6f 74 65 e2 80  |>This ...quote..|
    00000040  9d 20 63 6f 75 6c 64 20  67 65 74 20 74 72 69 63  |. could get tric|
    00000050  6b 79 3c 2f 61 3e 3c 2f  74 68 69 6e 67 3e 0a     |ky</a></thing>.|
    0000005f
    
    为了帮助您,我编写了一个简短的java程序,试图找到格式错误的字节:

    import java.nio.*;
    import java.nio.charset.*;
    import java.io.*;
    
    public class FindBadUTF8 {
      public static void main(String argv[]) throws Exception {
        String filename = argv[0];
        InputStream inStream = new FileInputStream(filename);
    
        CharsetDecoder d=Charset.forName("UTF-8").newDecoder();
        CharBuffer out = CharBuffer.allocate(1);
        ByteBuffer in = ByteBuffer.allocate(10);
        in.clear();
        long offset = 0L;
        while (true) {
          int read = inStream.read();
          if (read != -1) {
            in.put((byte)read);
          }
          out.clear();
          in.flip();
          CoderResult cr = d.decode(in, out, (read == -1));
          if (cr.isError()) {
            if (read != -1) {
              System.out.println("Error at offset " + offset + ": " + cr);
              return;
            } else {
              System.out.println("Error at end-of-file: " + cr);
              return;
            }
          }
          if (cr.isUnderflow()) {
            in.position(in.limit());
            in.limit(in.capacity());
          } else {
            in.clear();
          }
          if (read == -1) {
            break;
          }
          offset += 1L;
        }
        System.out.println("OK");
      }
    }
    
    该程序在我的示例文件中运行时出现错误,会给出以下信息:

    $ java FindBadUTF8 tmp.err.xml
    Error at offset 56: MALFORMED[2]
    
    实际上,偏移量56(十六进制中为0x38)是我损坏的字节:

    $ hexdump -C tmp.err.xml
    00000000  3c 3f 78 6d 6c 20 76 65  72 73 69 6f 6e 3d 22 31  |<?xml version="1|
    00000010  2e 30 22 20 65 6e 63 6f  64 69 6e 67 3d 22 55 54  |.0" encoding="UT|
    00000020  46 2d 38 22 3f 3e 0a 3c  74 68 69 6e 67 3e 3c 61  |F-8"?>.<thing><a|
    00000030  3e 54 68 69 73 20 e2 80  ff 71 75 6f 74 65 e2 80  |>This ...quote..|
    00000040  9d 20 63 6f 75 6c 64 20  67 65 74 20 74 72 69 63  |. could get tric|
    00000050  6b 79 3c 2f 61 3e 3c 2f  74 68 69 6e 67 3e 0a     |ky</a></thing>.|
    0000005f
    
    $hextump-C tmp.err.xml
    00000000 3c 3f 78 6d 6c 20 76 65 72 73 69 6f 6e 3d 22 31 |。此…报价|
    000000 40 9d 20 63 6f 75 6c 64 20 67 65 74 20 74 72 69 63 |。可能会有麻烦|
    000000506b793c2f613e3c2f7468696e673e0a | ky|
    0000005f
    
    你能分享你的代码吗?如果你使用的是记事本+,那么试试“转换为UTF-8”,这会使编码在文件本身中显式显示。NP++使用“ANSI as UTF-8”来表示(可能)是UTF-8,但没有BOM。查看十六进制,
    9d
    =
    10011101
    是UTF-8组合中有效的第三个字节,从
    e
    =
    1110
    开始,您所指的类的465是`if(b1==-1){`不会引发格式不正确的ByteSequenceException.Morover我看不到此类中的任何代码引发此异常。您确定指向了正确版本的lib源代码吗?@necreaux,不太正确,但我将添加堆栈跟踪,直到libs所在的位置called@rossum,我提到我试过了“在记事本++中打开文件并通过其UI将其编码为UTF-8”您阅读了我的全部帖子吗?我提到我尝试了您提到的4个项目中的3个。是的,我阅读了帖子,但如果您只执行了前三个步骤,而没有执行第四个步骤,您仍然可以得到异常,尤其是如果文件
    $ hexdump -C tmp.err.xml
    00000000  3c 3f 78 6d 6c 20 76 65  72 73 69 6f 6e 3d 22 31  |<?xml version="1|
    00000010  2e 30 22 20 65 6e 63 6f  64 69 6e 67 3d 22 55 54  |.0" encoding="UT|
    00000020  46 2d 38 22 3f 3e 0a 3c  74 68 69 6e 67 3e 3c 61  |F-8"?>.<thing><a|
    00000030  3e 54 68 69 73 20 e2 80  ff 71 75 6f 74 65 e2 80  |>This ...quote..|
    00000040  9d 20 63 6f 75 6c 64 20  67 65 74 20 74 72 69 63  |. could get tric|
    00000050  6b 79 3c 2f 61 3e 3c 2f  74 68 69 6e 67 3e 0a     |ky</a></thing>.|
    0000005f