目錄
數據碼編碼
在閱讀這篇文章前,你需要具備一定的編程能力。
數據碼編碼,就是將二維碼存儲的字符轉化成二進制。 這些字符可以是數字、字母、中文。 那麼數據碼編碼時,就根據數字模式,數字字母模式,8位字節模式,中文模式進行編碼。 8位字節模式可以描述整個計算機世界的字符,而其他模式是量身打造的,所以所需字節比8位字節模式要少。
該教程目前只實現了數字模式和8位字節模式的編碼規範。
流程圖
教程
模式指示符
模式 | 指示符 |
ECI | 0111 |
數字 | 0001 |
字母數字 | 0010 |
8位字節(Byte) | 0100 |
中國漢字 | 1101 |
結構鏈接 | 0011 |
FNC1(第一位置) | 0101 |
FNC1(第二位置) | 1001 |
模式指示符在數據碼字中佔4位,是每個數據碼字的第一部分。
字符計數指示符
版本 | 數字模式 | 字母數字模式 | 8位字節模式 | 中國漢字模式 |
1-9 | 10 | 9 | 8 | 8 |
10-26 | 12 | 11 | 16 | 10 |
27-40 | 14 | 13 | 16 | 12 |
字符計數指示符的位數在數據碼字中根據版本和編碼模式而有所不同,它計算的是模式編碼時,源字符的長度。例如:對"qrcode"進行編碼,那麼所獲得的源字符長度是6,如果編碼模式選擇的是字母數字模式,版本選擇的是1,那麼對應的該字符計數指示符的位數是9,而6在二進制中是"110",那麼該字符計數指示符是"000000110"。
模式編碼
該方法進行的是根據模式來分發模式編碼
查看源碼數字模式編碼
例1:將"01234567"進行編碼,首先要對這些數字進行分割。分割成 012 345 67的形式,即將源字符(純數字),如果字符長度滿足3的倍數,那麼正好可以分割成3n的序列形式。但是如果不滿足3的倍數,那麼就可以用3n+1和3n+2的序列形式來表示。所以示例中是一個標準的3n+2序列形式。
在二進制中想要表示這些數字,3位的十進制數字,最大爲999,二進制中2的10次方[1024]可以涵蓋這些數字,2位的十進制數字,最大爲99,二進制中2的7次方[128]可以涵蓋這些數字,1位的十進制數字,最大爲9,二進制中2的4次方[16]可以涵蓋這些數字。
例1:將"01234567"進行編碼:
012->0000001100
345->0101011001
67->1000011
例2:將"0125"進行編碼:
012->0000001100
5->0101
字母數字模式編碼
8位字節編碼
8位字節編碼即依據ASCII編碼表進行編碼。如果你想用8位字節表示中文,那麼可以根據GB2312或UTF-8來對中文字符進行處理,具體的GB2312處理方式:
#include<string>
#include<iostream>
int main(){
std::string a = "你";
std::cout << std::hex << (short)a[0] << std::endl;
std::cout << std::hex << (short)a[1] << std::endl;
char p[5] = { 0xffc4, 0xffe3 };
std::cout << p;
std::cin >> a;
}
以此,可以對照GB2312編碼表對中文向8位字節流轉換。
中國漢字編碼
結束指示符
我們有義務標識數據碼已經結束,並且應該根據二維碼定義的數據碼長度巧妙得告訴數據碼結束了。
結束指示符:"0000",如果剩餘位不足4位,填充滿0。
補充0
我們有義務將數據碼補充成完整的8位字節。
根據數據碼[模式指示符+字符計數指示符+模式編碼+結束指示符]的長度補充0,直至該長度滿足8的倍數。(1byte = 8bit)
補充字節
我們有義務將數據碼補充成與版本模式一致的長度。
根據數據碼[模式指示符+字符計數指示符+模式編碼+結束指示符+補充0]的長度輪流補充"11101100","00010001",以此讓數據碼字符合二維碼版本、編碼模式、糾錯等級規定的數據碼字長度。
源碼
模式指示符
教程/*
* @method mode_indicator
* @params void
* @return void
* @throw()
* @methodState: stable
* This function get the mode indicator to append it to <code>result<code>.
*/
void qrcode::data_encode::DataEncode::mode_indicator(){
this->member_result = "";
int mode = this->member_basic_information->getMode();
switch (mode){
case qrcode::settings::mode::sign::NUMBER_MODE:
this->member_result += "0001";
break;
case qrcode::settings::mode::sign::BYTE_MODE:
this->member_result += "0100";
break;
case qrcode::settings::mode::sign::CHINESE_MODE:
this->member_result += "1101";
break;
case qrcode::settings::mode::sign::LETTER_MODE:
this->member_result += "0010";
break;
}
}
字符計數指示符
教程/*
* @method character_count_indicator
* @params int count
* @return void
* @throw()
* @methodState: stable
* This function get the character_count_indicator to append it to <code>result<code>
*/
void qrcode::data_encode::DataEncode::character_count_indicator(int length){
int code_length = qrcode::data_encode::settings::character_count_indicator_length(this->member_basic_information);
std::string binary = qrcode::tools::decimal_to_binary(length);
int binary_length = binary.length();
if (code_length > binary.length()){
for (int i = 0; i < code_length - binary_length; i++){
binary.insert(binary.begin(), '0');
}
}
this->member_result += binary;
}
模式編碼
教程/*
* @method mode_encode
* @params std::string code
* @return void
* @throw()
* @methodState: extendable
* This function is used to dispatch ***_mode_encode,and now just support for
* 4 modes.There are many languages on the earth.And I cannot know them from A to Z.
* if you want encode the language of your motherland
* which is not Chinese(GB2312) or English(ASCII) in this demo,
* please know the rules for encoding and do it yourself.
*/
void qrcode::data_encode::DataEncode::mode_encode(std::string code){
std::string encode;
int mode = this->member_basic_information->getMode();
switch (mode){
case qrcode::settings::mode::sign::NUMBER_MODE:
encode = number_mode_encode(code);
break;
case qrcode::settings::mode::sign::CHINESE_MODE:
encode = chinese_mode_encode(code);
break;
case qrcode::settings::mode::sign::BYTE_MODE:
encode = byte_mode_encode(code);
break;
case qrcode::settings::mode::sign::LETTER_MODE:
encode = letter_mode_encode(code);
break;
}
member_result += encode;
}
-
數字模式編碼
教程
/*
* @method number_mode_encode
* @params std::string code
* @return std::string
* @throw
* @methodState: stable
* This function is used to encode the code from number mode.
*/
std::string qrcode::data_encode::DataEncode::number_mode_encode(std::string code){
//original source length
int length = code.length();
//result
std::string result = "";
//operated_data
std::string operated_data = "";
for (int i = 0; i < length; i = i + 3){
operated_data = code.substr(i, 3);
if (operated_data.length() < 3){
break;
}
char * temp = NULL;
temp = new char[11];
int demical = std::stoi(operated_data);
std::string binary = tools::decimal_to_binary(demical);
sprintf_s(temp,11,"%010s",binary.data());
result += temp;
delete temp;
temp = NULL;
operated_data = "";
}
//handle the last of the code
if (operated_data.length() >= 0){
char * temp = NULL;
if (operated_data.length() == 1){
temp = new char[5];
int demical = std::stoi(operated_data);
std::string binary = tools::decimal_to_binary(demical);
sprintf_s(temp, 5, "%04s", binary.data());
}
else if (operated_data.length() == 2){
temp = new char[8];
int demical = std::stoi(operated_data);
std::string binary = tools::decimal_to_binary(demical);
sprintf_s(temp, 8, "%07s", binary.data());
}
result += temp;
delete temp;
temp = nullptr;
}
return result;
}
-
8位字節編碼
教程
/*
* @method byte_mode_encode
* @params std::string code
* @return std::string
* @throw
* @methodState: stable
* This function is used to encode the code from byte mode.
*/
std::string qrcode::data_encode::DataEncode::byte_mode_encode(std::string code){
std::string result;
for (auto item : code){
char * temp=nullptr;
if (item < 0x7f){
temp = new char[9];
std::string operated_data = qrcode::tools::decimal_to_binary(item);
sprintf_s(temp, 9, "%08s", operated_data.data());
result += temp;
}
else{
temp = new char[17];
std::string operated_data = qrcode::tools::decimal_to_binary(item);
sprintf_s(temp, 17, "%016s", operated_data.data());
result += temp;
}
delete temp;
temp = nullptr;
}
return result;
}
}
結束指示符
教程/*
* @method qrcode::data_encode::DataEncode::end_indicator
* @params void
* @return void
* @throw()
* @methodState: stable
* This function get the end_indicator to append it to<code>result</code>
*/
void qrcode::data_encode::DataEncode::end_indicator(){
int code_length = qrcode::data_encode::settings::code_length(this->member_basic_information);
// byte_length to bit_length,
int bit_length = code_length << 3;
int remain_bit = bit_length - member_result.length();
switch (remain_bit){
case 1:
member_result += "0";
break;
case 2:
member_result += "00";
break;
case 3:
member_result += "000";
break;
default:
member_result += "0000";
break;
}
}
補充0
教程/*
* @method qrcode::data_encode::DataEncode::make_up_to_a_multiple_of_8
* @params void
* @return void
* @throw()
* @methodState: stable
* This function append zeros to result to make up as a multiple of 8.
*/
void qrcode::data_encode::DataEncode::make_up_to_a_multiple_of_8(){
int remain_bit = member_result.length() & 7;
if (remain_bit){
for (int i = 0; i < 8 - remain_bit; i++){
member_result.push_back('0');
}
}
}
補充字節
教程/*
* @method qrcode::data_encode::DataEncode::pad_bytes
* @params void
* @return void
* @throw()
* @methodState: stable
* This function append a string of 8 bits to make up <code>result</code>
* as the final result.
*/
void qrcode::data_encode::DataEncode::pad_bytes(){
int byte_length = member_result.length() >> 3;
int code_length = qrcode::data_encode::settings::code_length(this->member_basic_information);
int remain_byte = code_length - byte_length;
bool turn = true;
while (remain_byte > 0){
if (turn){
member_result.append("11101100");
turn = false;
}
else{
member_result.append("00010001");
turn = true;
}
remain_byte--;
}
}