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

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

提问人:rpatters1 提问时间:10/21/2021 更新时间:10/21/2021 访问量:95

问:

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

特别是,(有时)我想先尝试编码为似乎永远不会失败。相反,它只是对 gobbledygook 进行编码。我可以提前进行某种检查吗?(喜欢,但在另一个方向?NSMacOSRomanStringEncodingcanBeConvertedToEncoding

Objective-C macOS Cocoa nsstring

评论


答:

1赞 Itai Ferber 10/21/2021 #1

与其逐个尝试编码,直到找到匹配项,不如考虑使用 +[NSString stringEncodingForData:encodingOptions:convertedString:usedLossyConversion:],在给定字符串数据和一些选项的情况下,它可能能够为您检测编码,并返回它(以及实际解码的字符串)。NSString

特别是对于您的用例,由于您有一个要尝试的编码列表,因此该参数将允许您使用 NSStringEncodingDetectionSuggestedEncodingsKey 传递这些编码。encodingOptions

因此,给定一个 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


关于您传入的编码数组,需要注意的一点是:尽管文档不承诺按顺序尝试建议的编码,但通过(当前)方法实现的反汇编表明,数组是使用快速枚举(即按顺序)枚举的。我可以想象这将来可能会改变(或者过去会有所不同),所以如果这对你来说是一个硬性要求,理论上你可以通过一次重复调用一个编码来解决这个问题,但考虑到这种方法的复杂性,这可能会非常昂贵。+stringEncodingForData:encodingOptions:convertedString:usedLossyConversion:

评论

0赞 rpatters1 10/21/2021
谢谢!这看起来很有希望。如果我能让它工作,我会把它标记为已解决。
0赞 Itai Ferber 10/21/2021
@rpatters1 请注意,字符串编码检测非常困难,并且本质上至少具有轻微的概率性,因此结果可能并不完美,但我希望这适用于您的用例。
0赞 rpatters1 10/21/2021
它会起作用的。不过,有些奇怪。我可以在 MacOSRoman 中对字符串进行编码(没有错误),然后将其发送回此例程,它完美地编码了 NSString(使用 MacOSRoman 编码),但无论如何都设置了 usedLossyConversion 布尔值。我把这当成一个错误,但现在我想也许我不应该这样做。我想知道除了返回的字符串为零之外,我是否应该检查其他错误条件。
0赞 Itai Ferber 10/21/2021
@rpatters1 根据一些粗略的测试,我认为这是一个错误。在我看来,结果似乎受到是否尝试了多个编码的影响,即使在成功的情况下也是如此。例如,尝试所有 256 个 Mac OS 罗马代码点,如果我以独占方式传入并设置 ,则不会报告有损转换,但如果其中任何一个不正确,它都会报告有损转换,即使在成功时也是如此。usedLossyConversionNSMacOSRomanStringEncodingNSStringEncodingDetectionUseOnlySuggestedEncodingsKey: YES
1赞 rpatters1 10/21/2021
感谢您的宝贵建议。目前,我仍在检查,但如果它成为一个问题,则将字符串带回来是一个可能的解决方案。resultnil