Wie mache ich Base64-Codierung unter iOS?

230

Ich würde gerne base64codieren und decodieren, aber ich konnte keine Unterstützung vom iPhone finden SDK. Wie kann ich base64mit oder ohne Bibliothek codieren und decodieren?

BlueDolphin
quelle
1
Am Ende dieses Beitrags befindet sich ein schönes Codebeispiel. Sehr eigenständig ... BaseSixtyFour
Greg Bernhardt
@ GregBernhardt Link ist tot.
Cœur

Antworten:

116

Dies ist ein guter Anwendungsfall für Ziel-C- Kategorien .

Für die Base64-Codierung:

#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

Für die Base64-Decodierung:

#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
Alex Reynolds
quelle
5
Wenn Obj-C etwas wie C ist, sollten Sie in der Lage sein, Folgendes zu tun: static char base64EncodingTable [64] = "ABCDE [etc] 789 + /";
Artelius
3
Ich habe herausgefunden, warum ich nur 4 Zeichen bekommen habe ... Vor der Rückkehr für die while () -Schleife muss ein} stehen. Ich würde es bearbeiten, aber ich sehe nicht so aus, wie ich kann.
Larry Hipp
3
Es ist kein Analysator-Fehler. Beachten Sie, dass der Code auch versucht, auf inbuf [3] zuzugreifen, das außerhalb der Grenzen dieses Arrays liegt. Dieser Code stinkt.
Mike Weller
1
Was bedeutet Längenwert?
MegaManX
3
Ab iOS7 hat Apple seine native Base 64-Codierungsmethode verfügbar gemacht. In Robs Antwort unten finden Sie Informationen zur Verwendung unter Beibehaltung der Abwärtskompatibilität.
Code Commander
100

Eine wirklich sehr schnelle Implementierung, die aus der PHP Core-Bibliothek in nativen Objective-C-Code portiert (und geändert / verbessert) wurde, ist in der QSStrings-Klasse aus der QSUtilities-Bibliothek verfügbar . Ich habe einen schnellen Benchmark durchgeführt: Die Codierung einer 5,3-MB-Bilddatei (JPEG) dauerte <50 ms und die Dekodierung etwa 140 ms.

Der Code für die gesamte Bibliothek (einschließlich der Base64-Methoden) ist auf GitHub verfügbar .

Oder alternativ, wenn Sie möchten, dass der Code nur die Base64-Methoden selbst enthält, habe ich ihn hier veröffentlicht:

Zunächst benötigen Sie die Zuordnungstabellen:

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

So codieren Sie:

+ (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;
}

Zu dekodieren:

+ (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 Ho
quelle
2
Endlich eine korrekte und effiziente Umsetzung. Vielen Dank. Ein Teil des anderen Codes hier macht mir Angst.
Mike Weller
4
Der wie strResultim Encoder zugewiesene Speicher scheint durchgesickert zu sein; es braucht nur ein free()am Ende (vor der Rückkehr, aber danach NSString stringWithCString)
JosephH
2
Muss in Ihrer encodeBase64WithData:Methode nicht der erste Parameter im Aufruf calloc()um 1 erhöht werden, um den Nullterminator ( '\0') zu berücksichtigen, den Sie am Ende hinzufügen?
Erikpreis
1
Die Tatsache, dass Apple dies nicht bietet, lässt Gott Kätzchen ermorden ... viele von ihnen ...
Dsingleton
2
Ich benutze dies seit einer Weile und es schien großartig zu funktionieren, bis ich anfing, einige Fehler im Zusammenhang mit Speicherbeschädigung zu bekommen und Guard Malloc zu verwenden. Ich habe es auf diese Zeile eingegrenzt: * objPointer = '\ 0'; Seien Sie also vorsichtig, wenn Sie dies in Ihren eigenen Apps verwenden.
Mattia
72

In der Vergangenheit hätten wir Sie an eine der vielen Basis-64-Bibliotheken von Drittanbietern verwiesen (wie in den anderen Antworten erläutert), um von Binärdaten in Basis-64-Zeichenfolgen (und zurück) zu konvertieren, aber iOS 7 verfügt jetzt über eine native Basis-64-Codierung (und macht die zuvor privaten iOS 4-Methoden verfügbar (falls Sie frühere Versionen von iOS unterstützen müssen).

Sie können also eine Konvertierung NSDatain eine NSStringBasis-64-Darstellung verwenden base64EncodedStringWithOptions. Wenn Sie auch iOS-Versionen vor 7.0 unterstützen müssen, können Sie Folgendes tun:

NSString *string;

if ([data respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
    string = [data base64EncodedStringWithOptions:kNilOptions];  // iOS 7+
} else {
    string = [data base64Encoding];                              // pre iOS7
}

Und um die Basis 64 NSStringwieder zu konvertieren , können NSDataSie verwenden initWithBase64EncodedString. Wenn Sie iOS-Versionen vor 7.0 unterstützen müssen, können Sie Folgendes tun:

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
}

Wenn Sie vor 7.0 keine Abwärtskompatibilität mit iOS-Versionen benötigen, ist es natürlich noch einfacher, einfach base64EncodedStringWithOptionsbzw. zu initWithBase64EncodedStringverwenden und sich nicht um die Laufzeitprüfung für frühere iOS-Versionen zu kümmern. Wenn Sie den obigen Code verwenden, wenn Ihr Mindestziel iOS 7 oder höher ist, erhalten Sie tatsächlich eine Compiler-Warnung zu den veralteten Methoden. In iOS 7 und höher konvertieren Sie also einfach in eine Basis-64-Zeichenfolge mit:

NSString *string = [data base64EncodedStringWithOptions:kNilOptions];

und wieder zurück mit:

NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:kNilOptions]; 
rauben
quelle
Danke für diesen Rob. Könnten Sie bitte kurz erläutern, was Sie geschrieben haben: " ... und die zuvor privaten iOS 4-Methoden offenlegen "?
Phi
8
Es ist eine Schande, dass diese Antwort unter all diesen benutzerdefinierten Implementierungen verborgen ist. Es ist eine Schwäche von SO, wo eine geeignetere Lösung möglicherweise lange nach der ursprünglichen Frage zustande gekommen ist. Diese Lösung muss nun mit der zuvor akzeptierten konkurrieren.
Jakev
Deshalb ist es immer nützlich, neuere richtige Antworten zu bewerten :)
Steve Wilford
warum zum Teufel Antworten wie diese nicht oben sind :(, ich habe viel Zeit damit verbracht, alle Antworten über T__T
Alsh Compiler
33

iOS bietet integrierte Unterstützung für die Base64-Codierung und -Decodierung. Wenn Sie sich ansehen resolv.h, sollten Sie die beiden Funktionen b64_ntopund sehen b64_pton. Die Square SocketRocket- Bibliothek bietet ein vernünftiges Beispiel für die Verwendung dieser Funktionen von Objective-C.

Diese Funktionen sind ziemlich gut getestet und zuverlässig - im Gegensatz zu vielen Implementierungen, die Sie in zufälligen Internet-Postings finden können. Vergiss nicht, gegen zu verlinken libresolv.dylib.

quellish
quelle
3
Genial; so viel besser als zufällige Internetseiten! Falls jemand Bedenken hat, diese kaum dokumentierten Funktionen zu verwenden, finden Sie die Quelle für diese auf der Apple-Website .
Jesse Rusak
1
Dieser Typ gibt etwas mehr Hintergrundinformationen: blog.montgomerie.net/ios-hidden-base64-routines
Mike
21

Da dies der Google-Hit Nummer eins bei Base64-Codierung und iPhone zu sein scheint, wollte ich meine Erfahrungen mit dem obigen Code-Snippet teilen.

Es funktioniert, aber es ist extrem langsam. Ein Benchmark für ein zufälliges Bild (0,4 MB) dauerte auf dem nativen iPhone 37 Sekunden. Der Hauptgrund ist wahrscheinlich die gesamte OOP-Magie - Einzelzeichen-NSStrings usw., die erst nach Abschluss der Codierung automatisch freigegeben werden.

Ein anderer Vorschlag, der hier (ab) veröffentlicht wurde, verwendet die openssl-Bibliothek, die sich ebenfalls wie ein Overkill anfühlt.

Der folgende Code dauert 70 ms - das ist eine 500-fache Beschleunigung. Dies führt nur eine Base64-Codierung durch (die Decodierung erfolgt, sobald ich darauf stoße).

+ (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;
}

Ich habe das Linienschneiden weggelassen, da ich es nicht brauchte, aber es ist trivial hinzuzufügen.

Für diejenigen, die an einer Optimierung interessiert sind: Ziel ist es, das Geschehen in der Hauptschleife zu minimieren. Daher wird die gesamte Logik zur Behandlung der letzten 3 Bytes außerhalb der Schleife behandelt.

Versuchen Sie außerdem, Daten direkt zu bearbeiten, ohne sie zusätzlich in / aus den Puffern zu kopieren. Und reduzieren Sie jede Arithmetik auf das Nötigste.

Beachten Sie, dass sich die Bits, die zusammengesetzt werden, um einen Eintrag in der Tabelle nachzuschlagen, nicht überlappen würden, wenn sie ohne Verschiebung zusammengefügt würden. Eine wesentliche Verbesserung könnte daher darin bestehen, 4 separate 256-Byte-Nachschlagetabellen zu verwenden und die Verschiebungen wie folgt zu beseitigen:

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];

Natürlich könnte man noch viel weiter gehen, aber das geht hier über den Rahmen hinaus.

mvds
quelle
Hmm. Ich konnte das nicht zum Laufen bringen. Ich beobachte eine andere Base64-Codierung als mein erwarteter Wert. Haben Sie dies anhand der Beispiele in RFC 4648 getestet? tools.ietf.org/html/rfc4648
Alex Reynolds
3
Sie haben Probleme zu sehen, auf was base64EncodingTable1, base64EncodingTable2, base64EncodingTable3 und base64EncodingTable4 verweisen?
Jamie Chapman
Sehr hilfreich, kann aber über das Ende des Eingabepuffers hinaus lesen. Wenn (left == 2), ist raw [inp + 2] ein Byte hinter dem Ende von tmpbuf. Ich denke, die Zeile sollte lauten: if (left == 2) outbuf [outp ++] = base64EncodingTable [((raw [inp + 1] & 0x0F) << 2)];
John Lemberger
Ändern Sie die folgende Zeile <code> char tmpbuf [2] = {0,0}; </ code> in <code> unsigned char tmpbuf [3] = {0,0,0}; </ code>
Satya
9

Bei der hervorragenden Verbesserung von mvds gibt es zwei Probleme. Ändern Sie den Code in diesen:

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++] = '=';
user335742
quelle
9

Bessere Lösung:

In NSData ist eine Funktion integriert

[data base64Encoding]; //iOS < 7.0
[data base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength]; //iOS >= 7.0
Nagaraj
quelle
Wir können dies basierend auf der iOS-Version tun, auf der die App mit "[[UIDevice currentDevice] systemVersion] .floatValue" ausgeführt wird.
Nagaraj
2
1. Das würde Ihnen nicht sagen, mit welchem ​​SDK Sie verlinkt haben, das ist eine Laufzeitprüfung. 2. Das widerspricht direkt den Richtlinien von Apple. Sie sollten die Verfügbarkeit einer Funktion überprüfen, nicht die Systemversion.
quellish
6

Ich bin froh, dass es den Leuten gefallen hat. Das Endspiel war ein wenig fehlerhaft, muss ich zugeben. Neben der richtigen Einstellung von inp = 0 sollten Sie entweder auch die Größe von tmpbuf auf 3 erhöhen, z

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

oder lassen Sie den rohen Orring weg [inp + 2]; Wenn wir für dieses Stück ein rohes [inp + 2]! = 0 hätten, wären wir natürlich immer noch auf dem Laufenden ...

In beiden Fällen können Sie aus Gründen der Übersichtlichkeit in Betracht ziehen, den Final Table-Suchblock mit dem in der Schleife identischen Block identisch zu halten. In der endgültigen Version, die ich verwendet habe, habe ich getan

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

Um das == hinzuzufügen

Tut mir leid, dass ich RFCs und andere Dinge nicht überprüft habe, hätte einen besseren Job machen sollen!

mvds
quelle
3
Sie haben hier bereits ein Konto, da Ihre vorherige Antwort tatsächlich ein anderes Konto ist. Dies sollte entweder eine Bearbeitung oder ein Kommentar sein.
Alastair Pitts
@alastair, Sie scheinen jedes Mal ein "Konto" zu erhalten, wenn Sie eine Antwort veröffentlichen, ohne sich zu registrieren, nachdem Sie Cookies bereinigt haben. Ich konnte keine Verbindung zu meinem ersten "Konto" herstellen (auch nicht mit derselben E-Mail- und IP-Adresse), daher habe ich es nur als neue Antwort dort abgelegt. Entschuldigung. - gerade registriert!
MVDS
3
Gibt es eine Möglichkeit, diese Antwort in Ihre vorherige zu bearbeiten, damit es eine definitiv korrekte Version gibt? Vielen Dank!
JosephH
6

Unter iOS8 und höher Verwendung - (NSString *)base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)optionsvon NSData

AlexeyVMP
quelle
3
#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
alpha09jp
quelle
2

Hier ist eine kompakte Objective-C-Version als Kategorie für NSData. Man muss nachdenken ...

@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

Bei Bedarf kann eine Auffüllung hinzugefügt werden, indem der Bereich von 'byt' erweitert und 'dest' vor der Rückkehr mit (2-byt) "=" Zeichen angehängt wird.

Eine Kategorie kann dann zu NSString hinzugefügt werden, also:

@implementation NSString (StringUtils)

- (NSString *)newStringInBase64FromString
{
 NSData *theData = [NSData dataWithBytes:[self UTF8String] length:[self length]]; 

 return [theData newStringInBase64FromData];
}

@end

quelle
2

iOS verfügt seit iOS 4 über integrierte Base64-Codierungs- und -Decodierungsmethoden (ohne Verwendung von Libresolv). Diese wurden jedoch nur im iOS 7 SDK deklariert. In der Apple-Dokumentation heißt es, dass Sie es für iOS 4 und höher verwenden können.

NSData *myData = ... some data
NSString *base64String = [myData base64Encoding];
NSData *decodedData = [[NSData alloc] initWithBase64Encoding:base64String];
user102008
quelle
2

Hier ist ein Beispiel zum Konvertieren eines NSData-Objekts in Base 64. Es zeigt auch, wie Sie in die andere Richtung gehen (ein Base 64-codiertes NSData-Objekt dekodieren):

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];
John Muchow
quelle
1

in iOS 7

        NSData *data=[[NSData alloc]init];
        [data base64Encoding];
Mani
quelle
Nagaraj hat dies bereits erwähnt. Siehe seinen Beitrag und die dazugehörigen Kommentare, die besagen, dass er seit iOS 4 vorhanden ist.
jww
1

Ich habe es mit der folgenden Klasse gemacht ..

@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

Während des Anrufs

 [Base64Converter base64StringFromData:dataval length:lengthval];

Das ist es...

Durai Amuthan.H
quelle
1

Ich denke, das wird hilfreich sein

 + (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];

}}

Mrug
quelle
NSStringUtil? Bitte geben Sie eine vollständige Antwort?
Mohsin Khubaib Ahmed
1
Dies sind zwei Methoden, die Sie in eine beliebige Klasse schreiben müssen. Sie können sie aufrufen und String-Instanzen als Parameter übergeben.
Mrug
0

Laden Sie Base64 herunter

Führen Sie den folgenden Code aus, um ein Bild in base64 zu konvertieren

NSString *base64String=[UIImagePNGRepresentation(image) base64Encoding];
Pankaj Wadhwa
quelle
0

Gemäß Ihrer Anforderung habe ich mit Swift 4 eine Beispieldemo erstellt, in der Sie Zeichenfolge und Bild gemäß Ihrer Anforderung codieren / decodieren können.

  • Ich habe auch Beispielmethoden für relevante Operationen hinzugefügt.

    //
    //  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
        }
    
    
    }
iOS-Team
quelle