可靠地自动将用户文件解码为Unicode是否可能?[C#]

8
我有一个网络应用程序,允许用户上传他们的内容进行处理。处理引擎需要UTF8(而且我正在从多个用户的文件中组成XML),因此我需要确保能够正确解码上传的文件。
既然我很惊讶我的用户是否知道他们的文件甚至被编码了,我几乎没有希望他们能够正确地指定要使用的编码器。因此,我的应用程序需要在解码之前检测编码方式。
这似乎是一个普遍存在的问题,我很惊讶没有找到框架功能或通用解决方案。我是否没有使用有意义的搜索词语?
我已经实现了BOM感知检测(http://en.wikipedia.org/wiki/Byte_order_mark),但我不确定有多少文件会被上传而没有BOM来指示编码方式,并且对于大多数非UTF文件来说这并不有用。
我的问题归结为:
  1. 对于绝大多数文件,BOM感知检测是否足够?
  2. 在BOM检测失败的情况下,是否可以尝试不同的解码器并确定它们是否“有效”?(我的尝试表明答案是否定的。)
  3. 在什么情况下,“有效”的文件会在C#编码/解码框架中失败?
  4. 是否有任何存储库具有各种编码的众多文件可用于测试?
  5. 虽然我特别询问C#/.NET,但我想知道Java、Python和其他语言的答案,以备下次需要时使用。

到目前为止,我已经找到:

  • 一份含有Ctrl-S字符的“有效”UTF-16文件导致编码成UTF-8时抛出异常(非法字符?)(那是一个XML编码异常。)
  • 解码一个有效的UTF-16文件,用UTF-8成功,但文本中会带有空字符。嗯?
  • 目前我只期望UTF-8、UTF-16和可能的ISO-8859-1文件,但如果可能的话,我希望解决方案是可扩展的。
  • 我的现有输入文件集不足以揭示所有在实际文件中可能发生的问题。
  • 虽然我正在尝试解码的文件是“文本”,但我认为它们通常是使用留下垃圾字符的方法创建的。因此,“有效”的文件可能不是“纯粹”的。哦,太好了。

谢谢。


你为什么认为UTF-8和UTF-16是兼容的?一个以单字节块存储数据,另一个以2字节块存储... - Matthew Scharley
BOM主要用于微软操作系统,Unix则更喜欢不带BOM的编码。 - Vlad
无论您的格式如何,是否允许使用 Ctrl-S 字符并不取决于它。UTF-8 和 UTF-16 都能够编码 Ctrl-S,只是对于使用所获得的 UTF-8 的软件来说,这个字符可能会出现意外情况。 - Vlad
如果你将一个UTF-16文件“伪装成”UTF-8进行解码,那么每隔一个字节就会得到一个空字符。事实上,字符“0”在UTF-8中被编码为字节“0x30”,但在UTF-16中却是两个字节“0x30 0x00”(即“0x0030”)。 - Vlad
@Matthew:你是在暗示我可以在同一个XML文件中混合使用UTF-8和UTF-16编码的字符串吗?@Vlad - 并不是任何特定的软件出了问题,实际上是C#核心*System.Xml.Linq.XElement.WriteTo(XmlWriter)*方法抛出了异常(尽管代码已经改变,所以我不能在没有大量工作的情况下重现这个错误)。 - NVRAM
显示剩余3条评论
5个回答

3

虽然没有绝对可靠的方法,但是您可以通过一些启发式方法获得“相当好”的结果。

  • 如果数据以BOM开头,请使用它。
  • 如果数据包含0字节,则很可能是utf-16或ucs-32。您可以通过查看0字节的位置来区分这些,并区分这些的big-endian和little-endian变体
  • 如果数据可以无误解码为utf-8,则很可能是utf-8(或US-ASCII,但这是utf-8的子集)
  • 接下来,如果您想走国际化路线,请将浏览器的语言设置映射到该语言最可能的编码。
  • 最后,假设为ISO-8859-1

当然,“相当好”是否足够取决于您的应用程序。如果您需要确保,您可能希望将结果显示为预览,并让用户确认数据是否正确。如果不正确,请尝试下一个可能的编码,直到用户满意为止。

注意:如果数据包含垃圾字符,则此算法将无法正常工作。例如,单个垃圾字节在否则有效的utf-8中会导致utf-8解码失败,从而使算法走上错误的路径。您可能需要采取其他措施来处理此问题。例如,如果您可以预先识别可能的垃圾,请在尝试确定编码之前将其删除。 (如果您过于激进地剥离,也无所谓,一旦确定了编码,您就可以解码原始未剥离的数据,只需将解码器配置为替换无效字符而不是抛出异常即可。)或计算解码错误并适当加权。但这可能很大程度上取决于您的垃圾的性质,即您可以做出什么样的假设。


1
这很有帮助,但请注意,我使用C#/.NET编码框架无异常地解码了一些UTF16LE文件;虽然会出现错误(空字符),但没有异常。我的意图是自动检测(因此发布此帖),我已部分实现了它,因为我已经检测到MSWord、PDF和其他非文本文件,但问题在于确定何时使用正确的编码。 - NVRAM
你说得对,0字节检查需要首先进行,我已经相应地修正了我的回答步骤顺序。 - oefe

2

你是否尝试过从用户那里读取一些文件的代表性交叉部分,通过程序运行它们,进行测试,纠正任何错误并继续前进?

我发现File.ReadAllLines()在非常广泛的应用范围内都很有效,而且不用担心所有编码问题。它似乎处理得很好。

Xmlreader()在我弄清如何正确使用后表现还不错。

也许您可以发布一些具体的数据示例,以获得更好的响应。


谢谢,但我正在寻找一个通用解决方案。在这个应用程序中,应用程序部署在客户的站点上,我无法访问(或获得合法许可)文件。它们是用户希望上传的任何文本文档。有些是PDF转文本,有些是从网站抓取的,有些来自PPT幻灯片,有些……谁知道呢。 - NVRAM
那么我会建议确保在用户本地事件日志中记录有关输入/输出等方面的广泛日志记录。这听起来对我来说是一个无法取胜的局面。 - No Refunds No Returns
顺便说一下,我不明白你所说的“纠正任何错误并继续前进”的意思 - 我不能“纠正”用户的文件,现在我遇到的错误是他们必须正确选择编码格式。我会研究一下File.ReadAllLines()... - NVRAM
File.ReadAllLines() 的编码检测功能是否适用于流? - NVRAM

1

这是一个众所周知的问题。您可以尝试模仿Internet Explorer的做法。在The CodeProject上有一篇很好的文章,描述了微软解决该问题的方法。然而,由于所有的东西都基于启发式算法,没有一种解决方案是100%准确的。同时,也不能安全地假设BOM将存在。


1

你可能会喜欢看一下一个基于Python的解决方案,叫做chardet。它是Mozilla代码的Python移植版。虽然你可能无法直接使用它,但它的文档值得阅读,以及它所引用的原始Mozilla文章也值得一读。


值得一提的是,我使用Mono编译了UDE [http://code.google.com/p/ude/]。然后,我对使用ISO-8859-1、-2、UTF-{8,16LE,16BE,32LE,32BE}编码的文件运行了生成的EXE文件,结果只有UTF-8被正确地识别(对于其他所有编码,它猜测为windows-1255或-1252)。 - NVRAM
它不会识别没有BOM的UTF-nnxE;你的有BOM吗?ISO-8859-n只是想象中的产物--将其解码为Unicode,看看是否有在U+0080到U+009F范围内的字符;-) - John Machin

0

我遇到了类似的问题。我需要一个 PowerShell 脚本,可以确定文件是否是文本编码(任何常见编码)。

这绝对不是详尽无遗的,但这是我的解决方案...

PowerShell 搜索脚本,忽略二进制文件


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