当前位置:  开发笔记 > 编程语言 > 正文

如何在iOS上进行base64编码?

如何解决《如何在iOS上进行base64编码?》经验,为你挑选了9个好方法。

我想做base64编码和解码,但我找不到iPhone的任何支持SDK.如何在base64有或没有库的情况下进行编码和解码?



1> Alex Reynold..:

这是Objective C 类别的一个很好的用例.

对于Base64编码:

#import 

@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 

@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 + /";
我发现为什么我只得到4个字符...在while()循环返回之前需要有一个}.我会编辑它,但我看起来不像我能.
这不是分析器错误.请注意,代码也尝试访问超出该数组范围的inbuf [3].这段代码很臭.
截至iOS7,Apple已经公开了他们的原生base 64编码方法.请参阅下面的Rob的答案,了解如何使用它,同时保持向后兼容性.

2> 小智..:

QSUtilities库QSSt​​rings类中可以获得从PHP核心库移植(并修改/改进)到本机Objective-C代码的真正非常快速的实现.我做了一个快速的基准测试:5.3MB图像(JPEG)文件需要<50ms进行编码,大约需要140ms进行解码.

GitHub上提供了整个库的代码(包括Base64方法).

或者,如果您希望代码本身只是 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'`)?
我已经使用了一段时间,它似乎工作得很好,直到我开始得到一些内存损坏相关的错误并使用guard malloc我把它缩小到这一行:*objPointer ='\ 0'; 所以请注意,如果您在自己的应用程序中使用它.

3> Rob..:

从历史上看,我们会将您引导到许多第三方base 64库之一(如其他答案中所讨论的),用于从二进制数据转换为base 64字符串(和返回),但iOS 7现在具有本机base 64编码(和暴露以前私有的iOS 4方法,以防您需要支持早期版本的iOS).

因此,要转换NSDataNSStringbase 64表示,您可以使用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转换NSStringNSData你可以使用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
}

显然,如果您不需要向后兼容7.0之前的iOS版本,那么它就更容易了,只需使用base64EncodedStringWithOptionsinitWithBase64EncodedString分别使用早期iOS版本的运行时检查.实际上,如果您在最小目标是iOS 7或更高版本时使用上面的代码,那么您实际上会收到有关已弃用方法的编译器警告.因此,在iOS 7及更高版本中,您只需转换为base 64字符串:

NSString *string = [data base64EncodedStringWithOptions:kNilOptions];

再回来:

NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:kNilOptions]; 


遗憾的是,这个答案被埋没在所有这些自定义实现之下.这是SO的弱点,在提出原始问题很久之后可能会出现更合适的解决方案,现在该解决方案必须与之前接受的解决方案竞争.

4> quellish..:

iOS包含对base64编码和解码的内置支持.如果你看一下resolv.h你应该看到两个功能b64_ntopb64_pton.Square SocketRocket库提供了如何从objective-c使用这些函数的合理示例.

这些功能经过了充分的测试和可靠 - 与您在随机网络帖子中找到的许多实现不同.别忘了链接libresolv.dylib.


真棒; 比随机的网站好多了!如果有人担心使用这些记录在案的功能,你可以在Apple的网站上看到[这些来源](http://www.opensource.apple.com/source/libresolv/libresolv-51/base64.c) .

5> 小智..:

由于这似乎是谷歌在base64编码和iphone上的第一名,我觉得我喜欢用上面的代码片段分享我的经验.

它有效,但速度极慢.随机图像(0.4 mb)的基准测试在原生iphone上花了37秒.主要原因可能是所有OOP魔术 - 单个字符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];

当然你可以更进一步,但这超出了这里的范围.


苦苦挣扎,看看base64EncodingTable1,base64EncodingTable2,base64EncodingTable3和base64EncodingTable4是什么引用的?

6> 小智..:

在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++] = '=';



7> 小智..:

更好的方案:

NSData中有一个内置函数

[data base64Encoding]; //iOS < 7.0
[data base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength]; //iOS >= 7.0


那不会告诉你你链接的是什么SDK,那就是运行时检查.这与Apple的指导背道而驰.您应该检查功能的可用性,而不是系统版本.

8> mvds..:

很高兴的人喜欢它.我必须承认,最终的比赛有点瑕疵.除了正确设置inp = 0之外,你还应该将tmpbuf的大小增加到3,就像

unsigned char tmpbuf[3] = {0,0,0};

遗漏原始[inp + 2]; 如果我们有一个原始[inp + 2]!= 0这个块我们仍然会在循环中...

无论哪种方式都有效,您可以考虑保持最终表查找块与循环中的相同,以便清楚.在我使用的最终版本中

while ( outp%4 ) outbuf[outp++] = '=';

要添加==

对不起,我没有检查RFC和东西,应该做得更好!


你已经在这里有一个帐户,因为你以前的答案实际上是一个不同的帐户.此外,这应该是对该注释或注释的编辑.
您是否有机会将此答案编辑到上一个答案中,以便有一个明确正确的版本?谢谢!

9> AlexeyVMP..:

在iOS8及以后使用- (NSString *)base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)optionsNSData

推荐阅读
Life一切安好
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有