iOS 中 Base64 编解码分类实现
太阳火神的美丽人生 (http://blog.csdn.net/opengl_es)
本文遵循“署名-非商业用途-保持一致”创作公用协议
再补充:由此可见,当一个人极力地想要说明自已,表述清楚自已的想法的时侯,最后会被自已搞得更加不清楚了,从而给别人看到的是,你很混乱。即然我们是生活在人所组成的社会中,那么更多的应该考虑人的思维,而不应该过份地关注事物本身的真伪。无论你说的是对的或是错的,但只要你能针对你的观众,按他(她)们的心理需要来说明白就可以了,更确切地说,不是把真象告诉他们,而是把他们心中所知道的和能接受的事情,再重复说给他们听一遍就可以了,就这么简单。因为你是生活和生存在人的世界中,而非真理的世界中,过份强调真理,只能是违背人的内心需求,即使是真理,也会变成谬论,更何况,真理本身是没办法真正表达清楚的。
从另一个角度去看,当你看到一个人在努力地表达一些事情的时侯,本来已经有点靠谱了,但越说越不靠谱了,这其实并不能说明他混乱,而是他进入一种我们无法进入的境界了。不是我们进入不了,而是我们没有专注而已。
专注该专注的,但从长久来看,就是失去本真初,妄心肆意,耐表真心,不可如来,如初生之时。
唉,别说了,再说下去,我也要入境了,哈哈哈,那可不好玩啊......
----------
补充:我还是转一下 base64 的维基解释和用途吧,免得被我个人的理解误导了,我的理解足够我理解和使用,但并不代表我表述的确实是我的想法,也或者说,并不代表我能很好地表述我的想法。
这同样是一个世界性的难题,一个人想到的本身就有偏差,说出来的往往与想到的偏差更大,所以这个世界上并没有真理存在,真正的真理是无法被表达出来的!(大概意思是这样子的,忘了是哪个名人说的至理明言了,以前觉得他好像是疯了,不过这几年来看,有些理解了,所以也明白了高数中的极限的概念,只能是趋近而已)
------
Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于2的6次方等于64,所以每6个位元为一个单元,对应某个可打印字符。三个字节有24个位元,对应于4个Base64单元,即3个字节需要用4个可打印字符来表示。它可用来作为电子邮件的传输编码。在Base64中的可打印字符包括字母A-Z、a-z、数字0-9,这样共有62个字符,此外两个可打印符号在不同的系统中而不同。一些如uuencode的其他编码方法,和之后binhex的版本使用不同的64字符集来代表6个二进制数字,但是它们不叫Base64。
Base64常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据。包括MIME的email,email via MIME,在XML中存储复杂数据.
我们知道,可识别的文本及各种结构,最终都能转换成二进制来表示,而二进制无非就是它们的内存存储结构的字节排布。
那么,在 iOS 中文本及相关结构有 NSString 等;二进制则使用 NSData 来管理和表示。
而 NSData 可以和这些文本及相关结构进行转换,所以我们只要能对 NSData 进行相应的 Base64 编码,那么就可以对其它能转换成 NSData 的文本及相关结构进行编码:
其实 Base64 编码出来的也是文本,但这种文本串有个特点,就是它使用的都是最基本的字符,不会因7位编码和8位编码等一系列问题,而在某些原始的网络设备中(比如某国外的一台路由器,有这个可能会路由到那里去)无法处理。
并且在一些如 XML 和 JSON 这样的数据承载结构中,只支持明文字符,如果用二进制,则会使其所基于的底层结构混乱,而破坏其自身的可用性。
可以这样来表示这个过程:
Others => 二进制 => Base64编码串
NSString => NSData => Base64
同样的道理,从 Base64 串解码出原始内容,也需要先将 Base64 串转换成 NSData 二进制内容,然后再进行各自的转换。
这样一来,我们就只需要完成 NSData 与 Base64 串的互转,这个问题,会让我们想起在 iOS 中使用分类来扩展 NSData 的功能是最好的办法,且无需额外的管理。
下面就贴一下曾经使用过的一套分类扩展,忘记是从哪里下载到的了,权当是自已的发个原创吧。不过这里的 C 编解码 base64 的方式和当年我学习 C 语言写网页(这个确实是可以的,而且作为 CGI 处理少量请求,效率很高,但写起来拼输出串很费事)时按 base64 原理 和 URL编解码原理写的方式不同,当时的硬件配置,算法精练精高效,而现在的查表方式更轻松易懂一些。
废话不多说,上代码:
Objective-C 类声明:
#import <Foundation/Foundation.h> void *NewBase64Decode(const char *inputBuffer, size_t length, size_t *outputLength); char *NewBase64Encode(const void *inputBuffer, size_t length, bool separateLines, size_t *outputLength); @interface NSData (Base64) + (NSData *)dataFromBase64String:(NSString *)aString; - (NSString *)base64EncodedString; @end
Objective-C 类实现:
#import "NSData+Base64.h" // // Mapping from 6 bit pattern to ASCII character. // static unsigned char base64EncodeLookup[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // // Definition for "masked-out" areas of the base64DecodeLookup mapping // #define xx 65 // // Mapping from ASCII character to 6 bit pattern. // static unsigned char base64DecodeLookup[256] = { xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 62, xx, xx, xx, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, xx, xx, xx, xx, xx, xx, xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, xx, xx, xx, xx, xx, xx, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, }; // // Fundamental sizes of the binary and base64 encode/decode units in bytes // #define BINARY_UNIT_SIZE 3 #define BASE64_UNIT_SIZE 4 // // NewBase64Decode // // Decodes the base64 ASCII string in the inputBuffer to a newly malloced // output buffer. // // inputBuffer - the source ASCII string for the decode // length - the length of the string or -1 (to specify strlen should be used) // outputLength - if not-NULL, on output will contain the decoded length // // returns the decoded buffer. Must be free'd by caller. Length is given by // outputLength. // void *NewBase64Decode(const char *inputBuffer, size_t length, size_t *outputLength) { if (length == -1) { length = strlen(inputBuffer); } size_t outputBufferSize = (length / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE; unsigned char *outputBuffer = (unsigned char *)malloc(outputBufferSize); size_t i = 0; size_t j = 0; while (i < length) { // // Accumulate 4 valid characters (ignore everything else) // unsigned char accumulated[BASE64_UNIT_SIZE]; size_t accumulateIndex = 0; while (i < length) { unsigned char decode = base64DecodeLookup[inputBuffer[i++]]; if (decode != xx) { accumulated[accumulateIndex] = decode; accumulateIndex++; if (accumulateIndex == BASE64_UNIT_SIZE) { break; } } } // // Store the 6 bits from each of the 4 characters as 3 bytes // outputBuffer[j] = (accumulated[0] << 2) | (accumulated[1] >> 4); outputBuffer[j + 1] = (accumulated[1] << 4) | (accumulated[2] >> 2); outputBuffer[j + 2] = (accumulated[2] << 6) | accumulated[3]; j += accumulateIndex - 1; } if (outputLength) { *outputLength = j; } return outputBuffer; } // // NewBase64Decode // // Encodes the arbitrary data in the inputBuffer as base64 into a newly malloced // output buffer. // // inputBuffer - the source data for the encode // length - the length of the input in bytes // separateLines - if zero, no CR/LF characters will be added. Otherwise // a CR/LF pair will be added every 64 encoded chars. // outputLength - if not-NULL, on output will contain the encoded length // (not including terminating 0 char) // // returns the encoded buffer. Must be free'd by caller. Length is given by // outputLength. // char *NewBase64Encode( const void *buffer, size_t length, bool separateLines, size_t *outputLength) { const unsigned char *inputBuffer = (const unsigned char *)buffer; #define MAX_NUM_PADDING_CHARS 2 #define OUTPUT_LINE_LENGTH 64 #define INPUT_LINE_LENGTH ((OUTPUT_LINE_LENGTH / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE) #define CR_LF_SIZE 2 // // Byte accurate calculation of final buffer size // size_t outputBufferSize = ((length / BINARY_UNIT_SIZE) + ((length % BINARY_UNIT_SIZE) ? 1 : 0)) * BASE64_UNIT_SIZE; if (separateLines) { outputBufferSize += (outputBufferSize / OUTPUT_LINE_LENGTH) * CR_LF_SIZE; } // // Include space for a terminating zero // outputBufferSize += 1; // // Allocate the output buffer // char *outputBuffer = (char *)malloc(outputBufferSize); if (!outputBuffer) { return NULL; } size_t i = 0; size_t j = 0; const size_t lineLength = separateLines ? INPUT_LINE_LENGTH : length; size_t lineEnd = lineLength; while (true) { if (lineEnd > length) { lineEnd = length; } for (; i + BINARY_UNIT_SIZE - 1 < lineEnd; i += BINARY_UNIT_SIZE) { // // Inner loop: turn 48 bytes into 64 base64 characters // outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2]; outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4) | ((inputBuffer[i + 1] & 0xF0) >> 4)]; outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i + 1] & 0x0F) << 2) | ((inputBuffer[i + 2] & 0xC0) >> 6)]; outputBuffer[j++] = base64EncodeLookup[inputBuffer[i + 2] & 0x3F]; } if (lineEnd == length) { break; } // // Add the newline // outputBuffer[j++] = '\r'; outputBuffer[j++] = '\n'; lineEnd += lineLength; } if (i + 1 < length) { // // Handle the single '=' case // outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2]; outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4) | ((inputBuffer[i + 1] & 0xF0) >> 4)]; outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i + 1] & 0x0F) << 2]; outputBuffer[j++] = '='; } else if (i < length) { // // Handle the double '=' case // outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2]; outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0x03) << 4]; outputBuffer[j++] = '='; outputBuffer[j++] = '='; } outputBuffer[j] = 0; // // Set the output length and return the buffer // if (outputLength) { *outputLength = j; } return outputBuffer; } @implementation NSData (Base64) // // dataFromBase64String: // // Creates an NSData object containing the base64 decoded representation of // the base64 string 'aString' // // Parameters: // aString - the base64 string to decode // // returns the autoreleased NSData representation of the base64 string // + (NSData *)dataFromBase64String:(NSString *)aString { NSData *data = [aString dataUsingEncoding:NSASCIIStringEncoding]; size_t outputLength; void *outputBuffer = NewBase64Decode([data bytes], [data length], &outputLength); NSData *result = [NSData dataWithBytes:outputBuffer length:outputLength]; free(outputBuffer); return result; } // // base64EncodedString // // Creates an NSString object that contains the base 64 encoding of the // receiver's data. Lines are broken at 64 characters long. // // returns an autoreleased NSString being the base 64 representation of the // receiver. // - (NSString *)base64EncodedString { size_t outputLength; char *outputBuffer = NewBase64Encode([self bytes], [self length], true, &outputLength); NSString *result = [[[NSString alloc] initWithBytes:outputBuffer length:outputLength encoding:NSASCIIStringEncoding] autorelease]; free(outputBuffer); return result; } @end
或许这段的逻辑更精炼一些,但确实需要你头脑清醒些,才能挼清每一小步的逻辑,这就是早些年中国式编程的一个误区,也是印度当时能成为世界软件外包大国的原因(避勉这种无意义的抽象和极致,更多精力花费在业务逻辑上,以满足客户要求):
BOOL encodeBase64(const unsigned char *src, NSInteger srcLength, char *dest, NSUInteger *pDestLength) { static char base64Char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; NSUInteger shouldLength = (srcLength + 2) / 3 * 3; NSUInteger base64Length = shouldLength / 3 * 4; if (NULL == pDestLength || *pDestLength < base64Length) { return NO; } *pDestLength = base64Length; char *data = malloc(shouldLength); memcpy(data, src, srcLength); for (NSUInteger i = srcLength; i < shouldLength; i++) { data[i] = 0; } for (NSUInteger i = 0; i < shouldLength; i += 3) { NSUInteger idx = i / 3 * 4; dest[idx] = base64Char[(data[i] & 0xfc) >> 2]; dest[idx + 1] = base64Char[((data[i] & 0x03) << 4 ) + ((data[i + 1] & 0xf0) >> 4)]; dest[idx + 2] = base64Char[((data[i + 1] & 0x0f) << 2) + ((data[i + 2] & 0xc0) >> 6)]; dest[idx + 3] = base64Char[data[i + 2] & 0x3f]; } free( data ); for (NSUInteger i = 1; i <= shouldLength - srcLength; i++) { dest[base64Length - i] = '='; } return YES; }
好了,Base64 编解码完成,其实叫它编解码是正确的,不过它同时有加密的功能,至少用肉眼无法误别内容,就像二维码一样,也是一种编解码,同时具有加密功能,加密的对象是肉眼!
后续还有MD5、DES、AES等等对称加密、非对称加密,这些不光有编解码的功效,同时更主要是加解密,加密对象是任何未知密钥或源内容的人或机器。
编码的意义并不一定在于要加密,而加密本身就是一种编码的方式!
不过,MD5已经被中国科学家破解,详见报道 山东大学王小云教授成功破解MD5 。
但,这只说明 MD5 并不是不可逆(无法反算出原始内容)的加密方式,而并不表示我们也很容易地能做到。
一种常规的 MD5 破解方式是跑字典,而跑字典也是很多破解的常用方式。