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 破解方式是跑字典,而跑字典也是很多破解的常用方式。