OC字符串NSString类的模拟封装-length和CharacterAtIndex:方法

      这两个方法一个是返回字符串的字符个数,一个是返回下标对应的字符,但是并没有想象中的那么简单,甚至可以说比init方法难得多,因为OC中的字符串大多包含有中文,而中文在内存中的字节数并不固定(UTF8大多是3个字节).这两个方法都要对字符是否是ascii字符做判断,这就要了解UTF8在内存的存储方式.
一   UTF8存储规则
        UTF8是Unicode编码的实现之一,相似的还有UTF16,UTF32等等.Unicode编码是为了解决ascii不能表示世界所有国家字符的设计的,它的编码统一是2个字节,如果是ascii字符则会加上一个0字节.但是用它来编码那么内存中的ascii字符都要用两个字节,一个英文文档大小要加倍,所以就有了UTF8来解决这一总是.
        UTF8编码是一种变长的编码方式,用1到4个字节存储1个字符,ascii字符依然用1个字节存储,非ascii字符就将Unicode中的编码通过转码规则存储.
具体转码规则,可以百度,大体是 一个n字节的Unicode字符,转换成UTF8从右向左存放,一个字节存放6位,高的两位是10,最高位的字节前n位为1,第n+1们为0  ,最高位的字节1的个数最少是2(2个字节),最多是4(4个字节),ascii字符是0.
        所以一个UTF8字符串,如果某个字节的最高为0,说明这个字节存放的是ascii字符,如果某个字节的高位 连续的1的个数不为0,那1的个数就是这个字符存储的字节数
        注:下面的两个方法实现代码只适用于大端存储格式(低地址存放高字节)
 
二   length
        了解了上面的UTF8编码规则写一个获取字符串字符个数的方法就清晰明了了.

// 判断是否是Unicode字符(ascii字符),参数2为存储的字节数,适用于大端存储格式

- (BOOL)isUnicode:(const char)c andByteCount:(NSUInteger *)byteCount

{

    int i = 0;

    for (i = 7; (c >> i) & 1; i--); 

    if (i == 7) { // ascii 字符

        *byteCount = 1;

        return NO;

    }

    *byteCount = 7 - i; // 字节数

    return YES;

}


// 字符串的长度

- (NSUInteger)length

{

    char *p = (char*)_string;

    NSUInteger count = 0// 当前字符个数

    NSUInteger byteCount = 0// 当前字符的字节数

    while (*p) // 默认是从字符的高地址读起

    {

       

        //int i = 0;

        //for (i = 7; (*p >> i) & 1; i--); // 查找当前字节(是否有UTF-8的非ascii字符)

        BOOL isUnicodeChar = [self isUnicode:*p andByteCount:&byteCount];

         // 当前字符为 字母

        if (/*i == 7*/isUnicodeChar == NO)

        {

            count++; // 字符个数加1

            p++; // 指向下一个字节

        } else if (/*i < 7*/isUnicodeChar == YES// 当前字符为 汉字(或非ascio字符)

        {

            count++; // 字符数加1

            p += /*(7 - i)*/ byteCount; // 指向下一个字符(跳过7 - i个字节)

        }

        

    }

    

    return count;

}      
二      CharacterAtIndex:
      这个方法实现要先通过一个循环找到第index个字符的首地址,再将这个字符的UTF8编码转换成Unocode编码,转码算法是根据首字节(高字节,大端格式)中1的个数进行,如果没有1,说明是ascii字符直接返回该字节的值,如果有n个1,就从第0位依次读取到第 8 - n - 1(-1是因为连续的1后面有个0不用读取)位,此后的 n-1 个字节都从 第 位读取到 第 5 位共 6 位,
将这些读取到的 位组成的二进制数 转换成十进制数就是Unicode码 

  
- ( unichar )charActerAtIndex:( NSUInteger )index

{

    char *p = (char *)_string;

    unichar num = 0// 要返回的ascii值或unicode

    NSUInteger newIndex = 0// 当前下标

    //NSUInteger bit = 0; // 表示位数

    NSUInteger byteCount = 0// 字节数

    BOOL isUnicodeChar; // 是否是ascii字符

    while (newIndex != index) // 找到第index个字符(可能包含非ascii字符)

    {

        //for (bit = 7; (*p >> bit) & 1 ; bit--); // 判断当前字节是什么字符

        isUnicodeChar = [self isUnicode:*p andByteCount:&byteCount];

        if (/*bit == 7*/ isUnicodeChar == NO) { // ascii字符

            p++;

            newIndex++;

        }

        else if (/*bit < 7*/ isUnicodeChar == YES// 不是ascii字符

        {

            p += /*7 - bit*/ byteCount; // 跳过 7 - bit 个字节数

            newIndex++;

        }

    }

    // 此时p已经指向了第index个字符的首地址
 

// 找到当前字符的ascii值或unicode

    //for (bit = 7; (*p >> bit) & 1 ; bit--);

    isUnicodeChar = [self isUnicode:*p andByteCount:&byteCount];

    if (/*bit == 7*/ isUnicodeChar == NO)

    {

        num = *p;

    } else if (/*bit < 7*/ isUnicodeChar == YES)

    {

        NSUInteger Byte = /*7 - bit*/ byteCount; // 字节数

        NSUInteger i = 8 - /*(7 - bit)*/ byteCount - 1// 首个字节(最高字节)

        NSUInteger d = 16;

        while (Byte)

        {

            for (int j = (int)i; j >= 0; j--)

            {

                num += ((*p >> j) & 1) * pow(2, d--);

            }

            i = 5;

            Byte--;

            p++;

        }

    }

    

    return num;

}

 OC字符串NSString类的模拟封装-length和CharacterAtIndex:方法_第1张图片

你可能感兴趣的:(学习笔记)