Objective-C一些特殊的字符无法控制地变化

时间:2018-10-01 19:10:12

标签: ios objective-c

当我在此字符串上使用子字符串时,我有一个包含一些特殊字符的字符串(例如é,â,î,ı等)。我遇到不一致的结果。一些特殊的字符无法控制地更改

enter image description here

2 个答案:

答案 0 :(得分:5)

您假设这些都是个字符

[newword substringWithRange:NSMakeRange(0,1)];    
[newword substringWithRange:NSMakeRange(1,1)];
[newword substringWithRange:NSMakeRange(2,1)];    
[newword substringWithRange:NSMakeRange(3,1)];
// and so on...

换句话说,您相信:

  • location总是落在字符的开头。
  • 角色始终具有length 1。

两个假设都是错误的。请阅读Apple 字符串编程指南here)的“字符和字素簇”一章。

您的é的长度为2,因为它是基本字母e,后跟组合变音符号。如果要使其长度为1,则需要在使用字符串之前 normalize 字符串。调用precomposedStringWithCanonicalMapping并使用结果字符串。

示例和证明(在Swift中,但没关系,因为我始终使用NSString):

let s = "é,â,î,ı" as NSString
let c = s.substring(with: NSRange(location: 0, length: 1)) // e
let s2 = s.precomposedStringWithCanonicalMapping as NSString
let c2 = s2.substring(with: NSRange(location: 0, length: 1)) // é

答案 1 :(得分:2)

您正在将unicode字符串视为字节序列。除了低UTF8之外,Unicode代码点还可以是多字节的,因此您可以通过去除字母上方的重​​音部分来更改文本样式,例如:https://www.compart.com/en/unicode/U+0301

UTF8是可变宽度的,因此通过将其视为原始字节,您可能会得到怪异的结果,我建议使用更了解Unicode的代码,例如ICU(International Components for Unicode)。

现在假设您有一个这样的两个字节序列(这可能不是100%准确,但这说明了我的观点):

0x056 0x000
  e    NUL

现在您有了一个带有1个代码点和一个空终止符的UTF8字符串。现在说您想为该e添加一个重音符号。你会怎么做?您可以使用特殊的Unicode代码点来修改e,因此现在的字符串是:

0x056 0x0CC 0x810 0x000
  e     U+0301     NUL

U+0301是2个字节的控制字符(结合了重音符号),并且使e带有重音符号。


编辑:答案假设使用UTF8编码,这可能是一个错误的假设,但我认为答案(无论是UTF8还是UTF16,还是任何其他带有控制字符的编码类型)都说明了为什么您可能会感到神秘消失的口音。尽管这可能是UTF16,但为了简单起见,我们假装我们生活在一个生活稍微好一些的世界中,因为每个人都只使用UTF8,而UTF16不存在。


要解决此评论(与问题无关,而是一些有趣的琐事),以及有关NS / CF / Swift运行时,桥接和恒定CF字符串等有趣的东西,例如:内存中的实际字符串是实现定义的,并且可以变化(即使是常量字符串,请相信我,我知道,几天前我在Clang中为CoreFoundation修复了它们的ELF实现)。无论如何,这是一些代码:

CF_INLINE CFStringEncoding __CFStringGetSystemEncoding(void) {
    if (__CFDefaultSystemEncoding == kCFStringEncodingInvalidId) (void)CFStringGetSystemEncoding();
    return __CFDefaultSystemEncoding;
}

CFStringEncoding CFStringFileSystemEncoding(void) {
    if (__CFDefaultFileSystemEncoding == kCFStringEncodingInvalidId) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS
        __CFDefaultFileSystemEncoding = kCFStringEncodingUTF8;
#else
        __CFDefaultFileSystemEncoding = CFStringGetSystemEncoding();
#endif
    }
    return __CFDefaultFileSystemEncoding;
}

贯穿整个CoreFoundation / Foundation / SwiftFoundation(是的,您永远不知道实际上持有的是哪种NSString,它们通常假装是同一件事,但取决于您如何获取可能持有的对象到它的三个变体之一上。

这就是为什么存在这样的代码的原因,因为NS / CF(Constant)/ Swift字符串具有实现定义的内部表示形式。

if (((encoding & 0x0FFF) == kCFStringEncodingUnicode) && ((encoding == kCFStringEncodingUnicode) || ((encoding > kCFStringEncodingUTF8) && (encoding <= kCFStringEncodingUTF32LE)))) {

如果要保持一致的行为,则必须使用特定的固定编码对字符串进行编码,而不是依赖于内部表示。