美文网首页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