如何从InputStream中删除重音字符

时间:2010-05-18 09:29:13

标签: android xml-parsing diacritics

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

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

feed XML的序言说编码是“utf-8”。当我打开远程流并将其传递给我的Pull Parser时,我得到了无效的令牌,文档没有很好的异常。

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

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

感谢。

5 个答案:

答案 0 :(得分: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肯定会导致兼容的解析器发牢骚。
  4. 如果您可以将文件上传到pastebin或类似文件,我可以帮助查找原因并建议解决方案。

    编辑:好的,您无法上传。这是可以理解的。

    您获得的XML在某种程度上已损坏,理想的操作方法是联系负责生成它的一方,以查看问题是否可以解决。

    在做这件事之前要检查一件事 - 你确定你的数据不受干扰吗?某些形式的通信(SMS)仅允许7位字符。这会将0x92(ASCII正向刻度/撇号 - 严重重音?)转换为0x12。似乎非常巧合,特别是如果它们出现在您希望重音的文件中。

    否则,你必须尽力做到最好:

    1. 虽然不是绝对必要,但要防守并在解析器上将“UTF-8”作为setInput的第二个参数传递。

    2. 类似地,通过传递不同的编码作为第二个参数,强制解析器使用另一个字符编码。尝试添加“UTF-8”的编码是“iso-8859-1”和“UTF-16”。 Sun site上给出了java支持的编码的完整列表 - 您可以尝试所有这些。 (我找不到Android支持的编码的确切列表。)

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

    4. 例如

      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,但同样可以让您获取所需的数据或更轻松地查看问题所在。

答案 1 :(得分: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>

正则表达式不应该很难弄明白。它适用于我,希望它对你有所帮助。

答案 2 :(得分:2)

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

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

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

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


解决方案:

  1. 尝试通过其他方式检测编码
    • 如果您将始终从同一来源读取数据,则可以对某些文件进行采样并使用高级文本编辑器尝试推断文件的实际编码(即notepad++,{{ 3}}等等。)。
    • 以编程方式进行。在进行任何实际的xml处理之前预处理原始字节。
  2. 在XML处理器上强制实际编码
  3. 或者,如果您不介意非ASCII字符(无论是否出现奇怪的符号),您可以直接进入第2步,强制XML处理为任何ASCII兼容的8字节固定长度编码< / strong>(ANSI,任何Windows-XXXX代码页,Mac-Roman编码等)。使用您现有的代码,您可以尝试:

    XmlPullParser parser = Xml.newPullParser();
    parser.setInput(url.open(), "ISO-8859-1");
    

答案 3 :(得分:1)

调用setInput(istream, null)已经意味着pull解析器会尝试自己检测编码。由于文件存在实际问题,它显然失败了。所以它不像你的代码是错误的 - 你不能期望能够解析所有不正确的文件,无论是格式错误还是编码错误。

但是,如果您尝试解析此特定文档是强制性的,那么您可以做的是修改解析代码,使其处于将编码作为参数并包含在try / catch块中的函数中。第一次通过时,不要指定编码,如果遇到编码错误,请使用ISO-8859-1重新启动它。如果必须让它成功,请重复其他编码,否则在两次之后退出。

答案 4 :(得分:0)

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