5

我正在尝试使用 Pull 解析器解析 Android 上的 Rss2.0 提要。

XmlPullParser parser = Xml.newPullParser();
parser.setInput(url.open(), null);

提要 XML 的序言说编码是“utf-8”。当我打开远程流并将其传递给我的 Pull Parser 时,我得到了无效的令牌,记录了格式不正确的异常。

当我保存 XML 文件并在浏览器(FireFox)中打开它时,浏览器报告文件中存在 Unicode 0x12 字符(重音?)并且无法呈现 XML。

假设我对返回的 XML 没有任何控制权,那么处理这种情况的最佳方法是什么?

谢谢。

4

5 回答 5

6

你在哪里发现 0x12 是重音?UTF-8 的字符范围为 0x00-0x7F,编码与 ASCII 相同,ASCII 码点 0x12 是控制字符、DC2 或 CTRL+R。

这听起来像是某种编码问题。解决这个问题的最简单方法是查看您在十六进制编辑器中保存的文件。有一些事情需要检查:

  1. 开头的字节顺序标记 (BOM) 可能会混淆一些 XML 解析器
  2. 即使 XML 声明说编码是 UTF-8,它实际上也可能没有这种编码,并且文件将被错误地解码。
  3. 并非所有 unicode 字符在 XML 中都是合法的,这就是 firefox 拒绝呈现它的原因。特别是,XML 规范说 0x9、0xA 和 0xD 是唯一小于 0x20 的有效字符,所以 0x12 肯定会导致兼容的解析器抱怨。

如果您可以将文件上传到 pastebin 或类似文件,我可以帮助查找原因并提出解决方案。

编辑:好的,你不能上传。这是可以理解的。

您得到的 XML 不知何故损坏了,理想的做法是联系负责生成它的一方,看看是否可以解决问题。

不过,在这样做之前要检查一件事 - 你确定你的数据不受干扰吗?某些形式的通信 (SMS)只允许使用 7 位字符。这会将 0x92(ASCII 正向刻度/撇号 - 重音符?)变成 0x12。似乎很巧合,特别是如果这些出现在您期望重音的文件中。

否则,您将不得不尽力利用您所拥有的:

  1. 尽管不是绝对必要的,但请保持防御并将“UTF-8”作为第二个参数传递给setInput解析器上的 , 。

  2. 同样,通过传递不同的编码作为第二个参数,强制解析器使用另一种字符编码。除了“UTF-8”之外,要尝试的编码是“iso-8859-1”和“UTF-16”。Sun 站点上提供了支持的 Java 编码的完整列表- 您可以尝试所有这些。(我找不到 Android 支持的编码的明确列表。)

  3. 作为最后的手段,您可以删除无效字符,例如删除 0x20 以下的所有非空白字符(0x9,0xA 和 0xD 都是空白字符)。如果删除它们很困难,您可以替换它们。

例如

class ReplacingInputStream extends FilterInputStream
{
   public int read() throws IOException
   {
      int read = super.read();
      if (read!=-1 && read<0x20 && !(read==0x9 || read==0xA || read==0xB))
         read = 0x20;
      return read;          
   }
}

您将其包装在现有的输入流中,它会过滤掉无效字符。请注意,您可能很容易对 XML 造成更大的破坏,或者以无意义的 XML 告终,但同样可以让您获取所需的数据或更容易地找出问题所在。

于 2010-05-24T20:28:15.343 回答
2

我用正则表达式过滤它,但诀窍不是试图获取和替换重音。这取决于编码,您不想更改内容。

尝试将标签的内容插入到这个标签中

像这样

<title>My title</title>
<link>http://mylink.com</link>
<description>My description</description>

对此

<title><![CDATA[My title]]></title>
<link><![CDATA[http://milynk.com]]></link>
<description><![CDATA[My Description]]></description>

正则表达式不应该很难弄清楚。它对我有用,希望对你有帮助。

于 2010-05-24T04:44:54.253 回答
2

UTF-8 的问题在于它是一种多字节编码。因此,它需要一种方法来指示字符何时由一个以上的字节(可能是两个、三个、四个......)组成。这样做的方法是保留一些字节值来表示多字节字符。因此编码遵循一些基本规则:

  • 一字节字符没有设置 MSB(与 7 位 ASCII 兼容的代码)。
  • 两个字节字符用序列表示:110xxxxxx 10xxxxxx
  • 三个字节:1110xxxx 10xxxxxx 10xxxxxx
  • 四个字节:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

您的问题是您可能正在读取一些据称编码为 UTF-8 的字符串(如 XML 编码定义所述),但字节块可能并未真正以 UTF-8 编码(将某些内容声明为 UTF-8 是一个常见错误) 8,但使用不同的编码(例如 Cp1252)对文本进行编码。您的 XML 解析器尝试将字节块解释为 UTF-8 字符,但发现不符合编码规则的内容(非法字符)。即设置两个最重要字节的两个字节会带来非法编码错误:110xxxxxx 必须始终跟在 10xxxxxx 之后(诸如 01xxxxxx 11xxxxxx 00xxxxxx 之类的值是非法的)。

当使用非可变长度编码时,不会出现此问题。即,如果您在 XML 声明中声明您的文件使用 Windows-1252 编码但最终使用 ANSI,那么您唯一的问题将是非 ASCII 字符(值 > 127)将无法正确呈现。


解决方案:

  1. 尝试通过其他方式检测编码
    • 如果您总是从同一来源读取数据,您可以对一些文件进行采样并使用高级文本编辑器来尝试推断文件的实际编码(即notepad++jEdit等)。
    • 以编程方式进行。在进行任何实际的 xml 处理之前预处理原始字节。
  2. 在 XML 处理器上强制进行实际编码

或者,如果您不介意非 ASCII 字符(无论是否偶尔出现奇怪的符号),您可以直接进入第 2 步并强制 XML 处理为任何 ASCII 兼容的 8 字节固定长度编码(ANSI、任何 Windows-XXXX代码页、Mac-Roman 编码等)。使用您当前的代码,您可以尝试:

XmlPullParser parser = Xml.newPullParser();
parser.setInput(url.open(), "ISO-8859-1");
于 2010-05-27T13:39:45.703 回答
1

调用setInput(istream, null)已经意味着拉解析器尝试自行检测编码。由于文件存在实际问题,它显然失败了。因此,您的代码并不是错误的——您不能期望能够解析所有不正确的文档,无论是格式错误还是编码错误。

If however it's mandatory that you try to parse this particular document, what you can do is modify your parsing code so it's in a function that takes the encoding as a parameter and is wrapped in a try/catch block. The first time through, do not specify an encoding, and if you get an encoding error, relaunch it with ISO-8859-1. If it's mandatory to have it succeed, repeat for other encodings, otherwise call it quits after two.

于 2010-05-28T13:40:48.317 回答
0

在解析 XML 之前,您可以对其进行调整,并在解析之前手动删除重音符号。到目前为止,也许不是最好的解决方案,但它会完成这项工作。

于 2010-05-18T10:22:58.490 回答