[Objective-C] base64エンコード、デコードを実装する & 64進数

カタログ
  1. 1. .h の中身
  2. 2. .m の中身
  3. 3. 使い方メモ : Base64エンコ/デコード
  4. 4. 使い方メモ : 64進数

職業訓練で製作したアプリで Base64エンコード を使いたかったけどFoundationフレームワークにはBase64エンコードする関数がない…ということでテキトウに実装した。かなり強引な部分があるけどまぁいいか。F8とか混ざってる関数・メソッドは自分専用な感じ。

[追記 20151220] iOS7からNSDataに標準でBase64を扱う関数が使える

.h の中身

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#import <Foundation/Foundation.h>

// 64進数のchar値を6bitの数値に変換するためのテーブル
// 64進数の並びは 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
// 通常のbase64エンコードとは文字の割当が違うので注意
const char tableForF8Base64CharToDec[256];

// 6bitバイナリを64進数のchar値に変換するためのテーブル
// 64進数の並びは 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
// 通常のbase64エンコードとは文字の割当が違うので注意
const char tableForDecToF8Base64Char[64];

// 64進数のchar値を6bit数値に変換するためのテーブル
const char tableForBase64CharToDec[256];

// 6bitバイナリを64進数のchar値に変換するためのテーブル
const char tableForDecToBase64Char[64];

// 64進数char文字列を 数値に変換する
NSUInteger F8Base64CharsToDec (const char *cp);
NSUInteger base64CharsToDec (const char *cp);

// 数値を 64進数文字列に変換する
NSString* decToF8Base64String (NSUInteger dec);
NSString* decToBase64String (NSUInteger dec);

@interface NSData (NSDataF8Base64)

// 自作のbase64デコード
// パッディングなし
+ (NSData *)dataWithF8Base64String:(NSString *)base64String;

// 普通のbase64デコード
// 引数には 無改行のbase64文字列を入れる
// 改行などは無視されずに 0 として扱うので注意
// 引数がbase64文字列として正しいかはチェックしません
+ (NSData *)dataWithBase64String:(NSString *)base64String;

// 自作のbase64エンコード
// パッディングなし
- (NSString *)F8Base64String;

// 普通のbase64エンコード
// 無改行の一般的なbase64文字列を返す
- (NSString *)base64String;

@end

.m の中身

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
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
#import "F8Base64.h"

// 64進数のchar値を6bitの数値に変換するためのテーブル
// 64進数の並びは 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
const char tableForF8Base64CharToDec[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,62, 0, 0,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
0,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,
51,52,53,54,55,56,57,58,59,60,61,0, 0, 0, 0,63,
0,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
25,26,27,28,29,30,31,32,33,34,35,0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

// 6bitバイナリを64進数のchar値に変換するためのテーブル
// 64進数の並びは 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
const char tableForDecToF8Base64Char[64] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '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', '-', '_',
};

// 64進数のchar値を6bit数値に変換するためのテーブル
const char tableForBase64CharToDec[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,62, 0, 0, 0,63,
52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0,
0, 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,0, 0, 0, 0, 0,
0,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,0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

// 6bitバイナリを64進数のchar値に変換するためのテーブル
const char tableForDecToBase64Char[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', '+', '/',
};

// 64進数char文字列を数値に変換
NSUInteger F8Base64CharsToDec (const char *cp) {
NSUInteger dec = 0;
for (NSUInteger i = 0, l = strlen(cp); i < l; i++) {
dec = (dec << 6) + tableForF8Base64CharToDec[cp[i]];
}
return dec;
}

NSUInteger base64CharsToDec (const char *cp) {
NSUInteger dec = 0;
for (NSUInteger i = 0, l = strlen(cp); i < l; i++) {
dec = (dec << 6) + tableForBase64CharToDec[cp[i]];
}
return dec;
}

// 上の関数とは変換テーブルが違うだけ
NSString* decToF8Base64String ( NSUInteger dec ) {

if ( dec == 0 ) return @"0";

// NSINteger は 32 or 64ビット で 64 は 6ビットなので char[6] あれば int 値を表現可能
// 従って 6 + 1 (文字列終端)= 7を使う
char _c[7], c[7];
NSInteger counter = 0;

while( dec ) {
_c[counter++] = dec % 64;
dec /= 64;
}

NSInteger i = 0;
while ( counter-- ) {
c[i++] = tableForDecToF8Base64Char[_c[counter]];
}
c[i] = 0; // 文字終端

return [NSString stringWithUTF8String:c];
}

// 上の関数とは変換テーブルが違うだけ
NSString* decToBase64String ( NSUInteger dec ) {

if ( dec == 0 ) return @"0";

char _c[7], c[7];
NSInteger counter = 0;

while( dec ) {
_c[counter++] = dec % 64;
dec /= 64;
}

NSInteger i = 0;
while ( counter-- ) {
c[i++] = tableForDecToBase64Char[_c[counter]];
}
c[i] = 0; // 文字終端

return [NSString stringWithUTF8String:c];
}

@implementation NSData (NSDataF8Base64)

+ (NSData *)dataWithF8Base64String:(NSString *)base64String {

const char *cp = [base64String UTF8String];

NSUInteger len = strlen(cp), remainder = len % 4;
NSMutableData *data = [NSMutableData dataWithCapacity:len / 4 * 3 + (remainder + 1) / 2];
NSUInteger i = 0, bin24, l = len / 4;
unsigned char char3[3];

while ( i < l ) {
bin24 = tableForF8Base64CharToDec[cp[i*4]] << 18
| tableForF8Base64CharToDec[cp[i*4 + 1]] << 12
| tableForF8Base64CharToDec[cp[i*4 + 2]] << 6
| tableForF8Base64CharToDec[cp[i*4 + 3]];
i++;
char3[0] = (bin24 >> 16) & 255;
char3[1] = (bin24 >> 8) & 255;
char3[2] = bin24 & 255;
[data appendBytes:char3 length:3];
}

if ( remainder == 2 ) {
bin24 = (tableForF8Base64CharToDec[cp[i*4]] << 6 | tableForF8Base64CharToDec[cp[i*4 + 1]]) >> 4;
char3[0] = bin24;
[data appendBytes:char3 length:1];

} else if ( remainder == 3 ) {

bin24 = (tableForF8Base64CharToDec[cp[i*4]] << 12 | tableForF8Base64CharToDec[cp[i*4 + 1]] << 6 | tableForF8Base64CharToDec[cp[i*4 + 2]]) >> 2;
char3[0] = bin24 >> 8;
char3[1] = bin24 & 255;
[data appendBytes:char3 length:2];

} else {
// remainder == 1 の場合その余りは不正なので無視
}

return [NSData dataWithData:data];
}

+ (NSData *)dataWithBase64String:(NSString *)base64String {

const char *cp = [base64String UTF8String];
NSUInteger padCnt = 0;
if ( cp[strlen(cp) - 1] == '=' ) {
padCnt++;
}
if ( cp[strlen(cp) - 2] == '=' ) {
padCnt++;
}

NSUInteger len = strlen(cp);
NSMutableData *data = [NSMutableData dataWithCapacity:len / 4 * 3 - padCnt];
NSUInteger i = 0, bin24, l = len / 4 - 1;
unsigned char char3[3];

while ( i < l ) {
bin24 = tableForBase64CharToDec[cp[i*4]] << 18
| tableForBase64CharToDec[cp[i*4 + 1]] << 12
| tableForBase64CharToDec[cp[i*4 + 2]] << 6
| tableForBase64CharToDec[cp[i*4 + 3]];
i++;
char3[0] = (bin24 >> 16) & 255;
char3[1] = (bin24 >> 8) & 255;
char3[2] = bin24 & 255;
[data appendBytes:char3 length:3];
}

if ( padCnt == 2 ) {
bin24 = (tableForBase64CharToDec[cp[i*4]] << 6 | tableForBase64CharToDec[cp[i*4 + 1]]) >> 4;
char3[0] = bin24;
[data appendBytes:char3 length:1];

} else if ( padCnt == 1 ) {

bin24 = (tableForBase64CharToDec[cp[i*4]] << 12 | tableForBase64CharToDec[cp[i*4 + 1]] << 6 | tableForBase64CharToDec[cp[i*4 + 2]]) >> 2;
char3[0] = bin24 >> 8;
char3[1] = bin24 & 255;
[data appendBytes:char3 length:2];

} else {
// remainder == 1 の場合その余りは不正なので無視
}

return [NSData dataWithData:data];
}

- (NSString *)F8Base64String {

const unsigned char *cp = [self bytes];
NSUInteger len = [self length], remainder = len % 3;
NSMutableData *data = [NSMutableData dataWithCapacity:len / 3 * 4 + (remainder ? 0 : remainder + 1)];

NSUInteger i = 0, bin24, l = len / 3;
while ( i < l ) {
bin24 = cp[i*3] << 16 | cp[i*3 + 1] << 8 | cp[i*3 + 2];
i++;
[data appendBytes:&tableForDecToF8Base64Char[(bin24 >> 18) & 63] length:1];
[data appendBytes:&tableForDecToF8Base64Char[(bin24 >> 12) & 63] length:1];
[data appendBytes:&tableForDecToF8Base64Char[(bin24 >> 6) & 63] length:1];
[data appendBytes:&tableForDecToF8Base64Char[bin24 & 63] length:1];
}

if ( remainder == 1 ) {
bin24 = cp[i*3] << 4;
[data appendBytes:&tableForDecToF8Base64Char[bin24 >> 6] length:1];
[data appendBytes:&tableForDecToF8Base64Char[bin24 & 63] length:1];

} else if ( remainder == 2 ) {
bin24 = (cp[i*3] << 8 | cp[i*3 + 1]) << 2;
[data appendBytes:&tableForDecToF8Base64Char[bin24 >> 12] length:1];
[data appendBytes:&tableForDecToF8Base64Char[(bin24 >> 6) & 63] length:1];
[data appendBytes:&tableForDecToF8Base64Char[bin24 & 63] length:1];
}

// 文字終端
const char nullByte = 0;
[data appendBytes:&nullByte length:1];

return [NSString stringWithUTF8String:(const char *)[data bytes]];
}

- (NSString *)base64String {

const unsigned char *cp = [self bytes];
NSUInteger len = [self length], remainder = len % 3;
NSMutableData *data = [NSMutableData dataWithCapacity:len / 3 * 4 + (remainder ? 0 : 4) + 1];

NSUInteger i = 0, bin24, l = len / 3;
while ( i < l ) {
bin24 = cp[i*3] << 16 | cp[i*3 + 1] << 8 | cp[i*3 + 2];
i++;
[data appendBytes:&tableForDecToBase64Char[(bin24 >> 18) & 63] length:1];
[data appendBytes:&tableForDecToBase64Char[(bin24 >> 12) & 63] length:1];
[data appendBytes:&tableForDecToBase64Char[(bin24 >> 6) & 63] length:1];
[data appendBytes:&tableForDecToBase64Char[bin24 & 63] length:1];
}

if ( remainder == 1 ) {
bin24 = cp[i*3] << 4;
[data appendBytes:&tableForDecToBase64Char[bin24 >> 6] length:1];
[data appendBytes:&tableForDecToBase64Char[bin24 & 63] length:1];
[data appendBytes:[@"==" UTF8String] length:2];

} else if ( remainder == 2 ) {
bin24 = (cp[i*3] << 8 | cp[i*3 + 1]) << 2;
[data appendBytes:&tableForDecToBase64Char[bin24 >> 12] length:1];
[data appendBytes:&tableForDecToBase64Char[(bin24 >> 6) & 63] length:1];
[data appendBytes:&tableForDecToBase64Char[bin24 & 63] length:1];
[data appendBytes:[@"=" UTF8String] length:1];
}

// 文字終端
const char nullByte = 0;
[data appendBytes:&nullByte length:1];

return [NSString stringWithUTF8String:(const char *)[data bytes]];
}

@end

使い方メモ : Base64エンコ/デコード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
NSString *str;
NSData *dataOfStr;

str = @"base64 普通";
dataOfStr = [str dataUsingEncoding:NSUTF8StringEncoding];
NSString *normalBase64String = [dataOfStr base64String];

NSLog(@"base64普通 : %@", normalBase64String);

NSData *normalData = [NSData dataWithBase64String:normalBase64String];
NSData *normalStr = [[NSString alloc] initWithData:normalData encoding:NSUTF8StringEncoding];

NSLog(@"デコード : %@", normalStr);

str = @"base64 自分用";
dataOfStr = [str dataUsingEncoding:NSUTF8StringEncoding];
NSString *myBase64String = [dataOfStr base64String];

NSLog(@"base64自分用 : %@", myBase64String);

NSData *myData = [NSData dataWithBase64String:myBase64String];
NSData *myStr = [[NSString alloc] initWithData:myData encoding:NSUTF8StringEncoding];

NSLog(@"デコード : %@", myStr);

結果

1
2
3
4
base64普通 : YmFzZTY0IOaZrumAmg==
デコード : base64 普通
base64自分用 : YmFzZTY0IOiHquWIhueUqA==
デコード : base64 自分用

使い方メモ : 64進数

以下のコードは Base64エンコードじゃなくて (NSInteger)10進数 と (NSString*)64進数 を相互変換するための関数を使ってます。

1
2
3
4
5
6
7
8
9
10
11
NSInteger dec = 1234567890;

NSString *base64 = decToBase64String( dec );
dec = base64CharsToDec( [base64 UTF8String] );

NSLog( @"%@ -(base64)-> %d", base64, dec );

NSString *f8base64 = decToF8Base64String( dec );
dec = F8Base64CharsToDec( [f8base64 UTF8String] );

NSLog( @"%@ -(f8base64)-> %d", f8base64, dec );

結果

1
2
BJlgLS -(base64)-> 1234567890
19Bwbi -(f8base64)-> 1234567890

関連があるかもしれない記事