Как мне выполнить кодировку base64 на iOS?

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

Внизу этого поста есть хороший пример кода. Очень самодостаточный ... БазаШестьдесят четыре

Greg Bernhardt 17.01.2009 08:28

Ссылка @GregBernhardt мертва.

Cœur 08.06.2018 07:28
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
233
2
133 276
19

Ответы 19

Это хороший вариант использования цели 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 + /";

Artelius 05.05.2009 14:19

при использовании base64StringFromData должна ли длина быть длиной NSData? Я получаю только первые 4 символа, используя длину NSData. Спасибо

Larry Hipp 10.06.2009 09:22

Я понял, почему я получал только 4 символа ... Перед возвратом цикла while () должен быть}. Я бы отредактировал это, но похоже, что я не могу.

Larry Hipp 11.06.2009 03:05

он показывает ошибку error: ожидаемое выражение перед 'else' ошибка: ожидаемое выражение перед 'end'

Mahesh Babu 27.12.2010 11:39

Да, это не работает - среди прочего, put не заявлен в base64StringFromData ...

xil3 22.06.2011 18:25

В base64DataFromString: анализатор xcode говорит, что есть проблемы с двумя строками с inbuf[n] &. Он говорит: «Левый операнд '&' - это мусорное значение». Я не вижу проблемы с этим кодом, и поэтому я склонен думать, что это ошибка анализатора, но мне интересно, видит ли кто-нибудь проблему с кодом.

ThomasW 16.08.2011 11:53

Это не ошибка анализатора. Обратите внимание, что код также пытается получить доступ к inbuf [3], который находится за пределами этого массива. Этот код воняет.

Mike Weller 08.11.2011 11:44

Ниже приведены гораздо более быстрые решения, которые я бы рекомендовал использовать вместо этого.

Joshua Cohen 31.03.2012 07:17

Что представляет собой значение длины?

MegaManX 20.06.2012 15:04

Начиная с iOS7 Apple представила свой собственный метод кодирования base 64. См. Ответ Роба ниже, чтобы узнать, как его использовать при сохранении обратной совместимости.

Code Commander 07.08.2014 04:25

Поскольку это, кажется, хит номер один в 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

Alex Reynolds 07.05.2010 23:21

Не знаете, на что ссылаются base64EncodingTable1, base64EncodingTable2, base64EncodingTable3 и base64EncodingTable4?

Jamie Chapman 31.07.2010 17:34

Очень полезно, но может читать за пределами входного буфера. Когда (left == 2), raw [inp + 2] будет на один байт после конца tmpbuf. Я думаю, что строка должна быть такой: if (left == 2) outbuf [outp ++] = base64EncodingTable [((raw [inp + 1] & 0x0F) << 2)];

John Lemberger 15.11.2010 20:16

измените следующую строку <code> char tmpbuf [2] = {0,0}; </code> на <code> unsigned char tmpbuf [3] = {0,0,0}; </code>

Satya 11.04.2011 15:38

В отличном улучшении 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 Pitts 06.07.2010 04:16

@alastair, похоже, вы получаете "аккаунт" каждый раз, когда отправляете ответ без регистрации после очистки файлов cookie. Мне не удалось подключиться к своей первой «учетной записи» (даже с тем же адресом электронной почты и IP), поэтому я просто поместил ее в качестве нового ответа, извините за это. - только что зарегистрировались!

mvds 09.07.2010 01:07

Есть ли шанс отредактировать этот ответ на свой предыдущий, чтобы получить окончательно правильную версию? Спасибо!

JosephH 20.10.2011 16:28
#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;
}

Наконец-то правильная и действенная реализация. Спасибо. Другой код здесь меня пугает.

Mike Weller 08.11.2011 11:53

Кажется, утечка памяти, выделенной в кодировщике как strResult; ему просто нужен free() в конце (перед возвращением, но после NSString stringWithCString)

JosephH 25.11.2011 18:20

В вашем методе encodeBase64WithData:, разве первый параметр в вызове calloc() не нужно увеличивать на 1, чтобы учесть нулевой терминатор ('\0'), который вы добавляете в конце?

erikprice 31.01.2012 03:00

Тот факт, что яблоко не дает этого, заставляет Бога убивать котят ... многих из них ...

dsingleton 18.05.2012 20:50

Я использовал это некоторое время, и казалось, что он отлично работает, пока я не начал получать некоторые ошибки, связанные с повреждением памяти, и, используя охранный malloc, я сузил его до этой строки: * objPointer = '\ 0'; так что будьте осторожны, если вы используете это в своих собственных приложениях.

Mattia 20.07.2012 22:55

Для большей эффективности попробуйте NSData* objData = [NSData dataWithBytesNoCopy:objResult length:j freeWhenDone:YES].

rvalue 24.09.2012 06:53

Спасибо за разъяснение! Кровавый плагиат распространен в этих блогах ... :)

t0mm13b 03.05.2013 15:30

Начиная с iOS7 Apple представила свой собственный метод кодирования base 64. См. Ответ Роба ниже, чтобы узнать, как его использовать при сохранении обратной совместимости.

Code Commander 07.08.2014 04:26

Лучший ответ для кодирования Base64 без проблем с памятью .. Большое спасибо

Mani murugan 17.09.2015 15:26

iOS включает встроенную поддержку кодирования и декодирования base64. Если вы посмотрите на resolv.h, вы увидите две функции: b64_ntop и b64_pton. Библиотека Square SocketRocket предоставляет разумный пример того, как использовать эти функции из objective-c.

Эти функции довольно хорошо протестированы и надежны - в отличие от многих реализаций, которые вы можете найти в случайных публикациях в Интернете. Не забудьте сделать ссылку на libresolv.dylib.

Потрясающие; намного лучше, чем случайный интернет-сайт! Если кто-то беспокоится об использовании этих скудно документированных функций, вы можете увидеть источник для них на сайте Apple.

Jesse Rusak 20.04.2013 01:56

Этот парень дает еще немного об этом: blog.montgomerie.net/ios-hidden-base64-routines

Mike 05.02.2014 05:24

Я сделал это с помощью следующего класса ..

@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".

Nagaraj 24.10.2013 16:20

1. Это не скажет вам, с каким SDK вы связались, это проверка во время выполнения. 2. Это прямо противоречит рекомендациям Apple. Вы должны проверять наличие функции, а не версию системы.

quellish 24.10.2013 23:28

В то время, когда этот вопрос был первоначально опубликован, люди по понятным причинам направляли вас к сторонним библиотекам 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»?

phi 28.01.2014 12:21

Жаль, что этот ответ скрыт под всеми этими кастомными реализациями. Это слабое место SO, где более подходящее решение могло появиться спустя много времени после того, как был задан исходный вопрос, теперь это решение должно конкурировать с тем, что было принято ранее.

jakev 15.03.2014 05:22

Вот почему всегда полезно голосовать за более свежие правильные ответы :)

Steve Wilford 22.06.2015 21:37

почему, черт возьми, такие ответы не на высоте :(, я потратил много времени на обработку всех ответов выше T__T

Alsh compiler 03.07.2016 07:33

@Rob, как вы относитесь к редактированию этого, чтобы удалить ссылки на iOS 4 (!!) и iOS 7, поскольку Xcode в наши дни даже не может настроить таргетинг ни на одну из них? Я думал сделать это сам, но подумал, что вы, возможно, предпочтете. :) Бонусные баллы за включение быстрого примера кода ...

Abhi Beckert 10.09.2020 02:11

Я собираюсь оставить ссылки на iOS для исторических целей (в противном случае люди будут сбиты с толку всеми этими ответами о том, как это сделать вручную) и не аннулировать комментарии выше. Но я упростил свой ответ (поскольку условная логика для версий iOS до iOS 7 больше не актуальна; люди могут посмотреть предыдущая редакция, если им интересно).

Rob 10.09.2020 08:54

В 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? Дайте, пожалуйста, исчерпывающий ответ?

Mohsin Khubaib Ahmed 05.09.2015 21:32

Это два метода, которые вам нужно написать в любом классе, и вы можете вызвать его и передать String instaces в качестве параметра.

Mrug 07.09.2015 11:55

в iOS 7

        NSData *data=[[NSData alloc]init];
        [data base64Encoding];

Нагарадж уже упоминал об этом. См. Его сообщение и комментарии к нему, в которых говорится, что он существует с iOS 4.

jww 06.02.2014 12:27

В 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
        }
    
    
    }
    

Другие вопросы по теме