Comment puis-je faire l'encodage base64 sur iOS?

230

Je voudrais faire de l' base64encodage et du décodage, mais je n'ai trouvé aucun support de l'iPhone SDK. Comment puis-je base64encoder et décoder avec ou sans bibliothèque?

BlueDolphin
la source
1
Il y a un bel exemple de code au bas de cet article. Très autonome ... BaseSixtyFour
Greg Bernhardt
Le lien @GregBernhardt est mort.
Cœur

Réponses:

116

Ce cas est un bon usage pour Objective C catégories .

Pour l'encodage 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

Pour le décodage 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
Alex Reynolds
la source
5
Si Obj-C est quelque chose comme C, vous devriez pouvoir faire ceci: static char base64EncodingTable [64] = "ABCDE [etc] 789 + /";
Artelius
3
J'ai trouvé pourquoi je n'obtenais que 4 caractères ... Il doit y avoir un} avant le retour pour la boucle while (). Je l'éditerais mais je n'ai pas l'air de pouvoir le faire.
Larry Hipp
3
Ce n'est pas un bug de l'analyseur. Notez que le code tente également d'accéder à inbuf [3] qui est au-delà des limites de ce tableau. Ce code pue.
Mike Weller
1
Que représente la valeur de la longueur?
MegaManX
3
Depuis iOS7, Apple a dévoilé sa méthode de codage native en base 64. Voir la réponse de Rob ci-dessous pour savoir comment l'utiliser tout en conservant la compatibilité descendante.
Code Commander
100

Une implémentation vraiment, vraiment rapide qui a été portée (et modifiée / améliorée) de la bibliothèque PHP Core en code Objective-C natif est disponible dans la classe QSStrings de la bibliothèque QSUtilities . J'ai fait un test rapide: un fichier image (JPEG) de 5,3 Mo a pris <50 ms pour encoder et environ 140 ms pour décoder.

Le code de la bibliothèque entière (y compris les méthodes Base64) est disponible sur GitHub .

Ou bien, si vous voulez que le code ne concerne que les méthodes Base64, je l'ai posté ici:

Tout d'abord, vous avez besoin des tables de mappage:

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

Pour encoder:

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

Pour décoder:

+ (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
la source
2
Enfin une mise en œuvre correcte et efficace. Merci. Certains des autres codes ici me font peur.
Mike Weller
4
La mémoire allouée comme strResultdans l'encodeur semble avoir fui; il a juste besoin d'un free()à la fin (avant de revenir mais après NSString stringWithCString)
JosephH
2
Dans votre encodeBase64WithData:méthode, le premier paramètre de l'appel calloc()n'a-t-il pas besoin d'être incrémenté de 1 pour tenir compte du terminateur nul ( '\0') que vous ajoutez à la fin?
erikprice
1
Le fait que la pomme ne fournisse pas cela donne à Dieu envie de tuer des chatons ... beaucoup d'entre eux ...
dsingleton
2
J'utilise cela depuis un certain temps et cela a semblé fonctionner très bien jusqu'à ce que je commence à obtenir des erreurs liées à la corruption de mémoire et à l'aide de guard malloc, je l'ai réduit à cette ligne: * objPointer = '\ 0'; alors méfiez-vous si vous l'utilisez dans vos propres applications.
Mattia
72

Historiquement, nous vous aurions dirigé vers l'une des nombreuses bibliothèques de base 64 tierces (comme indiqué dans les autres réponses) pour la conversion des données binaires en chaîne de base 64 (et inversement), mais iOS 7 dispose désormais d'un codage de base 64 natif (et expose les méthodes iOS 4 précédemment privées, au cas où vous auriez besoin de prendre en charge des versions antérieures d'iOS).

Ainsi, pour convertir NSDataen NSStringreprésentation en base 64, vous pouvez utiliser base64EncodedStringWithOptions. Si vous devez également prendre en charge les versions iOS antérieures à 7.0, vous pouvez:

NSString *string;

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

Et pour reconvertir la base 64 NSString, NSDatavous pouvez l'utiliser initWithBase64EncodedString. De même, si vous devez prendre en charge les versions iOS antérieures à 7.0, vous pouvez faire:

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
}

De toute évidence, si vous n'avez pas besoin de compatibilité descendante avec les versions iOS antérieures à 7.0, c'est encore plus facile, utilisez simplement base64EncodedStringWithOptionsou initWithBase64EncodedString, respectivement, et ne vous embêtez pas avec la vérification de l'exécution pour les versions iOS antérieures. En fait, si vous utilisez le code ci-dessus lorsque votre cible minimale est iOS 7 ou supérieur, vous obtiendrez en fait un avertissement du compilateur concernant les méthodes obsolètes. Donc, dans iOS 7 et supérieur, vous convertiriez simplement en chaîne de base 64 avec:

NSString *string = [data base64EncodedStringWithOptions:kNilOptions];

et encore avec:

NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:kNilOptions]; 
Rob
la source
Merci pour ça Rob. Pourriez-vous s'il vous plaît développer brièvement ce que vous avez écrit, " ... et expose les méthodes iOS 4 précédemment privées "?
phi
8
C'est dommage que cette réponse soit enfouie sous toutes ces implémentations personnalisées. C'est une faiblesse de SO, où une solution plus appropriée peut être apparue longtemps après la question initiale, cette solution doit maintenant rivaliser avec ce qui était précédemment accepté.
jakev
C'est pourquoi il est toujours utile de voter pour des réponses correctes plus récemment :)
Steve Wilford
pourquoi les réponses de ce genre ne sont pas au top :(, j'ai passé beaucoup de temps à traiter toutes les réponses ci-dessus T__T
compilateur Alsh
33

iOS inclut une prise en charge intégrée pour l'encodage et le décodage base64. Si vous regardez, resolv.hvous devriez voir les deux fonctions b64_ntopet b64_pton. The Square SocketRocket bibliothèque fournit un exemple raisonnable de la façon d'utiliser ces fonctions à partir de objective-c.

Ces fonctions sont assez bien testées et fiables - contrairement à la plupart des implémentations que vous pouvez trouver dans des publications Internet aléatoires. N'oubliez pas de créer un lien contre libresolv.dylib.

quellish
la source
3
Impressionnant; tellement mieux qu'un site internet aléatoire! Si quelqu'un craint d'utiliser ces fonctions peu documentées, vous pouvez voir la source de ces fonctions sur le site d'Apple .
Jesse Rusak
1
Ce gars donne un peu plus d'informations à ce sujet: blog.montgomerie.net/ios-hidden-base64-routines
Mike
21

Comme cela semble être le numéro un de Google sur l'encodage base64 et l'iphone, j'ai eu envie de partager mon expérience avec l'extrait de code ci-dessus.

Cela fonctionne, mais c'est extrêmement lent. Un benchmark sur une image aléatoire (0,4 Mo) a pris 37 secondes sur l'iphone natif. La raison principale est probablement toute la magie OOP - NSStrings à caractère unique, etc., qui ne sont libérés automatiquement qu'après l'encodage.

Une autre suggestion publiée ici (ab) utilise la bibliothèque openssl, qui semble également exagérée.

Le code ci-dessous prend 70 ms - c'est une accélération de 500 fois. Cela ne fait que l'encodage base64 (le décodage suivra dès que je le rencontrerai)

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

J'ai laissé de côté la coupe car je n'en avais pas besoin, mais c'est trivial à ajouter.

Pour ceux qui souhaitent optimiser: l'objectif est de minimiser ce qui se passe dans la boucle principale. Par conséquent, toute logique pour traiter les 3 derniers octets est traitée en dehors de la boucle.

Essayez également de travailler sur les données sur place, sans copie supplémentaire vers / depuis les tampons. Et réduisez toute arithmétique au strict minimum.

Observez que les bits rassemblés pour rechercher une entrée dans le tableau ne se chevaucheraient pas lorsqu'ils devaient être redirigés sans décalage. Une amélioration majeure pourrait donc être d'utiliser 4 tables de recherche distinctes de 256 octets et d'éliminer les décalages, comme ceci:

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

Bien sûr, vous pourriez aller beaucoup plus loin, mais cela dépasse le cadre ici.

mvds
la source
Hmm. Je n'ai pas pu faire fonctionner ça. J'observe un encodage base64 différent de ma valeur attendue. Avez-vous testé cela avec les exemples de la RFC 4648? tools.ietf.org/html/rfc4648
Alex Reynolds
3
Vous avez du mal à voir à quoi se réfèrent base64EncodingTable1, base64EncodingTable2, base64EncodingTable3 et base64EncodingTable4?
Jamie Chapman
Très utile, mais il peut lire au-delà de la fin du tampon d'entrée. Lorsque (left == 2), raw [inp + 2] sera un octet au-delà de la fin de tmpbuf. Je pense que la ligne devrait être: if (left == 2) outbuf [outp ++] = base64EncodingTable [((raw [inp + 1] & 0x0F) << 2)];
John Lemberger du
changez la ligne suivante <code> char tmpbuf [2] = {0,0}; </code> en <code> unsigned char tmpbuf [3] = {0,0,0}; </code>
Satya
9

Dans l'excellente amélioration de mvds, il y a deux problèmes. Changez le code en ceci:

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
la source
9

Meilleure solution:

Il y a une fonction intégrée dans NSData

[data base64Encoding]; //iOS < 7.0
[data base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength]; //iOS >= 7.0
Nagaraj
la source
Nous pouvons le faire en fonction de la version iOS sur laquelle l'application s'exécute en utilisant "[[UIDevice currentDevice] systemVersion] .floatValue".
Nagaraj
2
1. Cela ne vous dirait pas avec quel SDK vous avez lié, c'est une vérification de l'exécution. 2. Cela va directement à l'encontre des directives d'Apple. Vous devez vérifier la disponibilité d'une fonctionnalité, pas la version du système.
quellish
6

Heureux que les gens aient aimé. La fin de partie était un peu défectueuse, je dois l'admettre. En plus de définir correctement inp = 0, vous devez également augmenter la taille de tmpbuf à 3, comme

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

ou omettre l'orring de raw [inp + 2]; si nous avions un [inp + 2] brut! = 0 pour ce morceau nous serions toujours dans la boucle bien sûr ...

Dans les deux cas, vous pouvez envisager de conserver le bloc de recherche de table final identique à celui de la boucle pour plus de clarté. Dans la version finale que j'ai utilisée, je l'ai fait

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

Pour ajouter le ==

Désolé, je n'ai pas vérifié les RFC et tout ça, j'aurais dû faire un meilleur travail!

mvds
la source
3
vous avez déjà un compte ici, car votre réponse précédente est en fait un compte différent. De plus, cela devrait être soit une modification de cela, soit un commentaire.
Alastair Pitts
@alastair, vous semblez avoir un "compte" à chaque fois que vous postez une réponse sans vous inscrire, après avoir nettoyé les cookies. Je n'ai pas pu me connecter à mon premier "compte" (même avec le même e-mail et la même adresse IP), je l'ai donc simplement ajouté comme nouvelle réponse, désolé pour cela. - vient de s'inscrire!
mvds
3
Y a-t-il une chance que vous puissiez modifier cette réponse dans votre précédente afin qu'il y ait une version correcte définitive? Merci!
JosephH
6

Sous iOS8 et utilisation ultérieure - (NSString *)base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)optionsde NSData

AlexeyVMP
la source
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
la source
2

Voici une version Objective-C compacte en tant que catégorie sur NSData. Il faut réfléchir à ...

@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

Le remplissage peut être ajouté si nécessaire en élargissant la portée de 'byt' et en ajoutant 'dest' avec (2-byt) caractères "=" avant de revenir.

Une catégorie peut ensuite être ajoutée à NSString, ainsi:

@implementation NSString (StringUtils)

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

 return [theData newStringInBase64FromData];
}

@end

la source
2

iOS a intégré des méthodes d'encodage et de décodage Base64 (sans utiliser libresolv) depuis iOS 4. Cependant, il n'a été déclaré que dans le SDK iOS 7. La documentation Apple indique que vous pouvez l'utiliser pour cibler iOS 4 et supérieur.

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

Voici un exemple pour convertir un objet NSData en Base 64. Il montre également comment procéder dans l'autre sens (décoder un objet NSData encodé en 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];
John Muchow
la source
1

dans iOS 7

        NSData *data=[[NSData alloc]init];
        [data base64Encoding];
Mani
la source
Nagaraj l'a déjà mentionné. Voir son article et les commentaires qui l'accompagnent qui indiquent qu'il existe depuis iOS 4.
jww
1

Je l'ai fait en utilisant la classe suivante ..

@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

Pendant l'appel appel

 [Base64Converter base64StringFromData:dataval length:lengthval];

C'est tout...

Durai Amuthan.H
la source
1

Je pense que ce sera utile

 + (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
la source
NSStringUtil? Veuillez donner une réponse complète?
Mohsin Khubaib Ahmed
1
Ce sont deux méthodes dont vous avez besoin pour écrire dans n'importe quelle classe et vous pouvez l'appeler et passer des instaces de chaîne en paramètre.
Mrug
0

Télécharger Base64

Faites le code suivant pour convertir une image en base64

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

Selon vos besoins, j'ai créé un exemple de démonstration à l'aide de Swift 4 dans lequel vous pouvez encoder / décoder des chaînes et des images selon vos besoins.

  • J'ai également ajouté des exemples de méthodes d'opérations pertinentes.

    //
    //  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
        }
    
    
    }
Équipe iOS
la source