C++工程实战入门笔记6-函数(三)关于base16编码的原理和函数模块化实战
首先,理解为什么要编码?
计算机内部所有数据都是用二进制(0和1) 存储的。但二进制对人类来说很难阅读和理解。比如:
文本 "Hello" 在计算机中存储为:01001000 01100101 01101100 01101100 01101111
ASCII码
使用二进制表示所有的大写和小写字母,数字0 到9、标点符号,有固定对应表。
常见ASCII码的大小规则:
数字< 大写字母 < 小写字母
1.数字比字母要小。如 “7”<“F”;
2.数字0比数字9要小,并按0到9顺序递增。如 “3”<“8” ;
3.字母A比字母Z要小,并按A到Z顺序递增。如“A”<“Z” ;
4.同个字母的大写字母比小写字母要小32。如“A”<“a” 。
几个常见字母的ASCII码大小: “A”为65;“a”为97;“0”为 48
base16编码
Unicode 码点从分配的那一刻起就是永久固定、不可改变的规则。
每个字符在Unicode标准中都有一个唯一的编号,称为"码点"。
Base16(十六进制编码)将每个字节(8位)的数据转换为两个十六进制字符(4位+4位),使用字符集 0123456789ABCDEF。
例如:
字符 'H' 的 ASCII 码是 72 (十六进制 0x48)
会被编码为 "48"
例如:
'A' → U+ 0041 → 十进制65'你' → U+ 4F60 → 十进制20320'😊' → U+ 1F60A → 十进制128522
根据码点值确定字节数
规则1:如果码点值 ≤ 127 (0x7F),使用1字节
包含所有ASCII字符:英文字母、数字、标点符号等
规则2:如果码点值 ≤ 2047 (0x7FF),使用2字节
包含拉丁字母扩展、希腊字母、西里尔字母、希伯来字母等
规则3:如果码点值 ≤ 65535 (0xFFFF),使用3字节
包含几乎所有常用汉字、日文、韩文字符基本多文种平面(BMP)中的所有字符
规则4:如果码点值 ≤ 1114111 (0x10FFFF),使用4字节
包含辅助平面字符:罕见汉字、历史文字、表情符号等
示例1:英文字母 ‘A’
Unicode码点: U+0041 (十进制65)
判断: 65 ≤ 127 → 使用1字节编码
UTF-8编码: 01000001 (0x41)
示例2:中文 ‘你’
Unicode码点: U+4F60 (十进制20320)
判断: 20320 > 2047? 是 → 20320 ≤ 65535? 是 → 使用3字节编码
UTF-8编码: 11100100 10111101 10100000 (0xE4 0xBD 0xA0)
示例3:表情符号 ‘😊’
Unicode码点: U+1F60A (十进制128522)
判断: 128522 > 65535? 是 → 128522 ≤ 1114111? 是 → 使用4字节编码
UTF-8编码: 11110000 10011111 10011000 10001010 (0xF0 0x9F 0x98 0x8A)
编码规则
“你好”
先看“你”
“你
”的码点:4F60,属于U+0800 - U+FFFF之间,需要三个字节。
4F60
转成二进制是
0100 1111 0110 0000
格式参考上图3字节,然后填入
11100100 10111101 10100000
再用十六进制表示上述码
E4 BD A0
再看“好”
“好
”的码点:597D,属于U+0800 - U+FFFF之间,需要三个字节。
597D
转成二进制是
0101 1001 0111 1101
格式参考上图3字节,然后填入
11100101 10100101 10111101
再用十六进制表示上述码
E5 A5 BD
所以综上:“你好”的base16编码是:E4BDA0E5A5BD
base16编码和解码C++实现
无函数,直接实现
int main()
{SetConsoleOutputCP(65001); // 设置控制台输出编码为UTF-8//string test_str = "测试用于base16的字符串";string test_str = u8"你好";//base16编码string base16str;//unsigned char 和char的区别//8位一个字节//unsigned 无符号,在做算术运算时不管符号//char 第一位是符号位 二进制不能有正负号for (unsigned char c : test_str){char h = c >> 4 ;//取高位:移位丢弃低位 0100 0001 >>4 之后成为 0000 0100char l = c & 0b00001111;//取低位,与00001111做与操作 之后成为 0000 0001//cout << "h:"<<h << endl;//cout << "l:" << l << endl;base16str += base16_enc_tab[h];//0-15=>0-9,A-F//cout << base16str << endl;base16str += base16_enc_tab[l];//cout << base16str << endl;}//cout << base16str << endl;////base16解码string ostr;const vector<char> base16_dec_tab{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,//0-9-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,//10-19-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,//20-29-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,//30-39-1,-1,-1,-1,-1,-1,-1,-1,//40-470,1,2,3,4,5,6,7,8,9,//48-57-1,-1,-1,-1,-1,-1,-1,//58-6410,11,12,13,14,15//65-70 A-F};for (int i = 0; i < base16str.size(); i+=2){char ch = base16str[i];char cl = base16str[i + 1];//'A'=>10,'0'=>0 0-15 //65=>A=>10 48=>0 70=>Funsigned char h = base16_dec_tab[ch];unsigned char l = base16_dec_tab[cl];// h:0000 0100 l:0000 0001 ==> 0100 0001ostr += (h << 4 ) | l;}cout << ostr << endl;system("pause");
}*/
函数模块化实现
base16.h
#pragma once
#include<string>
#include<vector>///////////////////////////////////
//base16编码
//@para data:需要编码的二进制数据
//@return 返回base16编码的字符串
std::string Base16Encode(const std::vector<unsigned char> data);///////////////////////////////////
//base16解码
//@para str:需要解码的base16字符串,必须是2的倍数
//@return 返回原始的二进制数据
std::vector<unsigned char> Base16Decode(const std::string &str);
base16.cpp
#include "stdafx.h"
#include"base16.h"
#include<iostream>
using namespace std;//静态全局变量 作用域本cpp文件
static const string enc_tab = "0123456789ABCDEF";
static const vector<char> dec_tab{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,//0-9-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,//10-19-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,//20-29-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,//30-39-1,-1,-1,-1,-1,-1,-1,-1,//40-470,1,2,3,4,5,6,7,8,9,//48-57-1,-1,-1,-1,-1,-1,-1,//58-6410,11,12,13,14,15//65-70 A-F
};///////////////////////////////////
//base16编码
//@para data:需要编码的二进制数据
//@return 返回base16编码的字符串
std::string Base16Encode(const std::vector<unsigned char> data)
{string res;for (unsigned char c : data){char h = c >> 4;//取高位:移位丢弃低位 0100 0001 >>4 之后成为 0000 0100char l = c & 0b00001111;//取低位,与00001111做与操作 之后成为 0000 0001res += enc_tab[h];//0-15=>0-9,A-Fres += enc_tab[l];}return res;
}///////////////////////////////////
//base16解码
//@para str:需要解码的base16字符串,必须是2的倍数
//@return 返回原始的二进制数据
std::vector<unsigned char> Base16Decode(const std::string &str)
{if (str.size() % 2 != 0){cerr << "base16 string is error" << endl;return{};}std::vector<unsigned char> res;for (int i = 0; i < str.size(); i += 2){char ch = str[i];char cl = str[i + 1];//'A'=>10,'0'=>0 0-15 //65=>A=>10 48=>0 70=>Funsigned char h = dec_tab[ch];unsigned char l = dec_tab[cl];// h:0000 0100 l:0000 0001 ==> 0100 0001res.push_back( (h << 4) | l);}return res;
}
主函数
#include "stdafx.h"
#include<iostream>
#include<bitset>
#include<string>
#include<vector>
#include "base16.h"
#include <windows.h>
using namespace std;
//编码用的映射表
//const string base16_enc_tab = "0123456789ABCDEF";
int main()
{SetConsoleOutputCP(65001);string test_str = u8"这个世界会好吗?";cout << u8"原文:" << test_str << endl;cout << endl;vector<unsigned char> data(test_str.begin(), test_str.end());data.push_back('\0');auto base16_str = Base16Encode(data);cout << u8"编码后base16:" << base16_str << endl;cout << endl;auto dec_str = Base16Decode(base16_str);cout << u8"base16解码后:" << dec_str.data() << endl;cout << endl;system("pause");
}