整活向:使用一个64位整型来存储电话号码
前因
今天在吃饭的时候刷着一条提问,说是电话号码用整型存储好还是字符串String类型存储好。许多人都是采用字符串类型进行存储,因为int32长度范围不够,即使用int64存储有些特殊符号也不好办。
于是我想整个活,用编码的方式将电话号码存储到一个整型中去。
分析
我们可以看电话号码的数字总共有10种,从0到9,而特殊符号#*+-,总共14种,可以用4个二进制位进行编码表示。
字符 | 二进制编码(4 位) |
---|---|
1 | 0001 |
2 | 0010 |
3 | 0011 |
4 | 0100 |
5 | 0101 |
6 | 0110 |
7 | 0111 |
8 | 1000 |
9 | 1001 |
0 | 1010 |
* | 1011 |
+ | 1100 |
= | 1101 |
- | 1110 |
然后最长的电话号码不超过11个字符,即使算上-符号也只有13个字符左右,因此按15个字符算,刚好60个二进制位,对于64位整型,最后剩下的4位还可以用来表示字符串长度用于标记有效的二进制位。
我们来举个例子,假设某人电话号码为2,那么就有1个字符,表示长度的二进制为0001,对于电话号码字符部分编码就是0010,那么64位整数转成二进制就是00100001。
我们在解码的时候就可以先读取低位上的4个二进制位获取长度,然后对这个长度乘4(十进制运算)得到有效的电话号码编码的二进制位数,在根据上表进行解码得到原本的电话号码。对于00100001来说,低4位为0001,转换成十进制为1,乘4就变成了4,表示后面4个二进制位是有效位,再取出0010转换成字符串就是2。
代码
我用C++简单写了写:
#include <iostream>
#include <map>
#include <string>
#include <bitset>
// 创建字符到整数的编码映射关系,这里简单示例用4位二进制对应的整数来编码
std::map<char, int> encodingMap = {
{'1', 1},
{'2', 2},
{'3', 3},
{'4', 4},
{'5', 5},
{'6', 6},
{'7', 7},
{'8', 8},
{'9', 9},
{'0', 10},
{'*', 11},
{'+', 12},
{'#', 13},
{'-', 14}
};
// 编码函数,将字符串按照规则编码为一个long long值
long long encode(const std::string& input) {
// 将字符串长度转换为4位二进制表示,存放在lengthBits中
std::bitset<4> lengthBits(input.size());
long long encodedValue = lengthBits.to_ulong();
// 遍历字符串,获取每个字符的编码并合并到encodedValue中
for (char c : input) {
if (encodingMap.find(c)!= encodingMap.end()) {
int code = encodingMap[c];
encodedValue <<= 4; // 左移4位,为新的编码腾出位置
encodedValue |= code; // 将编码合并进去
}
}
return encodedValue;
}
// 解码函数,将编码后的long long值还原为原始字符串
std::string decode(long long encodedData) {
std::string binaryStr;
// 将long long值转换为二进制字符串表示
while (encodedData > 0) {
binaryStr = (encodedData & 1? "1" : "0") + binaryStr;
encodedData >>= 1;
}
// 补齐前面可能缺失的0,确保长度是4的倍数
while (binaryStr.size() % 4!= 0) {
binaryStr = "0" + binaryStr;
}
// 获取长度部分(前4位对应的十进制值)
int length = std::stoi(binaryStr.substr(0, 4), nullptr, 2);
std::string result;
// 按照每4位一组解析编码还原字符串
for (size_t i = 4; i < binaryStr.size() && result.size() < length; i += 4) {
std::string codeStr = binaryStr.substr(i, 4);
int code = std::stoi(codeStr, nullptr, 2);
for (const auto& p : encodingMap) {
if (p.second == code) {
result += p.first;
break;
}
}
}
return result;
}
int main() {
std::cout << "请输入:" << std::endl;
std::string input = "";
std::cin >> input;
long long encoded = encode(input);
std::string decoded = decode(encoded);
std::cout << "原始字符串: " << input << std::endl;
std::cout << "编码后的值: " << encoded << std::endl;
std::cout << "解码后还原的字符串: " << decoded << std::endl;
return 0;
}
编译运行一下:
请输入:
+8613377777777
原始字符串: +8613377777777
编码后的值: 1065208185548666743
解码后还原的字符串: +8613377777777
结束
当然对于这个程序只是整活方向的,主要是用来体会这种节省空间的存储方式的思想。
例如对于某些图形库,他们RGB坐标使用int32整型就可以了,在某些程序员可能会定义3个int32类型而浪费了部分空间。
在实际项目中,对于电话号码,我更倾向于使用字符串存储,对于项目后续扩展以及理解业务逻辑有帮助。除非需要极致节省空间(比如一些硬件相关的),才会去使用这种方式去存储。