在 iOS 开发中,经常会遇到编码问题,通过系统API转码时由于没有容错性,导致因为个别异常编码问题,让全部转码失败。
- 在数据传递我们经常会采用base64 + gbk 方式,当在base64 转换成data时是没有问题的,主要是在转换gbk,如果里面存在个别使用UTF-8编码时,会导致转码失败,使用的接口如下:
CFStringRef CFStringCreateWithBytes(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex numBytes, CFStringEncoding encoding, Boolean isExternalRepresentation);
- 为解决上述问题,需要在转码异常时,把响应异常的编码数据剔除掉,然后再进行转码才可以解决,下面就是针对于GB18030编码异常数据的校验
检验GB18030 异常编码
- 异常编码采用 ? 代替
unsigned int checkGB18030(const unsigned char *bytes, unsigned int length, unsigned char *buffer)
{
unsigned int bufferIndex = 0;
unsigned int byteIndex = 0;
bool invalidByte = false;
while (byteIndex < length) {
Byte byte = bytes[byteIndex];
if (byte >= 0 && byte <= (Byte)0x7f) {
//单字节(英文、数字)
buffer[bufferIndex++] = byte;
} else if (byte >= (Byte)0x81 && byte <= (Byte)0xfe){
//可能是双字节,可能是四字节
if (byteIndex + 1 >= length) {
//这是最后一个字节了,但是这个字节表明后面应该还有1或3个字节,那么这个字节一定是错误字节
break;
}
Byte byte2 = bytes[++byteIndex];
if (byte2 >= (Byte)0x40 && byte <= (Byte)0xfe && byte != (Byte)0x7f) {
//是双字节,并且可能合法
unsigned char tuple[] = {byte, byte2};
CFStringRef cfstr = CFStringCreateWithBytes(kCFAllocatorDefault, tuple, 2, kCFStringEncodingGB_18030_2000, false);
if (cfstr) {
CFRelease(cfstr);
buffer[bufferIndex++] = byte;
buffer[bufferIndex++] = byte2;
} else {
//这个双字节字符不合法,但byte2可能是下一字符的第一字节
invalidByte = true;
}
} else if (byte2 >= (Byte)0x30 && byte2 <= (Byte)0x39) {
//可能是四字节
if (byteIndex + 2 >= length) {
break;
}
Byte byte3 = bytes[++byteIndex];
if (byte3 >= (Byte)0x81 && byte3 <= (Byte)0xfe) {
// 第三位合法,判断第四位
Byte byte4 = bytes[++byteIndex];
if (byte4 >= (Byte)0x30 && byte4 <= (Byte)0x39) {
//第四位可能合法
unsigned char tuple[] = {byte, byte2, byte3, byte4};
CFStringRef cfstr = CFStringCreateWithBytes(kCFAllocatorDefault, tuple, 4, kCFStringEncodingGB_18030_2000, false);
if (cfstr) {
CFRelease(cfstr);
buffer[bufferIndex++] = byte;
buffer[bufferIndex++] = byte2;
buffer[bufferIndex++] = byte3;
buffer[bufferIndex++] = byte4;
} else {
//这个四字节字符不合法,但是byte2可能是下一个合法字符的第一字节,回退3位
//并且将byte1,byte2用?替代
invalidByte = true;
}
} else {
//第四字节不合法
invalidByte = true;
}
} else {
// 第三字节不合法
invalidByte = true;
}
} else {
// 第二字节不是合法的第二位,但可能是下一个合法的第一位,所以回退一个byte
invalidByte = true;
}
if (invalidByte) {
invalidByte = false;
buffer[bufferIndex++] = '?';
}
}
byteIndex++;
}
return bufferIndex;
}
- 返回值为正常编码的字节数
- 返回数据通过buffer 传递出去
具体测试代码
void test()
{
char *base64 = "CTxkaXY+us69KioKCQkJCQk8c3Bhbj4x";
char buffer[1024] = {0};
NSData *data64 = [NSData dataWithBytes:base64 length:strlen(base64)];
NSData *decodeData = [GTMBase64 decodeData:data64];
const unsigned char *bytes = (void *)[decodeData bytes];
NSUInteger lenght = [decodeData length];
printf("base64: %s",bytes);
CFStringRef cfstr = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8 *)bytes, lenght, kCFStringEncodingGB_18030_2000, false);
if (cfstr) {
NSString *text = (__bridge NSString *)cfstr;
NSLog(@"%@",text);
CFRelease(cfstr);
} else {
lenght = checkGB18030(bytes, (unsigned int)lenght, (unsigned char *)buffer);
cfstr = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8 *)buffer, lenght, kCFStringEncodingGB_18030_2000, false);
if (cfstr) {
NSString *text = (__bridge NSString *)cfstr;
NSLog(@"%@",text);
CFRelease(cfstr);
} else {
NSLog(@"解析错误!!!!");
}
}
}