UTF32字符串转换成NSString

要将UTF32Char字符串转换为NSString,使用stringWithCString:encoding:方法,关键是编码方式的选择。

    const char *cstring = [@"你好,世界" cStringUsingEncoding:NSUTF32StringEncoding];
    NSString *string = [NSString stringWithCString:cstring encoding:NSUTF32StringEncoding];

不知道为什么string是nil,放弃;换成NSUTF16StringEncoding也有问题,这个后面说;只有UTF8正常。

 

UTF32转换为UTF8

UTF32使用定长编码,每个unicode码位使用4个字节,UTF8跟下面的UTF16都是不定长编码,分别有自己的格式;要做的就是把UTF32字符串按格式填到UTF8中。
UTF8最少一个字节,最多6个字节,编码规则如下(摘自阮一峰的博客):

 

  1. 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
  2. 对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

 

另外,NSUTF8StringEncoding按大端编码,所以要把后面的字节放在低位。

int convertUTF32to8(UTF8Char *dest, const UTF32Char *orig)
 {
        UTF32Char c;
        int i, len = 0;

        while ((c = *orig++) != '\0') {
            printf("%s %lx\n", __func__, c);
            if (c < 0x80) {
                i = 1;
            } else if (c < 0x800) {
                i = 2;
            } else if (c < 0x10000) {
                i = 3;
            } else if (c < 0x200000) {
                i = 4;
            } else if (c < 0x4000000) {
                i = 5;
            } else {
            i = 6;
        }
        if (i == 1) {
            *dest++ = (UTF8Char)c;
        } else {
            UTF8Char *dp = dest = dest + i;
            printf("===%d===\n", i);
            for (int m = 0; m < i; ++m) {
                *--dp = (UTF8Char)((c | (m == i - 1 ? (~0 << (8 - i)) : 0x80))
                             & (m == i - 1 ? (~(0x80 >> i)) : 0xbf));
                printf("%x ", *dp);
                c >>= 6;
            }
            printf("\n");
        }
        len += i;
    }

    *dest = '\0';
    return len;
}

试一下:

NSString *sample = @"大一二";

const char *cstring = [sample cStringUsingEncoding:NSUTF32StringEncoding];

int len = 0;
UTF32Char c, *cp = (UTF32Char *)cstring;
while ((c = *cp++) != '\0') ++len;

UTF8Char *c8 = malloc(6 * len + sizeof(UTF8Char));
convertUTF32to8(c8, (UTF32Char *)cstring);
NSString *string = [NSString stringWithCString:(const char *)c8 encoding:NSUTF8StringEncoding];

free(c8);

 

UTF32转换成UTF16

UTF16也是不定长编码,unicode把常用的字符放在0x0-0xffff中,所以通常UTF16是两个字节,这时UTF16和UTF32是相等的,可以通过类型转换赋值;对于0x10000和往上的字符,占用4个字节。
unicode定义了0x0-0x10ffff的码位,最高21位。0x10000和往上的字符,去掉最高位然后把剩下的20位分别放在两个UTF16Char中;高、低10位分别加上0xd800和0xdc00。
这样32位的UTF16字符高、低部分的范围分别是0xd800-0xd8ff和0xdc00-0xdcff,而unicode 0x0-0xffff中0xd800-0xdfff之间的码位“永久保留不映射到字符”,所以2字节的UTF16和4字节的UTF16高、低部分永远不会重叠。

int convertUTF32to16(UTF16Char *dest, const UTF32Char *orig)
{
    UTF32Char c;
    int len = 0;

    while ((c = *orig++) != '\0') {
        printf("%s %lx\n", __func__, c);
        if (c < 0x10000) {
            *dest++ = (UTF16Char)c;
            printf("%x ", *(dest - 1));
            ++len;
        } else {
            c -= 0x10000;
            *dest++ = ((UTF16Char)c & 0x3ff) | 0xd800;
            printf("%x ", *(dest - 1));

            *dest++ = ((UTF16Char)(c >> 10)) | 0xdc00;
            printf("%x ", *(dest - 1));

            len += 2;
        }
        printf("\n");
    }
    *dest = '\0';
    return len;
}

试验代码跟UTF8的大致相同,NSUTF16StringEncoding也是大端:

UTF16Char *c16 = malloc(4 * len + sizeof(UTF16Char));
convertUTF32to16(c16, (UTF32Char *)cstring);
NSString *string = [NSString stringWithCString:(const char *)c16    encoding:NSUTF16LittleEndianStringEncoding];

结果string只有”大”字,“一”字的unicode编码是0x4e00,所以怀疑是NSString UTF16编码的BUG,把0x0字节当作结束符而不是(UTF16Char)'\0',“刀”、“匀”等字和所有的ASCII字符也不能正常编码。

参考资料:

字符编码笔记:ASCII,Unicode和UTF-8 - 阮一峰的网络日志
iphone - Converting an NSString to and from UTF32 - Stack Overflow
UTF-8 - 维基百科,自由的百科全书
UTF-16 - 维基百科,自由的百科全书
UTF-32 - 维基百科,自由的百科全书
字体编辑用中日韩汉字Unicode编码表 - 编著:中韩翻译网 金圣镇

你可能感兴趣的:(UTF32字符串转换成NSString)