整活向:使用一个64位整型来存储电话号码

前因

今天在吃饭的时候刷着一条提问,说是电话号码用整型存储好还是字符串String类型存储好。许多人都是采用字符串类型进行存储,因为int32长度范围不够,即使用int64存储有些特殊符号也不好办。

于是我想整个活,用编码的方式将电话号码存储到一个整型中去。

分析

我们可以看电话号码的数字总共有10种,从0到9,而特殊符号#*+-,总共14种,可以用4个二进制位进行编码表示。

字符二进制编码(4 位)
10001
20010
30011
40100
50101
60110
70111
81000
91001
01010
*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类型而浪费了部分空间。

在实际项目中,对于电话号码,我更倾向于使用字符串存储,对于项目后续扩展以及理解业务逻辑有帮助。除非需要极致节省空间(比如一些硬件相关的),才会去使用这种方式去存储。

最后修改:2024 年 12 月 04 日
如果觉得我的文章对你有用,请随意赞赏