美文网首页OC进化iOS DeveloperiOS技术交流
NSData转NSString返回nil的终极解决方法

NSData转NSString返回nil的终极解决方法

作者: andforce | 来源:发表于2016-11-28 14:56 被阅读3804次

    iOS开发中,比较蛋疼的一个问题,NSData转UTF-8格式的NSString,有时候会返回nil。

    究其原因,无非就是:UTF-8的字符中混进了其他编码格式的字符,这样NSData转NSString的时候,就是返回nil

    而现在网上的方法基本就这几个:

    http://blog.csdn.net/cuibo1123/article/details/40938225
    http://blog.csdn.net/xocom/article/details/50905578
    http://www.cnblogs.com/xiao-love-meng/p/5757564.html

    这几个方法都不完美,都不能完全解决我遇到的问题。

    我的解决方法:

    查了UTF-8的wiki,utf-8的编码格式如下,理论上可以到6个字节,但之用到了4个字节。

    utf-8的编码规则

    其中,有一些特殊字节是不会出现在utf-8中的:


    utf-8中不会出现的字节

    其中,还指出了判断utf-8的方法:

    UTF-8字符串可以由一个简单的算法可靠地识别出来。就是,一个字符串在任何其它编码中表现为合法的UTF-8的可能性很低,并随字符串长度增长而减小。举例说,字符值C0,C1,F5至FF从来没有出现。为了更好的可靠性,可以使用正则表达式来统计非法过长和替代值(可以查看W3 FAQ: Multilingual Forms上的验证UTF-8字符串的正则表达式)。

    $field =~
      m/\A(
         [\x09\x0A\x0D\x20-\x7E]            # ASCII
       | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
       |  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
       | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
       |  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
       |  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
       | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
       |  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
      )*\z/x;
    

    因此,我们只要把上面的代码,转换成OC语言就行了:
    这里我直接写了一个Category:

    //
    // Created by WDY on 2016/11/24.
    // Copyright (c) 2016 andforce. All rights reserved.
    //
    
    #import "NSData+UTF8.h"
    
    @implementation NSData (UTF8)
    
    - (NSString *)utf8String {
        NSString *string = [[NSString alloc] initWithData:self encoding:NSUTF8StringEncoding];
        if (string == nil) {
            string = [[NSString alloc] initWithData:[self UTF8Data] encoding:NSUTF8StringEncoding];
        }
        return string;
    }
    
    //              https://zh.wikipedia.org/wiki/UTF-8
    //              https://www.w3.org/International/questions/qa-forms-utf-8
    //
    //            $field =~
    //                    m/\A(
    //            [\x09\x0A\x0D\x20-\x7E]            # ASCII
    //            | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
    //            |  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
    //            | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
    //            |  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
    //            |  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
    //            | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
    //            |  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
    //            )*\z/x;
    
    - (NSData *)UTF8Data {
        //保存结果
        NSMutableData *resData = [[NSMutableData alloc] initWithCapacity:self.length];
    
        NSData *replacement = [@"�" dataUsingEncoding:NSUTF8StringEncoding];
    
        uint64_t index = 0;
        const uint8_t *bytes = self.bytes;
    
        long dataLength = (long) self.length;
    
        while (index < dataLength) {
            uint8_t len = 0;
            uint8_t firstChar = bytes[index];
    
                // 1个字节
            if ((firstChar & 0x80) == 0 && (firstChar == 0x09 || firstChar == 0x0A || firstChar == 0x0D || (0x20 <= firstChar && firstChar <= 0x7E))) {
                len = 1;
            }
                // 2字节
            else if ((firstChar & 0xE0) == 0xC0 && (0xC2 <= firstChar && firstChar <= 0xDF)) {
                if (index + 1 < dataLength) {
                    uint8_t secondChar = bytes[index + 1];
                    if (0x80 <= secondChar && secondChar <= 0xBF) {
                        len = 2;
                    }
                }
            }
                // 3字节
            else if ((firstChar & 0xF0) == 0xE0) {
                if (index + 2 < dataLength) {
                    uint8_t secondChar = bytes[index + 1];
                    uint8_t thirdChar = bytes[index + 2];
                    
                    if (firstChar == 0xE0 && (0xA0 <= secondChar && secondChar <= 0xBF) && (0x80 <= thirdChar && thirdChar <= 0xBF)) {
                        len = 3;
                    } else if (((0xE1 <= firstChar && firstChar <= 0xEC) || firstChar == 0xEE || firstChar == 0xEF) && (0x80 <= secondChar && secondChar <= 0xBF) && (0x80 <= thirdChar && thirdChar <= 0xBF)) {
                        len = 3;
                    } else if (firstChar == 0xED && (0x80 <= secondChar && secondChar <= 0x9F) && (0x80 <= thirdChar && thirdChar <= 0xBF)) {
                        len = 3;
                    }
                }
            }
                // 4字节
            else if ((firstChar & 0xF8) == 0xF0) {
                if (index + 3 < dataLength) {
                    uint8_t secondChar = bytes[index + 1];
                    uint8_t thirdChar = bytes[index + 2];
                    uint8_t fourthChar = bytes[index + 3];
                    
                    if (firstChar == 0xF0) {
                        if ((0x90 <= secondChar & secondChar <= 0xBF) && (0x80 <= thirdChar && thirdChar <= 0xBF) && (0x80 <= fourthChar && fourthChar <= 0xBF)) {
                            len = 4;
                        }
                    } else if ((0xF1 <= firstChar && firstChar <= 0xF3)) {
                        if ((0x80 <= secondChar && secondChar <= 0xBF) && (0x80 <= thirdChar && thirdChar <= 0xBF) && (0x80 <= fourthChar && fourthChar <= 0xBF)) {
                            len = 4;
                        }
                    } else if (firstChar == 0xF3) {
                        if ((0x80 <= secondChar && secondChar <= 0x8F) && (0x80 <= thirdChar && thirdChar <= 0xBF) && (0x80 <= fourthChar && fourthChar <= 0xBF)) {
                            len = 4;
                        }
                    }
                }
            }
                // 5个字节
            else if ((firstChar & 0xFC) == 0xF8) {
                len = 0;
            }
                // 6个字节
            else if ((firstChar & 0xFE) == 0xFC) {
                len = 0;
            }
    
            if (len == 0) {
                index++;
                [resData appendData:replacement];
            } else {
                [resData appendBytes:bytes + index length:len];
                index += len;
            }
        }
    
        return resData;
    }
    @end
    

    相关文章

      网友评论

      • LD_左岸:+ (NSString *)converToGBK:(NSString *)str
        {

        NSStringEncoding encoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);

        NSData *pageData = [str dataUsingEncoding:NSUTF8StringEncoding];
        NSLog(@"pageData = %@",pageData);
        NSString *string = [[NSString alloc] initWithData:pageData encoding:encoding];

        return string;
        }
        这种 为nil咋回事
        andforce:你这是要转GBK啊,我这是解决UTF-8的问题
      • Mr卿:f�\^X\^AW@fï
      • Mr卿:f�\^X\^AW@fï 变成这样了 f���W@f�
      • 慕辰west:请问 转为UTF-8的data后,还能还原到原来的吗?
        andforce:@慕辰west 不能了
      • 那道曙光:我接收的是文本的nsdata,应该是编码有非法字符一直返回nil,用你这个方法全部返回的是�,请问需要在哪里改下吗?
        andforce:@那道曙光
        返回�并不是问题,你如果仔细看代码,你会发现,我代码种把非法的UTF-8用�代替了,如果你的NSData转码后全都是�,那说明你NSData中不包含UTF-8字符,应该换个思路考虑一下,是不是GBK编码的
      • ZHF_JianShu:我这边也是返回那些����r�M�� 想问下怎么才能看到正常的字符串呢?
      • long弟弟:请允许我尊您一声大神,您的代码我还没有试验,特想问一下:图片转NSData,NSData装NSString可以有数据吗?
        andforce:@long弟弟 这个不会正常的数据
        andforce:@long弟弟 这个我还真没有试过,不过你为啥会有如此奇怪的需求呢?实在要这么做,可以用base64把图片转成字符串
      • 拉风的胖鱼:感觉这个方法,用在解析歌词会出现问题……我查了用这个方法 NSStringEncoding myEncoding = CFStringConvertEncodingToNSStringEncoding (kCFStringEncodingGB_18030_2000);
        NSString *rawString=[[NSString alloc]initWithData:data encoding:myEncoding];就OK了
        0b9a78e0d359:哥 我用你的方法 解析出来是乱码呀,方加QQ帮我看看么 感之不尽。543323272
        拉风的胖鱼:@andforce 嗯嗯嗯,谢谢。
        andforce:你用OC多写一些爬虫就知道了,你这方法解决的只是一小部分问题
      • 十一岁的加重:andforce 这名字好熟悉,好像github上见过
      • 十一岁的加重:回去试了下,真的有数据了,感谢好文章
        andforce:@Hera88 不奇怪,这是非法的数据,有那个乱码代替了
        48191fad7b32:返回的好奇怪呀 �c��������'���x��� �
        andforce: @十一岁的加重 必须有数据啊,这问题搞了我2天。。。
      • 十一岁的加重:好东西啊,最近写爬虫,甚是需要

      本文标题:NSData转NSString返回nil的终极解决方法

      本文链接:https://www.haomeiwen.com/subject/rvsopttx.html