解决NSData转NSString返回nil的问题

Posted clnchanpin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解决NSData转NSString返回nil的问题相关的知识,希望对你有一定的参考价值。

在使用initWithData等方法将NSData转换成NSString时,假设NSData的内容含有非encoding编码的字符,将会返回nil。

----------SDK文档例如以下-------------

- (instancetype)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding;

Return Value

An NSString object initialized by converting the bytes in data into Unicode characters using encoding. The returned object may be different from the original receiver. Returns nil if the initialization fails for some reason (for example if data does not represent valid data for encoding).

-----------------------------


这个结果在非常多时候可能并非我们所希望的,比方在获取网页源代码进行分析等方面,假设页面採用了utf-8编码。仅仅是含有个别非utf-8字符,我们更希望转换NSString成功,抛弃(或替换)那些非法字符。


依照utf8格式标准

Unicode/UCS-4
bit数
UTF-8
byte数
范围(16进制)
0000 ~
007F
0~7
0XXX XXXX
1
0x - 7x
0080 ~
07FF
8~11
110X XXXX
10XX XXXX
2
Cx 8x - Dx Bx
0800 ~
FFFF
12~16
1110XXXX
10XX XXXX
10XX XXXX
3
Ex 8x 8x - Ex Bx Bx
1 0000 ~
1F FFFF
17~21
1111 0XXX
10XX XXXX
10XX XXXX
10XX XXXX
4
F8 8x 8x 8x 8x - FB Bx Bx Bx Bx 
20 0000 ~
3FF FFFF
22~26
1111 10XX
10XX XXXX
10XX XXXX
10XX XXXX
10XX XXXX
5
FC 8x 8x 8x 8x 8x - FD Bx Bx Bx Bx Bx
400 0000 ~
7FFF FFFF
27~31
1111 110X
10XX XXXX
10XX XXXX
10XX XXXX
10XX XXXX
10XX XXXX
6


假设一个字节小于0x80,那么他就是一个字符;

假设大于C0小于E0,表示2个字节组成的utf8字符(第一个是110开头的,第二个是10开头的);

假设大于E0小于F0,表示3个字节组成的utf8字符(第一个是1110开头的,第二个是10开头的。第三个是10开头的)。

以此类推,假设不符合utf-8规则,则表示一个非法字符,仅仅要替换这种字符就可以。

 


实现方法例如以下(此实现可用但不够严谨,如用于project中建议进行优化):

//替换非utf8字符
//注意:假设是三字节utf-8。第二字节错误,则先替换第一字节内容(觉得此字节误码为三字节utf8的头),然后推断剩下的两个字节是否非法;
- (NSData *)replaceNoUtf8:(NSData *)data
{
    char aa[] = {‘A‘,‘A‘,‘A‘,‘A‘,‘A‘,‘A‘};                      //utf8最多6个字符。当前方法未使用
    NSMutableData *md = [NSMutableData dataWithData:data];
    int loc = 0;
    while(loc < [md length])
    {
        char buffer;
        [md getBytes:&buffer range:NSMakeRange(loc, 1)];
        if((buffer & 0x80) == 0)
        {
            loc++;
            continue;
        }
        else if((buffer & 0xE0) == 0xC0)
        {
            loc++;
            [md getBytes:&buffer range:NSMakeRange(loc, 1)];
            if((buffer & 0xC0) == 0x80)
            {
                loc++;
                continue;
            }
            loc--;
            //非法字符,将这个字符(一个byte)替换为A
            [md replaceBytesInRange:NSMakeRange(loc, 1) withBytes:aa length:1];
            loc++;
            continue;
        }
        else if((buffer & 0xF0) == 0xE0)
        {
            loc++;
            [md getBytes:&buffer range:NSMakeRange(loc, 1)];
            if((buffer & 0xC0) == 0x80)
            {
                loc++;
                [md getBytes:&buffer range:NSMakeRange(loc, 1)];
                if((buffer & 0xC0) == 0x80)
                {
                    loc++;
                    continue;
                }
                loc--;
            }
            loc--;
            //非法字符,将这个字符(一个byte)替换为A
            [md replaceBytesInRange:NSMakeRange(loc, 1) withBytes:aa length:1];
            loc++;
            continue;
        }
        else
        {
            //非法字符,将这个字符(一个byte)替换为A
            [md replaceBytesInRange:NSMakeRange(loc, 1) withBytes:aa length:1];
            loc++;
            continue;
        }
    }
    
    return md;
}


转换后的NSData就能够正确转换为NSString了。


*假设是非utf-8编码,请自行对相应照编码协议转换。


以上是关于解决NSData转NSString返回nil的问题的主要内容,如果未能解决你的问题,请参考以下文章

将 NSData 转换为 NSString 返回 nil

iOS解决NSData转NSString后字符为空

php加密文件 解密data 转nsstring 为nil. rc4 ios

将 nil 传递给 archivedDataWithRootObject 返回奇怪的 NSData

iOS NSString 与NSData转化

解决 pathForResource 返回 nil的问题