如何发现是否可以使用给定的编码将 c 字符串编码为 NSString

How to discover if a c-string can be encoded to NSString with a given encoding

我正在尝试实现将 const char * 转换为 NSString 的代码。我想按指定的顺序尝试多种编码,直到找到一种有效的编码。不幸的是,NSString 上的所有 initWith... 方法都表示如果编码不起作用,结果是未定义的。

特别是,(有时)我想先尝试编码为 NSMacOSRomanStringEncoding,这似乎永远不会失败。相反,它只是编码 gobbledygook。我可以提前进行某种检查吗? (像 canBeConvertedToEncoding 但方向相反?)

与其一一尝试编码直到找到匹配项,不如考虑让 NSString 通过使用 +[NSString stringEncodingForData:encodingOptions:convertedString:usedLossyConversion:] 来帮助您解决问题,给定字符串数据和一些选项,这可能能够为您检测编码,然后 return 它(连同实际解码的字符串)。

特别针对您的用例,因为您有想要尝试的编码列表,encodingOptions 参数将允许您使用 NSStringEncodingDetectionSuggestedEncodingsKey 传递这些编码。

因此,给定一个 C 字符串和一些可能的编码选项,您可以执行如下操作:

NSString *decodeCString(const char *source, NSArray<NSNumber *> *encodings) {
    NSData * const cStringData = [NSData dataWithBytesNoCopy:(void *)source length:strlen(source) freeWhenDone:NO];
    
    NSString *result = nil;
    BOOL usedLossyConversion = NO;
    NSStringEncoding determinedEncoding = [NSString stringEncodingForData:cStringData
                                                          encodingOptions:@{NSStringEncodingDetectionSuggestedEncodingsKey: encodings,
                                                                            NSStringEncodingDetectionUseOnlySuggestedEncodingsKey: @YES}
                                                          convertedString:&result
                                                      usedLossyConversion:&usedLossyConversion];
    
    /* Decide whether to do anything with `usedLossyConversion` and `determinedEncoding. */
    return result;
}

用法示例:

NSString *result = decodeCString("Hello, world!", @[@(NSShiftJISStringEncoding), @(NSMacOSRomanStringEncoding), @(NSASCIIStringEncoding)]);
NSLog(@"%@", result); // => "Hello, world!"

如果您不是 100% 关心使用 您想要尝试的编码列表,您可以删除 NSStringEncodingDetectionUseOnlySuggestedEncodingsKey 选项。


关于您传入的编码数组需要注意的一件事:虽然文档不保证按顺序尝试建议的编码,但通过 (current) 方法实现显示数组是使用快速枚举(即按顺序)枚举的。我可以想象这在未来可能会改变(或者过去有所不同)所以如果这对你来说是一个硬性要求,你理论上可以通过重复调用 +stringEncodingForData:encodingOptions:convertedString:usedLossyConversion: 一次编码来解决它订单,但鉴于此方法的复杂性,这可能会非常昂贵。