Я хотел бы выполнить кодирование и декодирование base64, но я не смог найти никакой поддержки со стороны iPhone SDK. Как я могу кодировать и декодировать base64 с библиотекой или без нее?
Ссылка @GregBernhardt мертва.





Это хороший вариант использования цели 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
Если Obj-C чем-то похож на C, вы сможете это сделать: static char base64EncodingTable [64] = "ABCDE [etc] 789 + /";
при использовании base64StringFromData должна ли длина быть длиной NSData? Я получаю только первые 4 символа, используя длину NSData. Спасибо
Я понял, почему я получал только 4 символа ... Перед возвратом цикла while () должен быть}. Я бы отредактировал это, но похоже, что я не могу.
он показывает ошибку error: ожидаемое выражение перед 'else' ошибка: ожидаемое выражение перед 'end'
Да, это не работает - среди прочего, put не заявлен в base64StringFromData ...
В base64DataFromString: анализатор xcode говорит, что есть проблемы с двумя строками с inbuf[n] &. Он говорит: «Левый операнд '&' - это мусорное значение». Я не вижу проблемы с этим кодом, и поэтому я склонен думать, что это ошибка анализатора, но мне интересно, видит ли кто-нибудь проблему с кодом.
Это не ошибка анализатора. Обратите внимание, что код также пытается получить доступ к inbuf [3], который находится за пределами этого массива. Этот код воняет.
Ниже приведены гораздо более быстрые решения, которые я бы рекомендовал использовать вместо этого.
Что представляет собой значение длины?
Начиная с iOS7 Apple представила свой собственный метод кодирования base 64. См. Ответ Роба ниже, чтобы узнать, как его использовать при сохранении обратной совместимости.
Поскольку это, кажется, хит номер один в Google по кодировке base64 и iphone, мне захотелось поделиться своим опытом с приведенным выше фрагментом кода.
Это работает, но очень медленно. Тест на случайном изображении (0,4 мб) занял 37 секунд на родном iphone. Основная причина, вероятно, заключается во всей магии ООП - одиночных символьных строках NSStrings и т. д., Которые автоматически выпускаются только после завершения кодирования.
Другое предложение, опубликованное здесь (ab), использует библиотеку openssl, что тоже кажется излишним.
Код ниже занимает 70 мс - это ускорение в 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];
Конечно, вы могли бы пойти намного дальше, но здесь это выходит за рамки.
Хм. Я не мог заставить это работать. Я наблюдаю кодировку base64, отличную от ожидаемой. Вы проверяли это на примерах из RFC 4648? tools.ietf.org/html/rfc4648
Не знаете, на что ссылаются base64EncodingTable1, base64EncodingTable2, base64EncodingTable3 и base64EncodingTable4?
Очень полезно, но может читать за пределами входного буфера. Когда (left == 2), raw [inp + 2] будет на один байт после конца tmpbuf. Я думаю, что строка должна быть такой: if (left == 2) outbuf [outp ++] = base64EncodingTable [((raw [inp + 1] & 0x0F) << 2)];
измените следующую строку <code> char tmpbuf [2] = {0,0}; </code> на <code> unsigned char tmpbuf [3] = {0,0,0}; </code>
В отличном улучшении mvds есть две проблемы. Измените код на этот:
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++] = '=';
Рад, что людям понравилось. Я должен признать, что финальная стадия была немного некорректной. Помимо правильной установки inp = 0, вы должны также увеличить размер tmpbuf до 3, например
unsigned char tmpbuf[3] = {0,0,0};
или же не учитывает исходный код [inp + 2]; если бы у нас было необработанное [inp + 2]! = 0 для этого фрагмента, мы, конечно, все равно были бы в цикле ...
В любом случае, вы можете подумать о том, чтобы конечный блок поиска в таблице оставался идентичным блоку в цикле для ясности. В финальной версии, которую я использовал, я сделал
while ( outp%4 ) outbuf[outp++] = '=';
Чтобы добавить ==
Извините, я не проверил RFC и прочее, надо было сделать лучше!
у вас уже есть учетная запись здесь, так как ваш предыдущий ответ на самом деле является другой учетной записью. Кроме того, это должно быть либо редактирование, либо комментарий.
@alastair, похоже, вы получаете "аккаунт" каждый раз, когда отправляете ответ без регистрации после очистки файлов cookie. Мне не удалось подключиться к своей первой «учетной записи» (даже с тем же адресом электронной почты и IP), поэтому я просто поместил ее в качестве нового ответа, извините за это. - только что зарегистрировались!
Есть ли шанс отредактировать этот ответ на свой предыдущий, чтобы получить окончательно правильную версию? Спасибо!
#import "NSDataAdditions.h"
@implementation NSData (NSDataAdditions)
+ (NSData *) base64DataFromString: (NSString *)string {
unsigned long ixtext, lentext;
unsigned char ch, input[4], output[3];
short i, ixinput;
Boolean flignore, flendtext = false;
const char *temporary;
NSMutableData *result;
if (!string)
return [NSData data];
ixtext = 0;
temporary = [string UTF8String];
lentext = [string length];
result = [NSMutableData dataWithCapacity: lentext];
ixinput = 0;
while (true) {
if (ixtext >= lentext)
break;
ch = temporary[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 ctcharsinput = 3;
Boolean flbreak = false;
if (flendtext) {
if (ixinput == 0)
break;
if ((ixinput == 1) || (ixinput == 2))
ctcharsinput = 1;
else
ctcharsinput = 2;
ixinput = 3;
flbreak = true;
}
input[ixinput++] = ch;
if (ixinput == 4){
ixinput = 0;
output[0] = (input[0] << 2) | ((input[1] & 0x30) >> 4);
output[1] = ((input[1] & 0x0F) << 4) | ((input[2] & 0x3C) >> 2);
output[2] = ((input[2] & 0x03) << 6) | (input[3] & 0x3F);
for (i = 0; i < ctcharsinput; i++)
[result appendBytes: &output[i] length: 1];
}
if (flbreak)
break;
}
}
return result;
}
@end
Вот компактная версия Objective-C как категория на NSData. Нужно подумать о ...
@implementation NSData (DataUtils)
static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- (NSString *)newStringInBase64FromData
{
NSMutableString *dest = [[NSMutableString alloc] initWithString:@""];
unsigned char * working = (unsigned char *)[self bytes];
int srcLen = [self length];
// tackle the source in 3's as conveniently 4 Base64 nibbles fit into 3 bytes
for (int i=0; i<srcLen; i += 3)
{
// for each output nibble
for (int nib=0; nib<4; nib++)
{
// nibble:nib from char:byt
int byt = (nib == 0)?0:nib-1;
int ix = (nib+1)*2;
if (i+byt >= srcLen) break;
// extract the top bits of the nibble, if valid
unsigned char curr = ((working[i+byt] << (8-ix)) & 0x3F);
// extract the bottom bits of the nibble, if valid
if (i+nib < srcLen) curr |= ((working[i+nib] >> ix) & 0x3F);
[dest appendFormat:@"%c", base64[curr]];
}
}
return dest;
}
@end
Если требуется, можно добавить заполнение, расширив область действия 'byt' и добавив 'dest' с (2-байтовыми) символами "=" перед возвратом.
Затем в NSString можно добавить категорию, таким образом:
@implementation NSString (StringUtils)
- (NSString *)newStringInBase64FromString
{
NSData *theData = [NSData dataWithBytes:[self UTF8String] length:[self length]];
return [theData newStringInBase64FromData];
}
@end
Действительно, очень быстрая реализация, которая была перенесена (и модифицирована / улучшена) из библиотеки PHP Core в собственный код Objective-C, доступна в Класс QSStrings из Библиотека QSUtilities. Я провел быстрый тест: для кодирования файла изображения размером 5,3 МБ (JPEG) потребовалось <50 мсек и около 140 мсек для декодирования.
Код для всей библиотеки (включая методы 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;
}
Наконец-то правильная и действенная реализация. Спасибо. Другой код здесь меня пугает.
Кажется, утечка памяти, выделенной в кодировщике как strResult; ему просто нужен free() в конце (перед возвращением, но после NSString stringWithCString)
В вашем методе encodeBase64WithData:, разве первый параметр в вызове calloc() не нужно увеличивать на 1, чтобы учесть нулевой терминатор ('\0'), который вы добавляете в конце?
Тот факт, что яблоко не дает этого, заставляет Бога убивать котят ... многих из них ...
Я использовал это некоторое время, и казалось, что он отлично работает, пока я не начал получать некоторые ошибки, связанные с повреждением памяти, и, используя охранный malloc, я сузил его до этой строки: * objPointer = '\ 0'; так что будьте осторожны, если вы используете это в своих собственных приложениях.
Для большей эффективности попробуйте NSData* objData = [NSData dataWithBytesNoCopy:objResult length:j freeWhenDone:YES].
Спасибо за разъяснение! Кровавый плагиат распространен в этих блогах ... :)
Начиная с iOS7 Apple представила свой собственный метод кодирования base 64. См. Ответ Роба ниже, чтобы узнать, как его использовать при сохранении обратной совместимости.
Лучший ответ для кодирования Base64 без проблем с памятью .. Большое спасибо
iOS включает встроенную поддержку кодирования и декодирования base64. Если вы посмотрите на resolv.h, вы увидите две функции: b64_ntop и b64_pton. Библиотека Square SocketRocket предоставляет разумный пример того, как использовать эти функции из objective-c.
Эти функции довольно хорошо протестированы и надежны - в отличие от многих реализаций, которые вы можете найти в случайных публикациях в Интернете.
Не забудьте сделать ссылку на libresolv.dylib.
Потрясающие; намного лучше, чем случайный интернет-сайт! Если кто-то беспокоится об использовании этих скудно документированных функций, вы можете увидеть источник для них на сайте Apple.
Этот парень дает еще немного об этом: blog.montgomerie.net/ios-hidden-base64-routines
Я сделал это с помощью следующего класса ..
@implementation Base64Converter
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', '+', '/'
};
+ (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
Во время звонка звоните
[Base64Converter base64StringFromData:dataval length:lengthval];
Это оно...
Скачать Base64
Выполните следующий код для преобразования изображения в base64
NSString *base64String=[UIImagePNGRepresentation(image) base64Encoding];
Чтобы обновить использование методов категории NSData (NSDataBase64Encoding) в iOS7, см. Мой ответ здесь: https://stackoverflow.com/a/18927627/1602729
Лучшее решение:
В NSData есть встроенная функция
[data base64Encoding]; //iOS < 7.0
[data base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength]; //iOS >= 7.0
Мы можем сделать это на основе версии iOS, на которой работает приложение, используя "[[UIDevice currentDevice] systemVersion] .floatValue".
1. Это не скажет вам, с каким SDK вы связались, это проверка во время выполнения. 2. Это прямо противоречит рекомендациям Apple. Вы должны проверять наличие функции, а не версию системы.
В то время, когда этот вопрос был первоначально опубликован, люди по понятным причинам направляли вас к сторонним библиотекам base 64 из-за отсутствия каких-либо собственных подпрограмм. Но iOS 7 представила процедуры кодирования base 64 (который на самом деле просто предоставляет частные методы, которые iOS были возвращены к iOS 4).
Итак, вы можете использовать метод NSDatabase64EncodedStringWithOptions: для создания строки base-64 из NSData.
NSString *string = [data base64EncodedStringWithOptions:kNilOptions];
И вы можете использовать initWithBase64EncodedString:options: для преобразования строки base-64 обратно в NSData:
NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:kNilOptions];
Или в Swift:
let string = data.base64EncodedString()
А также
let data = Data(base64Encoded: string)
Спасибо, Роб. Не могли бы вы вкратце рассказать о том, что вы написали «... и предоставляет ранее закрытые методы iOS 4»?
Жаль, что этот ответ скрыт под всеми этими кастомными реализациями. Это слабое место SO, где более подходящее решение могло появиться спустя много времени после того, как был задан исходный вопрос, теперь это решение должно конкурировать с тем, что было принято ранее.
Вот почему всегда полезно голосовать за более свежие правильные ответы :)
почему, черт возьми, такие ответы не на высоте :(, я потратил много времени на обработку всех ответов выше T__T
@Rob, как вы относитесь к редактированию этого, чтобы удалить ссылки на iOS 4 (!!) и iOS 7, поскольку Xcode в наши дни даже не может настроить таргетинг ни на одну из них? Я думал сделать это сам, но подумал, что вы, возможно, предпочтете. :) Бонусные баллы за включение быстрого примера кода ...
Я собираюсь оставить ссылки на iOS для исторических целей (в противном случае люди будут сбиты с толку всеми этими ответами о том, как это сделать вручную) и не аннулировать комментарии выше. Но я упростил свой ответ (поскольку условная логика для версий iOS до iOS 7 больше не актуальна; люди могут посмотреть предыдущая редакция, если им интересно).
В iOS были встроенные методы кодирования и декодирования Base64 (без использования libresolv), начиная с iOS 4. Однако они были объявлены только в iOS 7 SDK. В документации Apple указано, что вы можете использовать его при таргетинге на iOS 4 и выше.
NSData *myData = ... some data
NSString *base64String = [myData base64Encoding];
NSData *decodedData = [[NSData alloc] initWithBase64Encoding:base64String];
Вот пример преобразования объекта NSData в Base 64. Он также показывает, как пойти другим путем (декодировать объект NSData в кодировке base 64):
NSData *dataTake2 =
[@"iOS Developer Tips" dataUsingEncoding:NSUTF8StringEncoding];
// Convert to Base64 data
NSData *base64Data = [dataTake2 base64EncodedDataWithOptions:0];
// Do something with the data...
// Now convert back from Base64
NSData *nsdataDecoded = [base64Data initWithBase64EncodedData:base64Data options:0];
Думаю, это будет полезно
+ (NSString *)toBase64String:(NSString *)string {
NSData *data = [string dataUsingEncoding: NSUnicodeStringEncoding];
NSString *ret = [data base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
return ret;
}
+ (NSString *)fromBase64String:(NSString *)string {
NSData *aData = [string dataUsingEncoding:NSUTF8StringEncoding];
NSData *aDataDecoded = [[NSData alloc]initWithBase64EncodedString:string options:0];
NSString *decryptedStr = [[NSString alloc]initWithData:aDataDecoded encoding:NSUTF8StringEncoding];
return [decryptedStr autorelease];
}
NSStringUtil? Дайте, пожалуйста, исчерпывающий ответ?
Это два метода, которые вам нужно написать в любом классе, и вы можете вызвать его и передать String instaces в качестве параметра.
в iOS 7
NSData *data=[[NSData alloc]init];
[data base64Encoding];
Нагарадж уже упоминал об этом. См. Его сообщение и комментарии к нему, в которых говорится, что он существует с iOS 4.
В iOS8 и более поздних версиях используйте - (NSString *)base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)options NSData.
В соответствии с вашим требованием я создал образец демонстрации с использованием Swift 4, в котором вы можете кодировать / декодировать строку и изображение в соответствии с вашими требованиями.
Я также добавил образцы методов соответствующих операций.
//
// Base64VC.swift
// SOF_SortArrayOfCustomObject
//
// Created by Test User on 09/01/18.
// Copyright © 2018 Test User. All rights reserved.
//
import UIKit
import Foundation
class Base64VC: NSObject {
//----------------------------------------------------------------
// MARK:-
// MARK:- String to Base64 Encode Methods
//----------------------------------------------------------------
func sampleStringEncodingAndDecoding() {
if let base64String = self.base64Encode(string: "TestString") {
print("Base64 Encoded String: \n\(base64String)")
if let originalString = self.base64Decode(base64String: base64String) {
print("Base64 Decoded String: \n\(originalString)")
}
}
}
//----------------------------------------------------------------
func base64Encode(string: String) -> String? {
if let stringData = string.data(using: .utf8) {
return stringData.base64EncodedString()
}
return nil
}
//----------------------------------------------------------------
func base64Decode(base64String: String) -> String? {
if let base64Data = Data(base64Encoded: base64String) {
return String(data: base64Data, encoding: .utf8)
}
return nil
}
//----------------------------------------------------------------
// MARK:-
// MARK:- Image to Base64 Encode Methods
//----------------------------------------------------------------
func sampleImageEncodingAndDecoding() {
if let base64ImageString = self.base64Encode(image: UIImage.init(named: "yourImageName")!) {
print("Base64 Encoded Image: \n\(base64ImageString)")
if let originaImage = self.base64Decode(base64ImageString: base64ImageString) {
print("originalImageData \n\(originaImage)")
}
}
}
//----------------------------------------------------------------
func base64Encode(image: UIImage) -> String? {
if let imageData = UIImagePNGRepresentation(image) {
return imageData.base64EncodedString()
}
return nil
}
//----------------------------------------------------------------
func base64Decode(base64ImageString: String) -> UIImage? {
if let base64Data = Data(base64Encoded: base64ImageString) {
return UIImage(data: base64Data)!
}
return nil
}
}
Внизу этого поста есть хороший пример кода. Очень самодостаточный ... БазаШестьдесят четыре