串口字符串-HEX格式
介紹
串口通信過程中 通常涉及一個數據的模擬過程以及數據發送過程, 一般來說, 我們會發送一串指令給下位機
68 05 00 84 01 02 03
例如這種, 我們明白 這是我們 將相應的字符轉換成 hex 字符顯示,用於表示ascii 字母的使用, 但是在程序中 我們可以直接使用 字符串表示我們想寫入的字符, 容納后轉換之后 串口數據相應的ASCII碼的過程,
下面 介紹一下常用的轉換函數
數據格式
在我們的描述中,
首先給出一個 或者參考 Wikipedia ACII 的詳細介紹
此處我們均使用 "abc123" 作為字符串在各個里面的顯示來表示
- 字符串 string 指的是 "abc 123" 這種能用ascii 表示的 能夠打出來的字符, 由於我們部分字符不能打出來,或者說是 不可顯示字符,只用於控制指令, 存在缺陷 string str = "abc123"
- 字符串數組 char [] uchar[] 指 cpp 中 的一種數據, 存放的char 型或者 uchar 的字符, 我們可以使用 -128-127 或者 0-255的數字表示, 我們暫時均認為是 uchar 的數組 便於后續處理 uchar buffer= {97,98,99,49,50,51}
- 字符串指針 char* uchar * 是類型指針,一般指向的是字符串數據的首地址,由於我們處理過程中 很多函數 認為 0x00 '\0' 是結束,所以處理的時候盡量傳入 長度 uchar *buffer = buffer
- hex 字符串 將字符串數組里面的值轉換成16進制的兩位值,然后使用空格分割, 便於輸入指令, 例如 string command="61 62 63 31 32 33"
這種合適各有優劣, 比如我們在常用的 Modbus 控制中 使用 這種控制命令發送到下位機, 一般直接在串口輸入, 然后使用 hex 發送即可, 但是如果我們測試過程中 需要將相應的數據按照兩位的段, 填入txt 文本中, 每次讀取一行數據, 然后將數據轉換成 ascii 便能模擬 二進制流的輸入輸出
68 13 00 85 11 12 21 22 31 32 00 00 01 00 00 02 00 00 03 88 FF 01 02 03 04 05 0F
數據格式轉換
1. uchar 數據和 char 字節數據的相互轉換
char 類型的數據 -128-127, uchar 類型數據 0-255, 均是一個字節, char 類型的首位數據為符號位, 所以會存在從 127(0111 1111) +1 之后變成 -128(1000 0000) 的變換, 然后也會存在從-1(1111 1111) 到 0(0000 0000) 的變換, 詳細可以搜索 char 溢出
與 數據補碼
的相關內容,
所以 uchar -char 的相互變換可以考慮成 兩個部分
uchar 的 [0, 127] == char [0,127]
ucahr 的 [128, 255] == char [-128,-1]
// uchar 數據轉換 char >127 c1-256
char Utils_String::UChar2Char(uchar c1)
{
return static_cast<char>(c1 > 127 ? c1 - 256 : c1);
}
// char 類型轉換 uchar <0 -- c1 +256
uchar Utils_String::Char2UChar(char c1)
{
return static_cast<uchar>((c1 < 0 ? 256 + c1 : c1));
}
2. hex 與數字的相互轉換
一般的 hex 表示的 16進制字符, 由於為了便於我們進行顯示, 16進制由 0-9 a-f 供給16個字符依次表示 0-15 , 此處暫時不考慮 大寫字母, 大寫的A-F 等同於小寫的a-f,
由於 uchar 類型的數據在 0-256之間, 正好可以表示成 2個16進制字符來進行表示, 使用前綴字符0x 表示16進制, 0o 表示8進制 0d 表示10進制 [0,255] ==[0x00,0xff]
我們建立一位字符0-f 與 0-15 之間的轉換, 有兩種轉換方式, 一種就是if 判斷進行強制轉換, 另外一種 是使用 碼表的排列進行一定 的優化處理
1位hex 與num 的相互轉換代碼
/** * @fn int Utils_String::Hex2Num(const char ch) * * @brief 根據 各種情況 轉換字母 一位字母 大於 48 的 +9 然后取后4位的值 * * * * @author IRIS_Chen * @date 2019/12/16 * * @param ch The ch * * @return An int */
int Utils_String::Hex2Num(const char ch)
{
//int res = (ch & '@' ? ch + 9 : ch) & 0x0F;
//LInfo("ch:{},res:{}", ch, res);
return (ch & '@' ? ch + 9 : ch) & 0x0F;
}
/** * @fn char Utils_String::Num2Hex(int num, bool Up ) * * @brief 將 0-15 轉換成 0-F * * @author IRIS_Chen * @date 2019/12/18 * * @param num Number of * @param Up True to up * * @return The total number of 2 hexadecimal */
char Utils_String::Num2Hex(int num, bool Up /* = true */)
{
char res;
if (num >= 10 && num <= 15)
{
res = num - 10 + (Up ? 'A' : 'a');
}
else
res = num + '0';
return res;
}
1位hex 與num 的相互轉換代碼
2位hex 與 uchar相互轉換
/** * @fn uchar Utils_String::Hex2Uchar(const std::string & str) * * @brief Hexadecimal 2 uchar 將兩個 hex 字符 轉換成 0-256 * * @author IRIS_Chen * @date 2019/12/18 * * @param str The string 默認初始兩位字符 FF == 255 00 = 0 0D = 14 * * @return An uchar */
uchar Utils_String::Hex2Uchar(const std::string & str)
{
uchar res = 0;
for (const auto &s:str)
{
res = (res << 4) + Hex2Num(s);
}
return res;
}
/** * @fn std::string Utils_String::Num2Hex(uchar num, bool Up) * * @brief Number 2 hexadecimal 得到的結果只有 小寫 * * @author IRIS_Chen * @date 2019/12/18 * * @param num Number of * @param Up True to up * * @return The total number of 2 hexadecimal */
std::string Utils_String::Num2Hex(uchar num, bool Up)
{
std::map<int, char> t_base = {
{ 8,'o' },
{ 10,'d' },
{ 16,'x' } };
if (!t_base.count(base))
return "";
// 使用 sprintf 格式化輸出, 將數字 轉換成相應的進制值
std::string format = "%0" + std::to_string(width) + t_base.find(base)->second;
char *buf = new char[20];
sprintf(buf, format.c_str(), num);
std::string res=std::string(buf);
// 轉換大小寫
return Up ? StringUpper(res) : StringLowwer (res);
}
3. 給定字符串轉換成字符串數組
由於 原生的 string 存在字符轉指針的轉換
可以使用 原生的指針來處理
/** * @fn const uchar * Utils_String::String2Uchar(const std::string & str) * * @brief String 2 uchar * * @author IRIS_Chen * @date 2019/12/16 * * @param str The string * * @return Null if it fails, else a pointer to a const uchar */
const uchar * Utils_String::String2Uchar(const std::string & str)
{
return (uchar*)str.c_str();
}
/** * @fn std::string Utils_String::Uchar2String(const uchar * buffer) * * @brief Uchar 2 string * * @author IRIS_Chen * @date 2019/12/16 * * @param buffer The buffer * * @return A std::string */
std::string Utils_String::Uchar2String(const uchar * buffer)
{
std::string str = (char*)buffer;
return str;
}
4. hex 字符串 轉換 字符串數組
依次取兩個字符, 轉換成一個 ucahr 值 存入數據中即可
hex 字符串 與字符串數組的相互轉換
/** * @fn uchar * Utils_String::Hex2CharArr(uchar *&buffer, const std::string & str, bool flg_space) * * @brief Hexadecimal 2 character array hex 字符串 轉換成 數組 * * @author IRIS_Chen * @date 2019/12/18 * * @param [in,out] buffer [in,out] If non-null, the buffer * @param str The string * @param flg_space True to flg space hex 是否使用空格分割 * * @return Null if it fails, else a pointer to an uchar */
uchar * Utils_String::Hex2CharArr(uchar *&buffer, const std::string & str, bool flg_space)
{
// 出錯 只有兩個值 默認不符合 操作
if (str.size() < 3) return nullptr;
// 默認夠長 判斷是否存在空格
if (str[2] == ' ') flg_space = true;
int step = flg_space ? 3 : 2;
buffer = new uchar[(str.size() + 1) / step +1 ];
std::string str2 = "";
for (int i = 0; i < static_cast<int>(str.size());)
{
uchar ch = Hex2Uchar(str.substr(static_cast<size_t>(i), 2));
// 根據是否有空格選擇 移動
*(buffer + i/step) = ch;
i += step;
str2 += std::to_string((int)ch) + "-";
}
// LInfo("charArr:{}", str2);
return buffer;
}
/** * @fn std::string Utils_String::CharArr2Hex(uchar * buffer, int length, int flg_space) * * @brief Character array 2 hexadecimal 數組 轉換成 hex 字符 * * 將UCHAR 字符串依次轉換成 string 字符串 * * @author IRIS_Chen * @date 2019/12/18 * * @param [in,out] buffer If non-null, the buffer * @param length The length * @param flg_space The flg space * * @return A std::string */
std::string Utils_String::CharArr2Hex(uchar * buffer, int length, int flg_space)
{
std::string str = "";
// 讀取數組中所有字符
for (int i = 0; i < length; i++)
{
str += Num2Hex(*buffer++);
// 如果開啟空格的話 每兩個字符 之間加入一個空格 最后一個不加
if (flg_space && i != length - 1)
str += " ";
}
return str;
}
更多
大概就這么多常用的轉換過程, 還存在一個 單獨 數組轉換 字符串的函數, 可以使用 sprintf
格式化輸出, 或者使用 暴力循環取余得到最后的結果值
數字與字符串的轉換 可多位 多進制
/** * @fn std::string Utils_String::NumToString(int num, int width, int base) * * @brief Number to string 整型數據 前補 0 占位符顯示 進制 為 8 10 16 如果超過給出的寬度 原始寬度顯示 * * @author IRIS_Chen * @date 2019/12/3 * * @param num Number of * @param width The width * @param base The base * * @return The total number of to string */
std::string Utils_String::NumToString(int num, int width, int base)
{
#if 0
// 保證 存在進制 只考慮 2 8 , 10 16 進制
static std::map<int, char> t_base = {
{ 2,'0' },
{ 8,'o' },
{ 10,'d' },
{ 16,'x' } };
if (!t_base.count(base))
return "";
// 二進制 特殊處理
if (base == 2)
{
std::string str_8 = NumToString(num, width, 8);
// 設置8進制 轉2進制碼表, 轉換之后 去除前綴0 即可
static const std::vector<std::string> table = { "000","001","010","011","100","101","110","111" };
std::string str_2 = "";
for (auto s : str_8)
{
if(s =='0')
continue;
str_2 += table[s - '0'];
}
// 去掉前面所有的 0值 從1 開始
int pos = static_cast<int>(str_2.find_first_of('1'));
// #TODO(Schen00) 處理, 得到的長度小於width 的情況
return pos == std::string::npos ?"":str_2.substr (pos);
}
std::string format = "%0" + std::to_string(width) + t_base.find(base)->second;
char *buf = new char[20];
sprintf(buf, format.c_str(), num);
return std::string(buf);
#else
std::string res = "";
while (num)
{
res = base == 16 ? Num2Hex(num % base) : (num % base + '0') + res;
num /= base;
}
// 不足位 補0 足位 不刪除
int cnt = width - res.size();
while (cnt--)
{
res = '0' + res;
}
return res;
#endif
}