答案 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)))) {
如果要保持一致的行为,则必须使用特定的固定编码对字符串进行编码,而不是依赖于内部表示。