C++ 笔记——字符串自定义加密处理

版权声明:欢迎转载,转载请保留文章出处。 https://blog.csdn.net/junzia/article/details/85466578

根据惯例,先放定义。加密,是以某种特殊的算法改变原有的信息数据,使得未授权的用户即使获得了已加密的信息,但因不知解密的方法,仍然无法了解信息的内容。 加密算法非常多,常见的加密算法有MD5、AES、Base64、DES等等。但是此篇博客记录的加密算法和上述加密算法无关,主要记录的是按照个人喜好对字符串做处理的简单方法。

字符串编码

一段字符串,把所有的a变成c,把c变成d,把d变成a,我们可以把这样改变原有信息的处理过程看做加密。拿到处理后的字符串的人,重新把字符串中所有的a变成d,把d变成c,把c变成a。这样他才能得到原来的字符串。我们可以把这个根据加密算法对加密后的数据逆向恢复成原有数据的过程叫做解密。
我们知道,在ASCII表中字符(也就是英文字母、数字和一些标点符号及控制符),我们都可以用一个char来存储。其他的字符,比如汉字、韩文、日文等,都需要用wchar_t才能存储。而charwchar_t只是内存上的区别,就如同charint一样,我们可以把一个int用四个char来存储,内容并不会改变。
存储位文本文件时,字符串存储也会有不同的编码方法。比如我们所熟知的UTF-8、GB2312等。不同的编码方式存储相同的内容,一般得到的是不同结果。当程序需要重新从文本文件中读回内容时,就需要按照存储时候的规则(也就是编码方式),来用相应的解码方式来读回。
以UTF-8为例,我们以UTF-8的方式写入文本文件时,并不是直接把字符按照01010001这样的二进制方式直接把字符对应的值写入到文件中的。而是按照UTF-8的编码规范,把字符变换成一个新的数值,然后写入到文件中的。
UTF-8是一种变长编码,所有的字符并不是按照固定的长度来进行编码的,不同的字符所占的字节数可能会不同。按照UTF-8的编码规范会有以下规律:

  • 当一个字符转换成二进制后,用7位足以表示时,这个字符编码占一个字节,且最高位为0。
  • 当一个字符转换成二进制后,需要用8-11位表示时,这个字符编码占两个字节,且第一个字节前三位为110,第二个字节前两位为10。
  • 当一个字符转换成二进制后,需要用12-17位表示时,这个字符编码占三个字节,且第一个字节前四位为,1110,后两个字节的前两位都位10。
  • 当一个字符转换成二进制后,需要用更多位表示时,按照以上规律,进行编码。

而通常情况下,我们并不需要心UTF-8的加密算法,在我们存储文本时候,选择使用UTF-8即可。这是因为库函数已经为我们做好了文本存储编码的事情。

自定义加密

按照以上分析,我们对一段字符串进行自定义变换。
原始数据为:

{
"employees": [
{ "firstName":"Bill" , "lastName":"Gates" },
{ "firstName":"George" , "lastName":"Bush" },
{ "firstName":"Thomas" , "lastName":"Carter" }
]
}

将原始字符串自定义加密下。我们把所有的字符对于的值都左移位3位,然后加上106。这时,虽然我们的原始数据用的都是英文字符,但是因为进行了移位和加法运算,最终得到的值可能会超出char的取值范围,所以我们直接用wchar_t来操作了。

void encode(const std::string &file){
    std::wofstream outfile(file, ios::out);
    outfile.imbue(locale("zh_CN.UTF-8"));
    std::wstring data = L"{\n"
                        "\"employees\": [\n"
                        "{ \"firstName\":\"Bill\" , \"lastName\":\"Gates\" },\n"
                        "{ \"firstName\":\"George\" , \"lastName\":\"Bush\" },\n"
                        "{ \"firstName\":\"Thomas\" , \"lastName\":\"Carter\" }\n"
                        "]\n"
                        "}";
    for (wchar_t chr : data) {
        auto dt = ((chr << 3) + 106);
        outfile.put(dt);
    }
    outfile.close();
}

经过变换后,得到加密后的字符串:

тºźΒϒϪϊϢвΒΒЂźȺŪ͂ºтŪźΚβϺЂЊ˚ͲϒΒźȺźɺβϊϊźŪNJŪźϊͲЂЊ˚ͲϒΒźȺźʢͲЊΒЂźŪђNJºтŪźΚβϺЂЊ˚ͲϒΒźȺźʢΒϢϺ΢ΒźŪNJŪźϊͲЂЊ˚ͲϒΒźȺźɺВЂΪźŪђNJºтŪźΚβϺЂЊ˚ͲϒΒźȺź̊ΪϢϒͲЂźŪNJŪźϊͲЂЊ˚ͲϒΒźȺźʂͲϺЊΒϺźŪђº͒ºђ

这只是简单的加密,为了更安全,可以采用更复杂一些的算法。甚至在UTF-8编码规范上做手脚,比如将所有的编码字节数扩展一位,存放一个随机数,指向后续数据中的某位取反。等等,诸如此类的操作,只要保证操作可逆即可。

对应的解密

按照上述的加密算法,做相应的解密。总的来说,就一个方法,取出存储的字符,进行逆运算,减去106然后右移3位。因为存入文本文件中的字符,进行了变换,在原有的字符上进行了左移位和加法,无法保证所有变换后的字符是单字节字符。所以问题的重点其实是,要以多字节字符的方式来进行运算。
知道问题关键点,代码上的实现就有很多种方式了,以下有三种示例。

按照UTF-8编码规范来进行读取

void translate1(const std::string &file){
    std::ifstream infile(file,ios::binary);
    //转码参考Utf-8编码规范
    //如果字符头9位是0,则用一个字节表示,首位为0,后续7位保持不变
    //如果字符头5位是0,则用两个字节表示,第一个字节用110开头,第二个字节用10开头
    //否则用三个字节表示,首字节用1110开头,后面两个字节都用10开头
    //转码如下:
    while(!infile.eof()){
        short num = 0;
        unsigned char tmp;

        infile.read(reinterpret_cast<char *>(&tmp), 1);

        if((tmp>>7) == 0){
            num = short(tmp&0x7f);
        }else if((tmp>>5) == 6){
            num = short(tmp & 0x1F);
            infile.read(reinterpret_cast<char *>(&tmp), 1);
            num = static_cast<short>((num << 6) | (tmp & 0x3F));
        }else if((tmp>>4) == 14){
            num = short(tmp & 0x0F);
            infile.read(reinterpret_cast<char *>(&tmp), 1);
            num = static_cast<short>((num << 6) | (tmp & 0x3f));
            infile.read(reinterpret_cast<char *>(&tmp), 1);
            num = static_cast<short>((num << 6) | (tmp & 0x3f));
        }else{
            break;
        }
        auto dt = static_cast<char>((num - 106) >> 3);
        std::cout<<dt;
    }
    std::cout<<endl;
}

借助C++ 的codecvt中的转换函数来进行读取

void translate2(const std::string &file){
    std::ifstream infile(file,ios::binary);
    std::string data;
    infile>>data;
    std::wstring_convert<std::codecvt_utf8<wchar_t>> strCnv;
    wstring wss = strCnv.from_bytes(data);
    for (wchar_t ws : wss) {
        char d = static_cast<char>((ws - 106) >> 3);
        std::cout << d;
    }
    std::cout<<endl;
}

借助wifstream来实现读取

void translate3(const std::string &file){
    std::wifstream infile(file,ios::binary);
    infile.imbue(locale("zh_CN.UTF-8"));
    std::wstringstream wss;
    wss << infile.rdbuf();
    std::wstring ss = wss.str();
    for (wchar_t ws : ss) {
        char d = static_cast<char>((ws - 106) >> 3);
        std::cout << d;
    }
    std::cout<<endl;
}
展开阅读全文

没有更多推荐了,返回首页