深入理解异或运算(XOR)及应用
异或运算(XOR)有一个非常重要的性质:自反性。也就是说,对一个数据进行两次相同的异或操作,会得到原始数据。
异或运算是按位进行操作的,在按位进行操作之前,先对数据进行转换,转换为 二进制, 然后再进行异或运算;敲黑板(重点):按位异或(^)的规则:相同为0,不同为1
0 和 0 异或 = 0
1 和 1 异或 = 0
0 和 1 异或 = 1
1 和 0 异或 = 1
首先要先了解一下ASCII编码,可以自行搜索一下ASCII表:
ASCII 编码于 1967 年第一次发布,最后一次更新是在 1986 年,迄今为止共收录了 128 个字符,包含了基本的拉丁字母(英文字母)、阿拉伯数字(也就是 1234567890)、标点符号(,.!等)、特殊符号(@#$%^&等)以及一些具有控制功能的字符(往往不会显示出来)(此段文字是我复制别人的)
以下为部分ASCII编码:
从一个简单的例子开始:
给出一个字符:a, 这个字符就是原始数据, 然后再给出一个任意字符/数字,比如:8, 我们可以称这个数为密钥, 'a' 的 ASCII为97(转换为二进制:01100001), 8的二进制:00001000
97是如何转换为二进制的?二进制是有0和1组成,满2进1,用0000 0001 表示 1, 用0000 0010表示2, 0000 0011 表示3,由此可得,从右往左第1位为1的时候,是1, 第2位为1的时候是2,第3位为1的时候是4,第4位为1的时候是8
得出:
那么 97 如何转换为二进制呢?
97 = 64 + 32 + 1, 放入二进制中就是 0000 0110 0001, 前面的0可以忽略,变成 110 0001,为了好看(或方便计算),保留一个: 0110 0001
8的二进制,就相对简单了
好了,到这里,大概的了解了二进制的转换和使用,回到前面:按位异或(^)的规则:相同为0,不同为1
按照规则开始计算 ‘a’ 和 8的异或运算,得到:
最后的结果是 105 (这个数据,我们暂时把他理解为加密后的数据)
回到最开头的那句话,异或运算(XOR)有自反性,对 105进行解密,需要一个“密钥”,前面使用的“密钥” 是 8, 此时使用 8 去界面 105 ,看看得到什么?
同样使用按位异或规则,进行运算(相同为0,不同为1):
此时数据又变回为 00 0110 0001(简化:0110 0001),转换为10进制也就是 97。这就是异或运算的自反性
换一个其他的是否也可行呢?开始操作;
举例:
给出一个字符:K(大写),"密钥":c(小写)
K:转换为ASCII值为:75,二进制:00 0100 1011
c:转换为ASCII值为:99, 二进制:00 0110 0011
K 和 c 按异或运算后((相同为0,不同为1)),会得到以下数据(相信大家都会了):
最终的结果是: 40 (二进制为:00 0010 1000)
对该数据进行“解码”, 传入“密钥”:c, 得到如下数据:
此时有还原得到:75 (二进制:00 0100 1011)
看到到此处,你已经理解了异或运算规则和使用。以上是对单个字符进行异或运算,那能不能对多个字符进行异或运算?当然可以:
举例:
给出数据:HelloWorld
给出“密钥”:b
分别对HelloWorld中的八个字符进行逐一异或
H二进制:0100 1000
e二进制:0110 0101
l二进制:0110 1100
o二进制:0110 1111
W二进制:0101 0111
r二进制:0111 0010
d二进制:0110 0100
b二进制:0110 0010
HelloWorld的全部转为为二进制后,数据如下:
0100 1000 0110 0101 0110 1100 0110 1100 0110 1111 0101 0111 0110 1111 0111 0010 0110 1100 0110 0100
和 b 进行异或运算((相同为0,不同为1)), 得到以下数据:
0010 1010 0000 0111 0000 1110 0000 1110 0000 1101 0011 0101 0000 1101 0001 0000 0000 1110 0000 0110
同样对该数据逆向异或运算,也能还原为HelloWorld
H e l l 0 W o r l
0100 1000 0110 0101 0110 1100 0110 1100 0110 1111 0101 0111 0110 1111 0111 0010 0110
———————————————————————————————————————————
d
1100 0110 0100
此时你已经学会了异或运算符的操作,那我们在哪里应用呢?
如我需要输入一段内容,然后对这段内容进行“加密”处理,对内容进行异或运算,别人就不能简单的查看到我的内容了。当我需要查看内容的时候,再对“加密”的内容进行解密,就能还原回原来的数据;
但通常我们要处理的数据都是较多,如果都是通过手动去计算的话,工作量就会很大,此时,我们需要编写一段代码,通过程序来帮我们解决该问题,张开手手就有:
#include <iostream>
#include <string>
using namespace std;int main()
{string data = "";cout << "请输入内容:" << endl;std::getline(std::cin, data);char key;cout << "请输入加密字符:";std::cin >> key;for (auto& c : data){c ^= key;}cout << endl;cout << "加密后:" << endl;cout << data << endl;for (auto& c : data){c ^= key;}cout << endl;cout << "解密后:" << endl;cout << data << endl;getchar();getchar();return 0;
}
测试一下:
输入内容:Following my completion of undergraduate studies, I am driven by a strong passion to pursue further academic accomplishments in the field of finance.
输入加密字符:p
结果:
为什么加密后显示异常?原因是加密后,直接转换为字符了,但有些字符是不可见的,所以会有一部分显示为空,或乱码。
然后重写解码后,又得到了原来的数据了;
同样也可以对中文进行异或运算
看完以上内容,此时你已经完全掌握了异或运算的操作了,实际的应用场景其实是比较多的,例如在app中输出的日志带有敏感信息的时候,又不想让别人轻易的知道输出的内容,自行写一个加密、解密算法其实也是比较耗时,而且容易影响运行的性能,一个简单的异或运算操作,就能轻松的处理过滤了敏感信息;且别人也不会轻易的查看到输出的信息。我们想要查看内容的时候,重新进行异或运算即可得到原数据