串口字符串-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 ' ' 是结束,所以处理的时候尽量传入 长度 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
}