我有UTF-8编码,但仍然出现“1字节UTF-8序列的无效字节1”的错误。

6

我动态创建了一个XML字符串(而不是从文件中读取)。然后我使用Cocoon 3通过FOP将其转换为PDF。在中间某个地方Xerces运行。当我使用硬编码的内容时,一切正常。但是,一旦我将德语Umlaut放入数据库并用该数据丰富我的XML,就会出现以下错误:

Caused by: org.apache.cocoon.pipeline.ProcessingException: Can't parse the XML string.
at org.apache.cocoon.sax.component.XMLGenerator$StringGenerator.execute(XMLGenerator.java:326)
at org.apache.cocoon.sax.component.XMLGenerator.execute(XMLGenerator.java:104)
at org.apache.cocoon.pipeline.AbstractPipeline.invokeStarter(AbstractPipeline.java:146)
at org.apache.cocoon.pipeline.AbstractPipeline.execute(AbstractPipeline.java:76)
at de.grobmeier.tab.webapp.modules.documents.InvoicePipeline.generateInvoice(InvoicePipeline.java:74)
... 87 more

Caused by: 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:684)
    at com.sun.org.apache.xerces.internal.impl.io.UTF8Reader.read(UTF8Reader.java:554)

我已经调试了我的应用程序并发现,我的“Ä”(来自数据库)的字节值为196,即十六进制中的C4。根据这个网站:http://www.utf8-zeichentabelle.de/,这正是我所期望的。
我不知道为什么我的代码失败了。
然后我尝试手动添加 BOM,像这样:
byte[] bom = new byte[3];
bom[0] = (byte) 0xEF;
bom[1] = (byte) 0xBB;
bom[2] = (byte) 0xBF;
String myString = new String(bom) + inputString;

我知道这并不是特别好的方法,但我尝试了一下 — 当然失败了。我试图在前面添加一个XML头:

<?xml version="1.0" encoding="UTF-8"?>

之前我尝试了一个方法,但是失败了。然后我把它和另一个方法结合起来,仍然失败。

最后我尝试了这样一种方法:

xmlInput = new String(xmlInput.getBytes("UTF8"), "UTF8");

实际上这并没有做任何事情,因为它已经是UTF-8格式了,但仍然失败了。

所以...你有什么想法我做错了什么,Xerces从我这里期望什么呢?

谢谢,Christian


我使用MySQL,表格和列都是以utf8_general_ci编码。我已经在我的jdbc连接中添加了useUnicode=true&characterEncoding=utf8。 - Christian
当您连接到具有不同编码的数据库时,指定JDBC连接上的这些参数可能不是一个好主意 - 只有在自动检测出现问题时才使用它。您用什么来写入数据,这是BLOB还是VARCHAR列? - JBert
我的工具并不是那么通用,它只会连接到我指定编码的数据库。此外,如果不指定编码,我实际上遇到了问题。我写入的数据来自一个网页,该网页本身采用UTF-8编码:<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>。然后它直接进入Struts。 - Christian
太好了!我已经将你的发现添加到我的答案中,并进行了更多的侦查工作,以便你可以关闭这个问题。 - JBert
太好了,你找到了Cocoon的问题。因此你将获得“感谢”的标志。我会把你的帖子转发给我的Cocoon朋友们。干杯! - Christian
显示剩余2条评论
3个回答

13
如果你的数据库只包含一个单字节(值为0xC4),那么你没有使用UTF-8编码。 "LATIN CAPITAL LETTER A WITH DIAERESIS"字符的码点值为U+00C4,但UTF-8无法用单个字节对其进行编码。如果你查看UTF8-zeichentabelle.de上的第三列“UTF-8(hex.)”,你会发现UTF-8将其编码为0xC3 84(两个字节)。请阅读Joel的文章“The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)”获取更多信息。

编辑:Christian自己找到了答案;结果是Cocoon 3 SAX组件中出现了问题(我猜是alpha 3版本)。原来,如果将XML作为字符串传递给XMLGenerator类,则在SAX解析期间会出现错误,导致出现这种混乱。

查看了代码,找到了Cocoon-stax中的实际问题:

if (XMLGenerator.this.logger.isDebugEnabled()) {
    XMLGenerator.this.logger.debug("Using a string to produce SAX events.");
}
XMLUtils.toSax(new ByteArrayInputStream(this.xmlString.getBytes()), XMLGenerator.this.getSAXConsumer();

如您所见,调用getBytes()将创建一个具有JRE默认编码的Byte数组,这将导致解析失败。这是因为XML声明自己为UTF-8,而数据现在又变成了字节,并且可能使用您的Windows代码页。

作为解决方法,可以使用以下方法:

new org.apache.cocoon.sax.component.XMLGenerator(xmlInput.getBytes("UTF-8"),
       "UTF-8");

这将触发正确的内部动作(正如Christian通过API实验所发现的)。
我已在Apache的错误跟踪器中打开了一个问题
编辑2:该问题已得到解决,并将包含在即将发布的版本中。

超出预期的好表现! - Pops

2
您在那个页面上看到的C4是指Unicode代码点U+00C4。用于表示UTF-8中此类代码点的字节序列不是"\xC4"。您需要的是UTF-8(十六进制)列中的内容,即"\xC3\x84"
因此,您的数据不是UTF-8格式。
您可以在这里阅读有关如何在UTF-8中编码数据的详细信息:这里

0

我在Windows 7上使用TextPad作为文本编辑器手动构建xml数据文件。我遇到了MalformedByteSequenceException的问题。在xml文件中,我的规范是UTF-8。经过搜索,我发现我的编辑器有一个名为“工具…转换为DOS”的工具。我使用它进行了转换,重新保存了文件,然后异常消失了,我的代码也正常运行了。

然后,我查看了我的编辑器中该文件类型的默认编码。它是ASCII,但当我将xml编码参数更改为ASCII时,我得到了另一个不同的MalformedByteSequenceException异常。

因此,在Windows系统上,您可以尝试将xml编码保留为UTF-8,但将文件编码为DOS格式保存。我没有深入研究这种方法为什么有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接