Принцип кодирования Base64 и реализация кода
Оригинальный адрес (на самом деле автор сам):www.sanphantom.com/?p=81
Так называемая кодировка base64 заключается в выборе 64 печатных символов (A-Za-z0-9+/) из кодовой таблицы ASCII в качестве основного набора символов для кодирования и преобразования других символов. Плюс "=" в качестве заполнения на самом деле составляет 65 символов.
Причина для Base64
Если вы хотите понять base64, вы должны сначала понять кодовую таблицу ASCII.Таблица кодов ASCII сформулирована Соединенными Штатами, которые являются носителями английского языка. Английский достаточно для кодирования 128 символов, но для представления других языков 128 символов недостаточно. Например, во французском языке над буквой стоит фонетический символ, который не может быть представлен в ASCII. В результате некоторые европейские страны решили запрограммировать новые символы со старшими битами, которые не используются в байтах. Например, по-французскиé
кодируется как 130 (двоичный10000010
). Таким образом, система кодирования, используемая этими европейскими странами, может представлять до 256 символов.
Однако здесь возникает новая проблема. В разных странах используются разные буквы, поэтому, несмотря на то, что все они используют 256-символьный метод кодирования, они представляют разные буквы. Например, 130 во французской кодировке означаетé
, которые в еврейской кодировке представляют собой буквыGimel
(ג
), что в русской кодировке представляло бы другой символ. Но в любом случае, во всех этих методах кодирования символы, представленные 0-127, одинаковы, и единственное отличие состоит в этом разделе 128--255.
Что касается иероглифов азиатских стран, то здесь используется больше символов, а китайских иероглифов насчитывается целых 100 000. Один байт может представлять только 256 видов символов, что определенно недостаточно, и для выражения одного символа необходимо использовать несколько байтов. Например, общепринятым методом кодирования для упрощенного китайского языка является GB2312, который использует два байта для представления китайского символа, поэтому теоретически он может представлять до 256 * 256 = 65536 символов.
При обмене данными в сети, например, из пункта А в пункт Б, они часто проходят через несколько маршрутизирующих устройств, поскольку разные устройства по-разному обрабатывают символы, эти невидимые символы могут обрабатываться некорректно, что не способствует передаче. Поэтому данные сначала кодируются в Base64, и все они становятся видимыми символами, так что возможность ошибок сильно снижается.
Почему Base64?
Почему base64 вместо base128, base256? На самом деле причина очень проста, потому что в кодовой таблице ASCII всего 95 печатных символов, поэтому наиболее разумно выбрать 64 печатных символа. В этом случае есть ли еще base32 и base16? Да конечно может быть. Просто большинство из них все еще используют кодировку base64.
Теоретическая реализация кодировки Base64
Таблицу индексов можно получить в соответствии с набором символов A-Za-z0-9+/:
показатель | соответствующий символ | показатель | соответствующий символ | показатель | соответствующий символ | показатель | соответствующий символ |
---|---|---|---|---|---|---|---|
0 | A | 17 | R | 34 | i | 51 | z |
1 | B | 18 | S | 35 | j | 52 | 0 |
2 | C | 19 | T | 36 | k | 53 | 1 |
3 | D | 20 | U | 37 | l | 54 | 2 |
4 | E | 21 | V | 38 | m | 55 | 3 |
5 | F | 22 | W | 39 | n | 56 | 4 |
6 | G | 23 | X | 40 | o | 57 | 5 |
7 | H | 24 | Y | 41 | p | 58 | 6 |
8 | I | 25 | Z | 42 | q | 59 | 7 |
9 | J | 26 | a | 43 | r | 60 | 8 |
10 | K | 27 | b | 44 | s | 61 | 9 |
11 | L | 28 | c | 45 | t | 62 | + |
12 | M | 29 | d | 46 | u | 63 | / |
13 | N | 30 | e | 47 | v | ||
14 | O | 31 | f | 48 | w | ||
15 | P | 32 | g | 49 | x | ||
16 | Q | 33 | h | 50 | y |
Конкретные этапы преобразования кодировки в Base64 следующие:
- Разделите строку, которую нужно преобразовать, на группы по 3 байта, 1 байт = 8 бит, и каждая группа содержит ровно 24 двоичных бита.
- Разделите вышеуказанные 24 двоичных бита на группы по 6, чтобы сформировать группы по 4.
- Добавьте два 0 перед каждой группой, чтобы сформировать 8-битную группу, то есть 4 байта.
- Получите соответствующее значение в соответствии с приведенной выше таблицей сравнения Base64 для формирования кодировки Base64.
**Например:**В следующей таблице показан процесс кодирования в Base64 строки «Man» как исходной строки.
текст | M | a | n | |
---|---|---|---|---|
ASCII | 77 | 97 | 110 | |
бинарный | 01001101 | 01100001 | 01101110 | |
группировка | 00 010011 | 00 010110 | 00 000101 | 00 101110 |
показатель | 19 | 22 | 5 | 46 |
Кодировка Base64 | T | W | F | u |
Тогда вы можете спросить, а что, если исходная строка меньше 3 байт?
Если длина входной исходной строки не делится на 3, нам нужно дополнить ее кодировку Base64 символом "=". Зачем вам нужно дополнение "="? Поскольку декодирование Base64 делится на 4-битные символы, если вы не дополните его, это приведет к сбою декодирования.
Когда двоичные биты исходной строки не кратны 6, мы все равно делим ее на 6-битные группы, а затем дополняем последнюю группу битами от 0 до 6 (заполнение в конце).
**Например:** Ниже приведен процесс кодирования строки «AB», результат кодирования — «QUI=».
текст | A | B | ||
---|---|---|---|---|
ASCII | 65 | 66 | ||
бинарный | 01000001 | 01000010 | ||
группировка | 00 010000 | 00 010100 | 00 001000 | |
показатель | 16 | 20 | 8 | |
Кодировка Base64 | Q | U | I | = |
Уведомление: существует множество кодировок китайских иероглифов, таких как UTF-8, GBK, GB2312 и т. д. Различные кодировки влияют на кодировку Base64.
исходный код
base64.h
/*base64.h*/
#ifndef _BASE64_H
#define _BASE64_H
#include <stdlib.h>
#include <string.h>
#include <math.h>
inline unsigned int BASE64_ENCODE_SIZE(unsigned int len)
{ // 计算字符串加密后的长度(不包括填充字符 '=')
return ceil(len * 8 / 6);
}
unsigned char *base64encode(const unsigned char *str, unsigned int len);
unsigned char *base64decode(const unsigned char *str, unsigned int len);
#endif
base64.c
#include <stdio.h>
#include "base64.h"
#define CHARPAD '='
extern inline unsigned int BASE64_ENCODE_SIZE(unsigned int);
/* Base64 编码表 */
static const unsigned char base64_table_encode[] = {
'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', '+', '/',
};
unsigned char *base64encode(const unsigned char *str, unsigned int len)
{
int i = 0, j = 0, k = 0;
unsigned int encodeSize = BASE64_ENCODE_SIZE(len);
unsigned char *result = (unsigned char *) malloc(sizeof(unsigned char) * encodeSize + 4);
result[encodeSize] = '\0'; /* 构造字符串 */
for(i = 0, j = 0; i < encodeSize; i += 4, j += 3)
{
result[i] = base64_table_encode[(str[j] >> 2) & 0x3f];
if (i + 1 >= encodeSize) break;
result[i+1] = base64_table_encode[((str[j] & 0x3) << 4) | ((str[j+1] >> 4) & 0xf)];
if (i + 2 >= encodeSize) break;
result[i+2] = base64_table_encode[((str[j+1] & 0xf) << 2) | ((str[j+2] >> 6) & 0x3)];
if (i + 3 >= encodeSize) break;
result[i+3] = base64_table_encode[(str[j+2] & 0x3f)];
}
return result;
}