题目描述
Base64 是网络上最常见的用于传输 8Bit 字节代码的编码方式之一。Base64 要求把每三 个 8Bit 的字节转换为四个 6Bit 的字节,然后把 6Bit 再添两位高位 0,组成四个 8Bit 的字 节,该 8Bit 的字节转化为十进制后一定表示一个 0-63 的整数。将这个整数对应转换表中的 字符便是编码后的结果。为简单起见,我们不考虑实际编码是的换行符,并且原码的长度一 定是 3 的倍数,因此编码后长度一定是 4 的倍数。转换后的字符串理论上将要比原来的长 1/3。转换表见百度。
下面举一个编码的例子。 原码为 abc。’a’,’b’,’c’的 ASCII 码分别为 97,98,99,对应二进制为 01100001
,01100010
,01100011
,连起来是 011000010110001001100011
,每六个分为一组, 每组前面补两个 0,便是 00011000
,00010110
,00001001
,00100011
,分别是 24
,22
,9
,35
,所以编码后是 YWJj
。 现在给出编码后的结果,请你求出原码。
输入格式
输入文件只有一行,为一个字符串表示编码后的结果。数据保证字符串的长度为 4 的倍数且合法,且原码中均为常用字符,且不含空格和回车。
输出格式
输出文件只有一行,表示原码
【样例输入】 SGVsbG8h
【样例输出】 Hello!
【数据规模】
设 L 为编码的总长度。
对于 30%的数据,1≤L≤200
对于 100%的数据,1≤L≤10000
L 为 4 的倍数。
【输入文件】 base.in
【输出文件】 base.out
【时间限制】 1 秒
【空间限制】 128 MB
分析:
一个字符串,比如“abc
”
首先操作字符a
:
字符a
,b
,c
对应的ASCII码为97
,98
,99
,将97,98,99转换成八位
二进制:01100001 01100010 01100011.
将这三个二进制组合起来,得到011000010110001001100011
接下来,每6个二进制位为一组,每一组前面补俩0,这样又得到了八位的二进制。
分组后: 00011000, 00010110, 00001001, 00100011
转成十进制: 24, 22, 9, 35
这四个十进制数就是索引了。通过索引表,我们可以得到转换后的base64编码:YWJj
。
知道了编码方法,解码方法就知道了。
假定base64编码为YWJj
。
首先,我们读入base64编码的第一个字符。比如:Y
。
由索引表:24 对应 Y,反推出Y对应的索引为24。
然后,我们把24转换成一个六位
二进制数011000。(这个二进制数必定是6位以内的二进制数,因为索引最多到63,也就是111111
)
然后,我们操作base64编码的第二个字符。比如:W
。
由索引表:22对应 W,反推出W对应的索引为22。
然后,我们把22转换成一个六位
二进制数010110
。
然后,我们操作base64编码的第三个字符。比如:J
。
由索引表:9对应 J,反推出J对应的索引为9。
然后,我们把9转换成一个六位
二进制数010001
。
然后,我们操作base64编码的第四个字符。比如:j
。
由索引表:35对应 J,反推出J对应的索引为35。
然后,我们把35转换成一个六位
二进制数100011
。
我们把所有的六位二进制数拼到一起,得到011000010110010001100011
。
然后以八个二进制位为一组,将这一串二进制分割,得到01100001
,01100100
,01100011
。
将它们转换成十进制,得到97
,98
,99
再根据ASCII码,推出对应的字符。
Done!
// Code::Blocks 编译通过
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
char base64[20000];//字符串数组
int bin[90000];//把base64编码转成二进制后的二进制数组
char decode[20000];//解码后的
static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";//索引
int ReadFile()
{
freopen("base.in","r",stdin);
freopen("base.out","w",stdout);
int i=0;
while(scanf("%c", &base64[i]) == 1)
{
if((int)base64[i] == '=')//读到等号就结束
{base64[i] = 0;return 0;}
i++;
}
}
void Dec2Bin(int num, int *a)//十进制转二进制。结果存放在传入的数组里
{//要转换的数字,接收最后的值的数组(数组从0到7,一共六位【6个二进制数】)
int i=0;
for(int j=0; j<6; j++)
a[j] = 0;
while(num)
{
a[i++] = (num%2)?1:0;
num >>= 1;
}
}
long Bin2Dec(char *s) /*将以数组形式存储的二进制数字转换为对应的十进制数字*/
{
long rt=0;
int n=0;
while (s[n])
{
n++;
}
for (int i=n--; i>=0; i--)
{
rt |= (s[i]-48)<<n-i;
}
return rt;
}
int Digits2Index(char a)//将一位base64编码转为索引表中的索引
{
if(a>='A' && a<='Z')
return a-'A';
if(a>='a' && a<='z')
return a-'a' + 26;
if(a>='0' && a<='9')
return a-'0' + 52;
if(a=='+')
return 62;
if(a=='/')
return 63;
return -1;
}
int Proc(char s[], int s_len)
{
int temp[6];//用来储存返回的二进制数据的整数数组temp[6]
int t;//记录bin数组长度
for(int i=0; i<s_len; i++)//把base64编码转换成一长串二进制
{
Dec2Bin(Digits2Index(s[i]),temp);//把一个base64编码读进来,再转成6位二进制,将6位二进制存到temp数组里。
for(int j=5; j>=0; j--)//反着循环
{
bin[6*i+(5-j)] = temp[j];//每6个为一组,外层i循环一次,就代表着添加进了6个二进制位,这样的话,用6*i表示当前应该插入到哪里。
t = 6*i+(5-j);//记录bin数组长度
}
}
char aa[8];
int tpp,tbin;//临时变量
t = (t+1)/8;//能把二进制串切成几字节。(1个字节由8个二进制位组成)
for(int i=0; i<t; i++)
{
for(int j=0; j<8; j++)
{
aa[j] = (char)(bin[8*i+j]+48);
}
aa[8] = '\0';//末尾补个0,不然会出错。因为二转十,最后一位靠'\0'判断
tbin = Bin2Dec(aa);
tpp = Digits2Index(tbin);
if(tpp == -1)//如果这个字符不在索引表里,那么就直接输出这个字符,否则输出索引表里的字符
cout << (char)tbin;
else
cout << base64digits[Digits2Index(Bin2Dec(aa))];
}
}
int main()
{
ReadFile();
Proc(base64, strlen(base64));
return 0;
}