Base64 用来将数据编码为可读的字符串。
编码完的结果只包含字母,数字,+,/ ,=
编码完的结果长度是原始值的4/3.
编码原理:
1. 3*8==4*6
将3字节数据映射到四字节
编码实现:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Algorithm
{
public class Base64Algorithm
{
const string BaseString = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static Dictionary<int, char> map=new Dictionary<int,char>();
static Dictionary<char, int> mapRevert = new Dictionary<char, int>();
static Base64Algorithm()
{
for (int i = 0; i < BaseString.Length; i++)
{
map[i] = BaseString[i];
mapRevert[BaseString[i]] = i;
}
}
public string ToBase64String(byte[] src)
{
int targetLen = src.Length;
byte[] buff=null;
int reminderCount = src.Length % 3;
if (reminderCount > 0)
{
targetLen = src.Length + (3 - reminderCount);
}
else
{
targetLen = src.Length;
}
buff = new byte[targetLen];
Array.Copy(src, 0, buff, 0,src.Length);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < targetLen / 3; i++)
{
char c0 = map[(int)(buff[i*3] >> 2) & 0x3f];
char c1 = map[(int)((buff[i*3] & 0x03) << 4 | buff[i*3+1] >> 4) & 0x3f];
char c2 = map[(int)((buff[i*3+1] & 0x0f) << 2 | buff[i*3+2] >> 6) & 0x3f];
char c3 = map[(int)buff[i*3+2] & 0x3f];
sb.Append(c0);
sb.Append(c1);
sb.Append(c2);
sb.Append(c3);
}
if (reminderCount == 2)
{
sb.Remove(sb.Length - 1, 1);
sb.Append('=');
}
else if (reminderCount == 1)
{
sb.Remove(sb.Length - 2, 2);
sb.Append("==");
}
return sb.ToString();
}
public string FromBase64String(string src)
{
throw new NotImplementedException();
}
}
}
测试:
/// <summary>
///A test for ToBase64String
///</summary>
[TestMethod()]
public void ToBase64StringTest()
{
Base64Algorithm target = new Base64Algorithm(); // TODO: Initialize to an appropriate value
byte[] src = Encoding.Default.GetBytes("A");
string expected;
string actual;
src = Encoding.ASCII.GetBytes("ABC");
expected = "QUJD";
actual = target.ToBase64String(src);
Assert.AreEqual(expected, actual);
actual = target.ToBase64String(src);
Assert.AreEqual(expected, actual);
src = Encoding.ASCII.GetBytes("AB");
expected = "QUI=";
actual = target.ToBase64String(src);
Assert.AreEqual(expected, actual);
Random rand=new Random();
for (int i = 0; i < 99; i++)
{
src = new byte[64+i];
rand.NextBytes(src);
expected = Convert.ToBase64String(src);
actual = target.ToBase64String(src);
Assert.AreEqual(expected, actual);
}
}
解码实现:
public byte[] FromBase64String(string src)
{
int padLen = 0;
byte[] result = new byte[src.Length * 3 / 4];
if (src.Length < 4)
{
throw new ApplicationException("length is too short.");
}
if (src.Length % 4 != 0)
{
throw new ApplicationException("length is not valid.");
}
for (int i = 0; i < src.Length / 4; i++)
{
byte bit0 = Convert.ToByte(mapRevert[src[i * 4]]);
byte bit1 = Convert.ToByte(mapRevert[src[i * 4 + 1]]);
byte bit2 = Convert.ToByte(mapRevert[src[i * 4 + 2]]);
byte bit3 = Convert.ToByte(mapRevert[src[i * 4 + 3]]);
result[i * 3] = Convert.ToByte((bit0 & 0x3f) << 2 | (bit1 & 0x3f) >> 4);
result[i * 3 + 1] = Convert.ToByte((bit1 & 0x0f) << 4 | (bit2 & 0x3f) >> 2);
result[i * 3 + 2] = Convert.ToByte((bit2 & 0x03) << 6 | bit3 & 0x3f);
}
if (src[src.Length - 1] == '=' &&
src[src.Length - 2] != '=')
{
padLen = 1;
}
else if (src[src.Length - 2] == '=' &&
src[src.Length - 1] == '=')
{
padLen = 2;
}
byte[] ret = result.Take(result.Length - padLen).ToArray();
return ret;
}
解码测试:
/// <summary>
///A test for FromBase64String
///</summary>
[TestMethod()]
public void FromBase64StringTest()
{
Base64Algorithm target = new Base64Algorithm(); // TODO: Initialize to an appropriate value
byte[] actual;
actual = target.FromBase64String("QUJD");
Assert.AreEqual(Encoding.ASCII.GetString(actual), "ABC");
actual = target.FromBase64String("QUI=");
Assert.AreEqual(Encoding.ASCII.GetString(actual), "AB");
actual = target.FromBase64String("QQ==");
Assert.AreEqual(Encoding.ASCII.GetString(actual), "A");
Random rand = new Random();
for (int i = 0; i < 1003; i++)
{
byte[] src = null;
src = new byte[64 + i];
rand.NextBytes(src);
//src = Encoding.ASCII.GetBytes("ABCDEFG");
string srcString = Convert.ToBase64String(src);
byte[] expectedBytes = Convert.FromBase64String(srcString);
actual = target.FromBase64String(srcString);
Assert.AreEqual(expectedBytes.Length, actual.Length);
for (int j = 0; j < expectedBytes.Length; j++)
{
Assert.AreEqual<byte>(expectedBytes[j], actual[j]);
}
}
}