내가 할 싶습니다 base64
인코딩 및 디코딩,하지만 난 아이폰에서 어떤 지원을 찾을 수 없습니다 SDK
. base64
라이브러리를 사용하거나 사용하지 않고 인코딩 및 디코딩을 어떻게 수행 할 수 있습니까?
답변
이것은 Objective C 범주에 대한 좋은 사용 사례입니다 .
Base64 인코딩의 경우 :
#import <Foundation/NSString.h>
@interface NSString (NSStringAdditions)
+ (NSString *) base64StringFromData:(NSData *)data length:(int)length;
@end
-------------------------------------------
#import "NSStringAdditions.h"
static char base64EncodingTable[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
};
@implementation NSString (NSStringAdditions)
+ (NSString *) base64StringFromData: (NSData *)data length: (int)length {
unsigned long ixtext, lentext;
long ctremaining;
unsigned char input[3], output[4];
short i, charsonline = 0, ctcopy;
const unsigned char *raw;
NSMutableString *result;
lentext = [data length];
if (lentext < 1)
return @"";
result = [NSMutableString stringWithCapacity: lentext];
raw = [data bytes];
ixtext = 0;
while (true) {
ctremaining = lentext - ixtext;
if (ctremaining <= 0)
break;
for (i = 0; i < 3; i++) {
unsigned long ix = ixtext + i;
if (ix < lentext)
input[i] = raw[ix];
else
input[i] = 0;
}
output[0] = (input[0] & 0xFC) >> 2;
output[1] = ((input[0] & 0x03) << 4) | ((input[1] & 0xF0) >> 4);
output[2] = ((input[1] & 0x0F) << 2) | ((input[2] & 0xC0) >> 6);
output[3] = input[2] & 0x3F;
ctcopy = 4;
switch (ctremaining) {
case 1:
ctcopy = 2;
break;
case 2:
ctcopy = 3;
break;
}
for (i = 0; i < ctcopy; i++)
[result appendString: [NSString stringWithFormat: @"%c", base64EncodingTable[output[i]]]];
for (i = ctcopy; i < 4; i++)
[result appendString: @"="];
ixtext += 3;
charsonline += 4;
if ((length > 0) && (charsonline >= length))
charsonline = 0;
}
return result;
}
@end
Base64 디코딩의 경우 :
#import <Foundation/Foundation.h>
@class NSString;
@interface NSData (NSDataAdditions)
+ (NSData *) base64DataFromString:(NSString *)string;
@end
-------------------------------------------
#import "NSDataAdditions.h"
@implementation NSData (NSDataAdditions)
+ (NSData *)base64DataFromString: (NSString *)string
{
unsigned long ixtext, lentext;
unsigned char ch, inbuf[4], outbuf[3];
short i, ixinbuf;
Boolean flignore, flendtext = false;
const unsigned char *tempcstring;
NSMutableData *theData;
if (string == nil)
{
return [NSData data];
}
ixtext = 0;
tempcstring = (const unsigned char *)[string UTF8String];
lentext = [string length];
theData = [NSMutableData dataWithCapacity: lentext];
ixinbuf = 0;
while (true)
{
if (ixtext >= lentext)
{
break;
}
ch = tempcstring [ixtext++];
flignore = false;
if ((ch >= 'A') && (ch <= 'Z'))
{
ch = ch - 'A';
}
else if ((ch >= 'a') && (ch <= 'z'))
{
ch = ch - 'a' + 26;
}
else if ((ch >= '0') && (ch <= '9'))
{
ch = ch - '0' + 52;
}
else if (ch == '+')
{
ch = 62;
}
else if (ch == '=')
{
flendtext = true;
}
else if (ch == '/')
{
ch = 63;
}
else
{
flignore = true;
}
if (!flignore)
{
short ctcharsinbuf = 3;
Boolean flbreak = false;
if (flendtext)
{
if (ixinbuf == 0)
{
break;
}
if ((ixinbuf == 1) || (ixinbuf == 2))
{
ctcharsinbuf = 1;
}
else
{
ctcharsinbuf = 2;
}
ixinbuf = 3;
flbreak = true;
}
inbuf [ixinbuf++] = ch;
if (ixinbuf == 4)
{
ixinbuf = 0;
outbuf[0] = (inbuf[0] << 2) | ((inbuf[1] & 0x30) >> 4);
outbuf[1] = ((inbuf[1] & 0x0F) << 4) | ((inbuf[2] & 0x3C) >> 2);
outbuf[2] = ((inbuf[2] & 0x03) << 6) | (inbuf[3] & 0x3F);
for (i = 0; i < ctcharsinbuf; i++)
{
[theData appendBytes: &outbuf[i] length: 1];
}
}
if (flbreak)
{
break;
}
}
}
return theData;
}
@end
답변
PHP Core 라이브러리에서 네이티브 Objective-C 코드로 포팅 (및 수정 / 개선) 된, 실제로 매우 빠른 구현 은 QSUtilities Library 의 QSStrings 클래스 에서 사용할 수 있습니다 . 빠른 벤치 마크를 수행했습니다. 5.3MB 이미지 (JPEG) 파일은 인코딩하는 데 50ms 미만, 디코딩하는 데 약 140ms가 걸렸습니다.
전체 라이브러리에 대한 코드 (Base64 메서드 포함)는 GitHub에서 사용할 수 있습니다 .
또는 코드를 Base64 메서드 에만 적용 하려면 여기에 게시했습니다.
먼저 매핑 테이블이 필요합니다.
static const char _base64EncodingTable[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const short _base64DecodingTable[256] = {
-2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -1, -1, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -2, -2, -2,
-2, 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, -2, -2, -2, -2, -2,
-2, 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, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
};
인코딩하려면 :
+ (NSString *)encodeBase64WithString:(NSString *)strData {
return [QSStrings encodeBase64WithData:[strData dataUsingEncoding:NSUTF8StringEncoding]];
}
+ (NSString *)encodeBase64WithData:(NSData *)objData {
const unsigned char * objRawData = [objData bytes];
char * objPointer;
char * strResult;
// Get the Raw Data length and ensure we actually have data
int intLength = [objData length];
if (intLength == 0) return nil;
// Setup the String-based Result placeholder and pointer within that placeholder
strResult = (char *)calloc((((intLength + 2) / 3) * 4) + 1, sizeof(char));
objPointer = strResult;
// Iterate through everything
while (intLength > 2) { // keep going until we have less than 24 bits
*objPointer++ = _base64EncodingTable[objRawData[0] >> 2];
*objPointer++ = _base64EncodingTable[((objRawData[0] & 0x03) << 4) + (objRawData[1] >> 4)];
*objPointer++ = _base64EncodingTable[((objRawData[1] & 0x0f) << 2) + (objRawData[2] >> 6)];
*objPointer++ = _base64EncodingTable[objRawData[2] & 0x3f];
// we just handled 3 octets (24 bits) of data
objRawData += 3;
intLength -= 3;
}
// now deal with the tail end of things
if (intLength != 0) {
*objPointer++ = _base64EncodingTable[objRawData[0] >> 2];
if (intLength > 1) {
*objPointer++ = _base64EncodingTable[((objRawData[0] & 0x03) << 4) + (objRawData[1] >> 4)];
*objPointer++ = _base64EncodingTable[(objRawData[1] & 0x0f) << 2];
*objPointer++ = '=';
} else {
*objPointer++ = _base64EncodingTable[(objRawData[0] & 0x03) << 4];
*objPointer++ = '=';
*objPointer++ = '=';
}
}
// Terminate the string-based result
*objPointer = '\0';
// Create result NSString object
NSString *base64String = [NSString stringWithCString:strResult encoding:NSASCIIStringEncoding];
// Free memory
free(strResult);
return base64String;
}
디코딩하려면 :
+ (NSData *)decodeBase64WithString:(NSString *)strBase64 {
const char *objPointer = [strBase64 cStringUsingEncoding:NSASCIIStringEncoding];
size_t intLength = strlen(objPointer);
int intCurrent;
int i = 0, j = 0, k;
unsigned char *objResult = calloc(intLength, sizeof(unsigned char));
// Run through the whole string, converting as we go
while ( ((intCurrent = *objPointer++) != '\0') && (intLength-- > 0) ) {
if (intCurrent == '=') {
if (*objPointer != '=' && ((i % 4) == 1)) {// || (intLength > 0)) {
// the padding character is invalid at this point -- so this entire string is invalid
free(objResult);
return nil;
}
continue;
}
intCurrent = _base64DecodingTable[intCurrent];
if (intCurrent == -1) {
// we're at a whitespace -- simply skip over
continue;
} else if (intCurrent == -2) {
// we're at an invalid character
free(objResult);
return nil;
}
switch (i % 4) {
case 0:
objResult[j] = intCurrent << 2;
break;
case 1:
objResult[j++] |= intCurrent >> 4;
objResult[j] = (intCurrent & 0x0f) << 4;
break;
case 2:
objResult[j++] |= intCurrent >>2;
objResult[j] = (intCurrent & 0x03) << 6;
break;
case 3:
objResult[j++] |= intCurrent;
break;
}
i++;
}
// mop things up if we ended on a boundary
k = j;
if (intCurrent == '=') {
switch (i % 4) {
case 1:
// Invalid state
free(objResult);
return nil;
case 2:
k++;
// flow through
case 3:
objResult[k] = 0;
}
}
// Cleanup and setup the return NSData
NSData * objData = [[[NSData alloc] initWithBytes:objResult length:j] autorelease];
free(objResult);
return objData;
}
답변
역사적으로 우리는 이진 데이터에서 기본 64 문자열로 변환하는 많은 타사 기본 64 라이브러리 중 하나를 안내했지만 iOS 7에는 기본 기본 인코딩이 있습니다 (및 이전 버전의 iOS를 지원 해야하는 경우 이전에 비공개 iOS 4 방법을 노출합니다.
따라서 기본 64 표현 으로 변환 NSData
하려면 NSString
사용할 수 있습니다 base64EncodedStringWithOptions
. 7.0 이전의 iOS 버전도 지원해야하는 경우 다음을 수행 할 수 있습니다.
NSString *string;
if ([data respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
string = [data base64EncodedStringWithOptions:kNilOptions]; // iOS 7+
} else {
string = [data base64Encoding]; // pre iOS7
}
그리고 base 64를 NSString
다시 변환하기 위해 NSData
사용할 수 있습니다 initWithBase64EncodedString
. 마찬가지로 7.0 이전의 iOS 버전을 지원해야하는 경우 다음을 수행 할 수 있습니다.
NSData *data;
if ([NSData instancesRespondToSelector:@selector(initWithBase64EncodedString:options:)]) {
data = [[NSData alloc] initWithBase64EncodedString:string options:kNilOptions]; // iOS 7+
} else {
data = [[NSData alloc] initWithBase64Encoding:string]; // pre iOS7
}
당신이 아이폰 OS 버전 7.0 이전과의 호환성을 필요로하지 않는 경우에 분명히, 그냥 사용에도 쉽게 base64EncodedStringWithOptions
또는 initWithBase64EncodedString
각각 이전의 iOS 버전에 대한 런타임 검사로 귀찮게하지 않습니다. 실제로 최소 대상이 iOS 7 이상일 때 위의 코드를 사용하면 실제로 사용되지 않는 메소드에 대한 컴파일러 경고가 표시됩니다. 따라서 iOS 7 이상에서는 다음을 사용하여 기본 64 문자열로 간단히 변환합니다.
NSString *string = [data base64EncodedStringWithOptions:kNilOptions];
다시 :
NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:kNilOptions];
답변
iOS에는 base64 인코딩 및 디코딩 지원 기능이 내장되어 있습니다. 당신이 보면 resolv.h
당신은 두 가지 기능을 참조해야 b64_ntop
하고 b64_pton
. Square SocketRocket 라이브러리는 objective-c에서 이러한 함수를 사용하는 방법에 대한 합리적인 예를 제공합니다.
이 기능은 임의의 인터넷 게시에서 찾을 수있는 많은 구현과 달리 테스트가 잘되고 안정적입니다. 에 링크하는 것을 잊지 마십시오 libresolv.dylib
.
답변
이것이 Google이 base64 인코딩 및 iPhone에서 가장 많이 사용하는 것으로 보이므로 위의 코드 스 니펫으로 내 경험을 공유하고 싶었습니다.
작동하지만 매우 느립니다. 임의의 이미지 (0.4mb)의 벤치 마크는 기본 iPhone에서 37 초가 걸렸습니다. 주된 이유는 아마도 모든 OOP 매직-단일 문자 NSString 등입니다. 인코딩이 완료된 후에 만 자동 릴리스됩니다.
여기에 게시 된 또 다른 제안 (ab)은 openssl 라이브러리를 사용하며 과도하게 느껴집니다.
아래 코드는 70ms가 걸리며 500 배 속도가 빨라집니다. 이것은 base64 인코딩 만 수행합니다 (디코딩은 마주 치면 따라갑니다)
+ (NSString *) base64StringFromData: (NSData *)data length: (int)length {
int lentext = [data length];
if (lentext < 1) return @"";
char *outbuf = malloc(lentext*4/3+4); // add 4 to be sure
if ( !outbuf ) return nil;
const unsigned char *raw = [data bytes];
int inp = 0;
int outp = 0;
int do_now = lentext - (lentext%3);
for ( outp = 0, inp = 0; inp < do_now; inp += 3 )
{
outbuf[outp++] = base64EncodingTable[(raw[inp] & 0xFC) >> 2];
outbuf[outp++] = base64EncodingTable[((raw[inp] & 0x03) << 4) | ((raw[inp+1] & 0xF0) >> 4)];
outbuf[outp++] = base64EncodingTable[((raw[inp+1] & 0x0F) << 2) | ((raw[inp+2] & 0xC0) >> 6)];
outbuf[outp++] = base64EncodingTable[raw[inp+2] & 0x3F];
}
if ( do_now < lentext )
{
char tmpbuf[2] = {0,0};
int left = lentext%3;
for ( int i=0; i < left; i++ )
{
tmpbuf[i] = raw[do_now+i];
}
raw = tmpbuf;
outbuf[outp++] = base64EncodingTable[(raw[inp] & 0xFC) >> 2];
outbuf[outp++] = base64EncodingTable[((raw[inp] & 0x03) << 4) | ((raw[inp+1] & 0xF0) >> 4)];
if ( left == 2 ) outbuf[outp++] = base64EncodingTable[((raw[inp+1] & 0x0F) << 2) | ((raw[inp+2] & 0xC0) >> 6)];
}
NSString *ret = [[[NSString alloc] initWithBytes:outbuf length:outp encoding:NSASCIIStringEncoding] autorelease];
free(outbuf);
return ret;
}
필요하지 않기 때문에 줄을 자르지 않았지만 추가하는 것은 쉽지 않습니다.
최적화에 관심이있는 사람들을 위해 : 목표는 메인 루프에서 일어나는 일을 최소화하는 것입니다. 따라서 마지막 3 바이트를 처리하는 모든 논리는 루프 외부에서 처리됩니다.
또한 버퍼로 /에서 추가로 복사하지 않고 내부 데이터 작업을 시도하십시오. 그리고 산술을 최소한으로 줄입니다.
테이블에서 항목을 조회하기 위해 조합 된 비트는 이동하지 않고 함께 정렬 될 때 겹치지 않습니다. 따라서 크게 개선 된 4 개의 256 바이트 룩업 테이블을 사용하고 다음과 같이 시프트를 제거 할 수 있습니다.
outbuf[outp++] = base64EncodingTable1[(raw[inp] & 0xFC)];
outbuf[outp++] = base64EncodingTable2[(raw[inp] & 0x03) | (raw[inp+1] & 0xF0)];
outbuf[outp++] = base64EncodingTable3[(raw[inp+1] & 0x0F) | (raw[inp+2] & 0xC0)];
outbuf[outp++] = base64EncodingTable4[raw[inp+2] & 0x3F];
물론 훨씬 더 나아갈 수는 있지만 여기서는 범위를 벗어납니다.
답변
mvd의 뛰어난 개선에는 두 가지 문제가 있습니다. 다음과 같이 코드를 변경하십시오.
raw = tmpbuf;
inp = 0;
outbuf[outp++] = base64EncodingTable[(raw[inp] & 0xFC) >> 2];
outbuf[outp++] = base64EncodingTable[((raw[inp] & 0x03) << 4) | ((raw[inp+1] & 0xF0) >> 4)];
if ( left == 2 ) outbuf[outp++] = base64EncodingTable[((raw[inp+1] & 0x0F) << 2) | ((raw[inp+2] & 0xC0) >> 6)];
else outbuf[outp++] = '=';
outbuf[outp++] = '=';
답변
더 나은 솔루션 :
NSData에는 내장 함수가 있습니다
[data base64Encoding]; //iOS < 7.0
[data base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength]; //iOS >= 7.0
