iOS - NSString 与 Emoji

背景

    前些阵子遇到一个需求,一个文本剪辑器,发布时需要统计除 Emoji 外的字数,于是求助度娘,以 ”NSString 是否包含 Emoji“ 为关键字找到不同的文章都给出了一模一样的解决方案,但实际测试中,这个方案随着 iOS 版本的更迭早已不再适用

解决方案

    在这篇文章里面: iOS - 判断 NSString 字符串是否包含 Emoji

原理

    想做 Emoji 判断首先需要要有两个前置的知识储备,Unicode 编码方式和原理,以及 NSString 和 Unicode 的关系。

Unicode

    限于篇幅,关于 Unicode 的简介和发展历程可以参考我的这篇文章:Unicode 简介与发展历程,里面有着详细的 UTF 编码原理介绍。

    此外,网络上面也有着许多和 Unicode 相关的大佬们的文章可以阅读。Unicode 官网里面更有着非常详细的介绍和细节。

NSString 与 UTF

    在了解了 Unicode 和 UTF 编码原理之后,只需要再了解 NSString 使用了那种编码方式就很容易找出解决方案。而 NSString 的编码方式在 NSString.h 的头文件中有明确的说明:

/* The unichar type represents a single UTF-16 code unit in an NSString. Although many human-readable characters are representable with a single unichar, some  such as Emoji may span multiple unichars. See discussion above.
*/
typedef unsigned short unichar;

    在明确了 NSString 使用 UTF-16 的编码方式之后,结合 UTF-16 的编码原理,可以得到 NSString 和码点之间的互相的转换关系如下:

/*
 Nsstring 转换为码点
 */
- (int)stringToCodePoint:(NSString *)string {
    const unichar hs = [string characterAtIndex:0];
    if (0xd800 <= hs && hs <= 0xdbff) {
        if (string.length > 1) {
            const unichar ls = [string characterAtIndex:1];
            const int codepoint = (((int)hs - 0xd800) * 0x400) + ((int)ls - 0xdc00) + 0x10000;
            return codepoint;
        } else {
            return 0;
        }
    } else {
        return (int)hs;
    }
}

/*
 码点转换为 NSString
 */
- (NSString *)stringFromCodePoint:(int)codePoint {
    if (codePoint <= 0xFFFF)
    {
        return [[NSString alloc] initWithBytes:&codePoint length:sizeof(codePoint) encoding:NSUTF8StringEncoding];
    }
    else if (0x10000 <= codePoint && codePoint <= 0x10FFFF)
    {
        int symbol = ((((0x808080F0 | (codePoint & 0x3F000) >> 4) | (codePoint & 0xFC0) << 10) | (codePoint & 0x1C0000) << 18) | (codePoint & 0x3F) << 24);
        return [[NSString alloc] initWithBytes:&symbol length:sizeof(symbol) encoding:NSUTF8StringEncoding];
    }
    else
    {
        return nil;
    }
}

    了解了 NSString 和 Unicode 码点之间的转换关系,就可以理解上面解决方案里面提供的方案。

题外话

    实际上,Emoji 的编码方式远比我们想象的要复杂得多,尽管上述的解决方案可以解决问题,但是在做码点和 NSString 的互相转化过程中,发现 Emoji 除了有单码点表示的方法之外,还有多码点组合字符的形式。

    这篇文章里面会详细的介绍 Unicode 与 Emoji 的组合字:Unicode 和 Emoji 中的组合字符

你可能感兴趣的:(iOS - NSString 与 Emoji)