从NSData创建NSString时猜测编码

19

从文件读取NSString时,可以使用initWithContentsOfFile:usedEncoding:error:方法来猜测文件的编码。

但是,如果我是从NSData创建字符串,那么我只能使用initWithData:encoding:方法,需要显式指定编码。在使用NSData而不是文件时,如何可靠地猜测编码?

2个回答

30
在iOS 8和OS X 10.10中,NSString上有一个新的API:

Objective-C

+ (NSStringEncoding)stringEncodingForData:(NSData *)data
                          encodingOptions:(NSDictionary *)opts
                          convertedString:(NSString **)string
                      usedLossyConversion:(BOOL *)usedLossyConversion;

Swift

open class func stringEncoding(for data: Data,
                   encodingOptions opts: [StringEncodingDetectionOptionsKey : Any]? = nil, 
                 convertedString string: AutoreleasingUnsafeMutablePointer<NSString?>?, 
                    usedLossyConversion: UnsafeMutablePointer<ObjCBool>?) -> UInt

现在你可以让这个框架猜测,根据我的经验那通常是很有效的!

从标题中可以看出(文档目前没有说明方法,但它在WWDC Session 204 (page 270)中被正式提到):

 
     
  1. 推荐的字符串编码数组(如果不指定列表中的第3个选项,则将考虑所有字符串编码,但数组中的编码会优先考虑;此外,数组中编码的顺序很重要:第一个编码比数组中的第二个编码更受欢迎)
  2.  
  3. 不使用的字符串编码数组(该列表中的字符串编码将根本不予考虑)
  4.  
  5. 一个布尔选项,指示是否仅考虑建议的字符串编码
  6.  
  7. 一个布尔选项,指示是否允许损失
  8.  
  9. 给定要替换为神秘字节的特定字符串的选项
  10.  
  11. 当前用户的语言
  12.  
  13. 一个布尔选项,指示数据是否由Windows生成
  14.  
 

如果字典中的值类型错误(例如,NSStringEncodingDetectionSuggestedEncodingsKey的值不是数组),则会抛出异常。

 

如果字典中的值未知(例如,建议的字符串编码数组中的值不是有效编码),则这些值将被忽略。

示例(Swift):

var convertedString: NSString?
let encoding = NSString.stringEncoding(for: data, encodingOptions: nil, convertedString: &convertedString, usedLossyConversion: nil)

如果您只需要解码后的字符串并不关心编码,可以删除let encoding =


似乎有一个原因,说明它还不是官方的。我用 PDF 的 NSData 编码来运行它,返回值是 -2147482362。 - FireDragonMule
我不太确定它是否按预期工作。PDF 不是字符串,而此方法从 NSData 中查找字符串的编码方式。你的意图是什么? - HAS
我正在通过SDK将PDF作为NSData检索。目前我遇到的问题是在Webview中显示它,因为我不知道编码是什么,或者是否存在编码。 - FireDragonMule
你看过这个答案了吗?如果那个方法不行的话,我建议你在SO上提出自己的问题,并提供更多关于你尝试过什么以及你的代码是什么以及哪里出了问题的细节。 :) - HAS
1
谢谢。我已经修复了。原来我获取的是非PDF数据。 - FireDragonMule
对我来说,它在中文、阿拉伯语、法语、土耳其语等方面都表现出色。 - Roen

12
一般而言,你不能确定一个文件的编码方式。但是,如果一个文件是有效的UTF-8格式,那么在很大程度上,它不太可能采用其他编码方式(除非所有的字节都在ASCII范围内,在这种情况下,任何“扩展ASCII”编码方式,包括UTF-8,都会给你相同的结果)。所有的Unicode编码方式也都有一个可选的BOM(字节顺序标记)来确定它们的编码方式。因此,一个合理的方法是:

  • 查找有效的BOM。如果存在,则使用适当的编码方式。
  • 否则,尝试将其解释为UTF-8格式。你可以通过调用initWithData:data encoding:NSUTF8StringEncoding并检查结果是否为非nil来实现此目的。
  • 如果失败,使用默认的8位编码方式,例如-[NSString defaultCStringEncoding](提供一个与本地环境相符的猜测)。

在最后一步中,可以尝试通过尝试各种不同的编码方式,并选择其中具有最少字符序列junk(即任何不是字母、空格或常见标点符号的字符)的一个来改善猜测。这将显著增加复杂度,而实际上并不可靠。

简而言之,要能够处理所有可用的编码方式,你需要像TextEdit那样:把决定权交给用户。

哦,还有一件事:从10.5开始,编码方式通常与文件一起存储在未记录的com.apple.TextEncoding扩展属性中。如果你使用+[NSString stringWithContentsOfFile:]或类似方法打开一个文件,则如果存在该属性,则会自动使用它。


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