Jak zrobić kodowanie base64 na iOS?

230

Chciałbym base64kodować i dekodować, ale nie mogłem znaleźć wsparcia z iPhone'a SDK. Jak mogę base64kodować i dekodować z biblioteką lub bez?

BlueDolphin
źródło
1
Na dole tego postu znajduje się ładna próbka kodu. Bardzo samodzielny ... BaseSixtyFour
Greg Bernhardt
@GregBernhardt link nie działa.
Cœur

Odpowiedzi:

116

Jest to sprawa dobrego wykorzystania na Objective C kategoriach .

W przypadku kodowania 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

W przypadku dekodowania 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
źródło
5
Jeśli Obj-C jest jak C, powinieneś móc to zrobić: static char base64EncodingTable [64] = "ABCDE [etc] 789 + /";
Artelius
3
Dowiedziałem się, dlaczego otrzymałem tylko 4 znaki ... Przed powrotem do pętli while () musi być}. Chciałbym go edytować, ale nie wyglądam, jakbym mógł.
Larry Hipp
3
To nie jest błąd analizatora. Zauważ, że kod próbuje również uzyskać dostęp do inbuf [3], który jest poza granicami tej tablicy. Ten kod śmierdzi.
Mike Weller,
1
Co oznacza wartość długości?
MegaManX,
3
Począwszy od iOS7, Apple ujawniło swoją natywną metodę kodowania base 64. Zobacz odpowiedź Roba poniżej, aby dowiedzieć się, jak go używać, zachowując zgodność wsteczną.
Code Commander
100

Naprawdę bardzo szybka implementacja, która została przeniesiona (i zmodyfikowana / ulepszona) z biblioteki PHP Core do natywnego kodu Objective-C jest dostępna w klasie QSStrings z biblioteki QSUtilities . Zrobiłem szybki test porównawczy: plik obrazu w formacie 5,3 MB (JPEG) kodował <50 ms, a dekodowanie - około 140 ms.

Kod dla całej biblioteki (w tym metod Base64) jest dostępny na GitHub .

Lub, jeśli chcesz, aby kod był tylko metodami Base64, opublikowałem go tutaj:

Najpierw potrzebujesz tabel mapowania:

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

Aby zakodować:

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

Aby zdekodować:

+ (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
źródło
2
Wreszcie prawidłowe i wydajne wdrożenie. Dzięki. Niektóre inne kody tutaj mnie przerażają.
Mike Weller,
4
Pamięć przydzielona jak strResultw enkoderze wydaje się przeciekać; potrzebuje tylko free()na końcu (przed powrotem, ale później NSString stringWithCString)
JosephH
2
Czy w twojej encodeBase64WithData:metodzie pierwszy parametr w wywołaniu nie calloc()musi być zwiększany o 1, aby uwzględnić terminator zerowy ( '\0'), który dodajesz na końcu?
erikprice
1
Fakt, że jabłko tego nie zapewnia, sprawia, że ​​Bóg chce mordować kocięta ... wiele z nich ...
dsingleton
2
Używam tego przez jakiś czas i wydawało się, że działa świetnie, dopóki nie zacząłem dostawać błędów związanych z uszkodzeniem pamięci i używając malloc guard zawęziłem go do następującej linii: * objPointer = '\ 0'; więc uważaj, jeśli używasz tego we własnych aplikacjach.
Mattia,
72

Historycznie kierowalibyśmy cię do jednej z wielu zewnętrznych bibliotek 64 bazowych (jak omówiono w innych odpowiedziach) w celu konwersji danych binarnych na ciąg 64 bazowy (i wstecz), ale iOS 7 ma teraz natywne kodowanie bazowe 64 (i ujawnia wcześniej prywatne metody iOS 4, na wypadek, gdybyś musiał obsługiwać wcześniejsze wersje iOS).

Zatem do konwersji NSDatana NSStringpodstawową reprezentację 64 możesz użyć base64EncodedStringWithOptions. Jeśli musisz obsługiwać również wersje iOS wcześniejsze niż 7.0, możesz:

NSString *string;

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

Aby przekonwertować bazę 64 z NSStringpowrotem NSData, możesz użyć initWithBase64EncodedString. Podobnie, jeśli potrzebujesz obsługiwać wersje iOS wcześniejsze niż 7.0, możesz:

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
}

Oczywiście, jeśli nie potrzebujesz wstecznej kompatybilności z wersjami iOS wcześniejszymi niż 7.0, jest to nawet łatwiejsze, po prostu użyj base64EncodedStringWithOptionslub initWithBase64EncodedString, odpowiednio, i nie zawracaj sobie głowy sprawdzaniem czasu działania wcześniejszych wersji iOS. W rzeczywistości, jeśli użyjesz powyższego kodu, gdy twój minimalny cel to iOS 7 lub wyższy, otrzymasz ostrzeżenie kompilatora o przestarzałych metodach. Tak więc, w iOS 7 i nowszych, po prostu konwertujesz na podstawowy ciąg 64 za pomocą:

NSString *string = [data base64EncodedStringWithOptions:kNilOptions];

i wróć z:

NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:kNilOptions]; 
Obrabować
źródło
Dzięki za ten Rob. Czy mógłbyś krótko rozwinąć to, co napisałeś: „ ... i ujawnia wcześniej prywatne metody iOS 4 ”?
pt
8
Szkoda, że ​​ta odpowiedź jest ukryta pod wszystkimi niestandardowymi implementacjami. Jest to słabość SO, gdzie bardziej odpowiednie rozwiązanie mogło powstać długo po zadaniu pierwotnego pytania, to rozwiązanie musi teraz konkurować z tym, co wcześniej zaakceptowano.
jakev
Właśnie dlatego zawsze warto głosować na poprawniejsze odpowiedzi :)
Steve Wilford
dlaczego do cholery takie odpowiedzi nie są na górze :(, spędziłem dużo czasu przetwarzając wszystkie odpowiedzi powyżej T__T
kompilator Alsh
33

iOS zawiera wbudowaną obsługę kodowania i dekodowania base64. Jeśli spojrzysz na resolv.hto powinieneś zobaczyć dwie funkcje b64_ntopi b64_pton. Biblioteka Square SocketRocket stanowi rozsądny przykład użycia tych funkcji z celu-c.

Funkcje te są dość dobrze przetestowane i niezawodne - w przeciwieństwie do wielu implementacji, które można znaleźć w losowych publikacjach internetowych. Nie zapomnij o linkowaniu przeciwko libresolv.dylib.

zgasić
źródło
3
Niesamowite; o wiele lepiej niż losowa strona internetowa! W przypadku, gdy ktoś martwi się użyciem tych skąpo udokumentowanych funkcji, możesz zobaczyć ich źródło na stronie Apple .
Jesse Rusak,
1
Ten facet daje trochę więcej informacji na ten temat: blog.montgomerie.net/ios-hidden-base64-routines
Mike
21

Ponieważ wydaje się, że jest to hit Google numer jeden w kodowaniu base64 i iPhone, chciałem podzielić się swoim doświadczeniem z powyższym fragmentem kodu.

Działa, ale jest bardzo wolny. Test porównawczy na losowym obrazie (0,4 MB) trwał 37 sekund w przypadku natywnego telefonu iPhone. Głównym powodem jest prawdopodobnie cała magia OOP - pojedyncze znaki NSStrings itp., Które są automatycznie wydawane dopiero po zakończeniu kodowania.

Inna sugestia opublikowana tutaj (ab) korzysta z biblioteki openssl, która również wydaje się przesadna.

Poniższy kod zajmuje 70 ms - to 500-krotne przyspieszenie. Dotyczy to tylko kodowania base64 (dekodowanie nastąpi, gdy tylko go napotkam)

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

Pominąłem wycinanie linii, ponieważ go nie potrzebowałem, ale dodanie tego jest banalne.

Dla tych, którzy są zainteresowani optymalizacją: celem jest zminimalizowanie tego, co dzieje się w głównej pętli. Dlatego cała logika zajmująca się ostatnimi 3 bajtami jest przetwarzana poza pętlą.

Spróbuj także pracować na danych w miejscu, bez dodatkowego kopiowania do / z buforów. I zredukuj dowolną arytmetykę do absolutnego minimum.

Zauważ, że bity, które są zebrane razem w celu wyszukania wpisu w tabeli, nie nakładałyby się, gdy miały być uporządkowane razem bez przesuwania. Istotnym ulepszeniem może być zatem użycie 4 oddzielnych 256-bajtowych tabel odnośników i wyeliminowanie przesunięć, takich jak to:

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

Oczywiście możesz pójść o wiele dalej, ale nie jest to tutaj możliwe.

mvds
źródło
Hmm Nie mogłem tego uruchomić. Obserwuję inne kodowanie base64 niż moja oczekiwana wartość. Czy przetestowałeś to na przykładach w RFC 4648? tools.ietf.org/html/rfc4648
Alex Reynolds
3
Próbujesz zobaczyć, do czego odwołują się base64EncodingTable1, base64EncodingTable2, base64EncodingTable3 i base64EncodingTable4?
Jamie Chapman
Bardzo pomocne, ale potrafi czytać poza końcem bufora wejściowego. Kiedy (left == 2), raw [inp + 2] będzie jeden bajt poza końcem tmpbuf. Myślę, że wiersz powinien brzmieć: if (left == 2) outbuf [outp ++] = base64EncodingTable [((raw [inp + 1] i 0x0F) << 2)];
John Lemberger,
zmień następujący wiersz <code> char tmpbuf [2] = {0,0}; </code> na <code> unsigned char tmpbuf [3] = {0,0,0}; </code>
Satya
9

W doskonałej poprawie mvds występują dwa problemy. Zmień kod na ten:

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++] = '=';
użytkownik335742
źródło
9

Lepsze rozwiązanie:

W NSData jest wbudowana funkcja

[data base64Encoding]; //iOS < 7.0
[data base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength]; //iOS >= 7.0
Nagaraj
źródło
Możemy to zrobić w oparciu o wersję iOS, na której działa aplikacja, używając „[[UIDevice currentDevice] systemVersion] .floatValue”.
Nagaraj
2
1. To nie powiedziałoby ci, z jakim SDK się łączyłeś, to jest kontrola czasu wykonywania. 2. Jest to sprzeczne z wytycznymi Apple. Powinieneś sprawdzić dostępność funkcji, a nie wersję systemową.
quellish
6

Cieszę się, że ludziom się to podobało. Muszę przyznać, że gra końcowa była trochę wadliwa. Oprócz prawidłowego ustawienia inp = 0 powinieneś także zwiększyć rozmiar tmpbuf do 3, np

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

lub pomiń orring surowego [inp + 2]; jeśli mielibyśmy surowy [inp + 2]! = 0 dla tej części, nadal bylibyśmy w pętli oczywiście ...

Tak czy inaczej działa, możesz dla zachowania przejrzystości zatrzymać ostatni blok wyszukiwania tabeli identyczny z blokiem w pętli. W ostatecznej wersji, której użyłem, zrobiłem

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

Aby dodać ==

Przepraszam, że nie sprawdziłem RFC i innych rzeczy, powinienem był wykonać lepszą robotę!

mvds
źródło
3
masz już tutaj konto, ponieważ Twoja poprzednia odpowiedź jest w rzeczywistości innym kontem. Ponadto powinna to być edycja tego lub komentarz.
Alastair Pitts,
@alastair, wydaje się, że dostajesz „konto” za każdym razem, gdy publikujesz odpowiedź bez rejestracji, po wyczyszczeniu plików cookie. Nie mogłem połączyć się z moim pierwszym „kontem” (nawet z tym samym adresem e-mail i adresem IP), więc po prostu umieściłem go jako nową odpowiedź, przepraszam za to. - właśnie zarejestrowany!
mvds,
3
Czy jest szansa, że ​​możesz edytować tę odpowiedź na poprzednią, aby uzyskać ostateczną poprawną wersję? Dzięki!
JosephH
6

W iOS8 i nowszych - (NSString *)base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)optionswersjach NSData

AlexeyVMP
źródło
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
źródło
2

Oto kompaktowa wersja Objective-C jako kategoria na NSData. Trochę myślenia o ...

@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

W razie potrzeby można dodać dopełnienie, rozszerzając zakres „byt” i dodając „dest” znakami (2-byt) „=” przed powrotem.

Kategorię można następnie dodać do NSString, a zatem:

@implementation NSString (StringUtils)

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

 return [theData newStringInBase64FromData];
}

@end

źródło
2

iOS ma wbudowane metody kodowania i dekodowania Base64 (bez użycia libresolv) od iOS 4. Zostało to jednak zadeklarowane tylko w zestawie SDK dla iOS 7. Dokumentacja Apple mówi, że możesz jej użyć, gdy celujesz w iOS 4 i wyżej.

NSData *myData = ... some data
NSString *base64String = [myData base64Encoding];
NSData *decodedData = [[NSData alloc] initWithBase64Encoding:base64String];
użytkownik102008
źródło
2

Oto przykład przekonwertowania obiektu NSData na Base 64. Pokazuje także, jak przejść w drugą stronę (dekodować obiekt NSData zakodowany w 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
źródło
1

w iOS 7

        NSData *data=[[NSData alloc]init];
        [data base64Encoding];
Mani
źródło
Nagaraj już o tym wspomniał. Zobacz jego post i komentarze, które towarzyszą temu stwierdzeniu, że był tam od iOS 4.
jww
1

Zrobiłem to przy użyciu następującej klasy ..

@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

Podczas połączenia telefonicznego

 [Base64Converter base64StringFromData:dataval length:lengthval];

Otóż ​​to...

Durai Amuthan.H
źródło
1

Myślę, że to będzie pomocne

 + (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
źródło
NSStringUtil? Proszę podać pełną odpowiedź?
Mohsin Khubaib Ahmed
1
Są to dwie metody, które musisz napisać w dowolnej klasie. Możesz ją wywołać i przekazać instancje String jako parametr.
Mrug
0

Pobierz Base64

Wykonaj następujący kod, aby przekonwertować obraz na base64

NSString *base64String=[UIImagePNGRepresentation(image) base64Encoding];
Pankaj Wadhwa
źródło
0

Zgodnie z twoim wymaganiem stworzyłem przykładową wersję demo przy użyciu Swift 4, w której możesz zakodować / zdekodować ciąg i obraz zgodnie z wymaganiami.

  • Dodałem również przykładowe metody odpowiednich operacji.

    //
    //  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
        }
    
    
    }
Zespół iOS
źródło