问题描述
小R 负责解析IP报文头信息,现有一个十六进制格式的IP报文头数据 header,他需要从中解析并输出其中的总长度、标志位以及目的IP地址,用逗号分隔。
IP报文头信息依次包含多个字段,其中标识(16位)和目的IP地址(32位)是重点。输入数据为合法的十六进制IP报文头,固定长度为59个字符,每两个十六进制数字表示一个字节,字节之间以单空格分隔。
注:报文数据为大端序(即高位字节在低地址),小R需要将这些数据进行解析,输出的总长度和标志为十进制整数,目的IP地址为点分十进制格式(如192.168.20.184)。

返回规则如下:
- 解析其中的总长度、标志位以及目的IP地址,用逗号分隔。
示例 1:
输入:
header = "45 00 10 3c 7c 48 20 03 80 06 00 00 c0 a8 01 02 c0 a8 14 b8"
输出:"4156,1,192.168.20.184"
示例 2:
输入:
header = "4b ba 0d 15 d0 42 16 bc 50 25 38 33 cb e0 77 ed 56 a4 30 46"
输出:"3349,0,86.164.48.70"
示例 3:
输入:
header = "f7 87 78 be cf bf ae 9e d6 bc b1 5f 38 2c 07 37 95 f8 32 c5"
输出:"30910,5,149.248.50.197"
分析
观察IP报文头的字符串表示header,可知header
- 有空格,
- 每个空格之间的内容是1个字节,以16进制表示。
注意到题目要求解析的“标志位”占3位,不是4的倍数。
因此,代码如果能将16进制表示转为2进制表示,那么才容易从header选出代表标志位的子序列。
笔者2进制表示的思路有两种:
- 使用无符号整数数组,
- 使用只含有
'0'和'1'的字符串。
第1种思路和现实情景中处理IP报文头的代码逻辑是一致的。 由于报文头中存在像标志位这种长度较小的字段, 代码必然需要使用与运算选出字段,还可能需要使用右位移运算将字段的最低位移动至第1位。 这种思路实现起来有些麻烦,且不方便debug。
第2种思路实现起来较为直观。代码先将header转换为'0''1'串,
然后根据题目要求,从串中截取出所需子串,而不需要使用位运算。
不过最后还要将选取的子串转化为整数。
笔者采用该思路。
题解
#include <array>
#include <map>
#include <sstream>
#include <string>
constexpr auto idx2bin { []() {
std::array<std::array<char, 5>, 16> tmp {};
for (int i = 0; i != 16; ++i)
for (int j = 0; j != 4; ++j)
tmp[i][j] = i & 8 >> j ? '1' : '0';
return tmp;
}() };
int hex2idx(char hex)
{
static std::map<char, int> memo;
auto it = memo.find(hex);
if (it != memo.end())
return it->second;
return memo[hex] = hex & 16 ? hex - '0' : (hex & 7) + 9;
}
std::string solution(const std::string& header)
{
std::string bits;
for (auto&& c : header)
if (c != ' ')
bits += idx2bin[hex2idx(c)].data();
std::stringstream ans;
ans << std::stoi(bits.substr(16, 16), nullptr, 2) << ',' << std::stoi(bits.substr(48, 3), nullptr, 2) << ','
<< std::stoi(bits.substr(128, 8), nullptr, 2) << '.' << std::stoi(bits.substr(136, 8), nullptr, 2) << '.'
<< std::stoi(bits.substr(144, 8), nullptr, 2) << '.' << std::stoi(bits.substr(152, 8), nullptr, 2);
return ans.str();
}