必须加std::ios::binary标志,否则Windows会将\r\n转为\n导致字节数减少、位置错乱;Linux/macOS虽影响小,但跨平台代码需统一处理。

为什么直接用 std::ifstream 读二进制文件必须加 std::ios::binary
不加这个标志,Windows 下会把 \r\n 自动转成单个 \n,读出来的字节数变少、位置错乱,异或后必然解密失败。Linux/macOS 虽然影响小,但跨平台代码必须统一处理。
实操建议:
- 打开输入文件用
std::ifstream fin("in.bin", std::ios::binary) - 打开输出文件用
std::ofstream fout("out.bin", std::ios::binary) - 检查是否成功:
if (!fin.is_open()) { /* 报错 */ }
如何安全地逐字节异或而不破坏文件长度
异或操作本身是可逆的,但常见错误是把文件当文本读(比如用 fin >> ch),这会跳过空字节 \0 和控制字符,导致数据截断。必须用 read() 或 get() 按字节/块提取原始字节流。
推荐做法:
立即学习“C++免费学习笔记(深入)”;
- 用
fin.read(reinterpret_cast<char>(&byte), 1)</char>单字节读取(适合小文件) - 大文件优先用缓冲区:
std::vector<char> buf(8192); fin.read(buf.data(), buf.size()); size_t n = fin.gcount();</char> - 每次读完立即异或并写入:
buf[i] ^= key;,fout.write(buf.data(), n);
char 类型做异或时符号位会导致问题吗
不会。C++ 标准规定:异或(^)是对整数类型的位运算,char 在参与运算前会整型提升为 int,但只要原值是 0–255 范围内的字节(即你从二进制文件读出的原始字节),异或结果再存回 char 时,只取低 8 位,完全等价于无符号字节运算。
注意点:
- 别用
signed char或unsigned char做变量类型纠结——直接用char即可,它只是 8 位容器 - 密钥建议用
unsigned char key = 0x5a;明确范围,避免负值混淆 - 如果要做多字节密钥循环(如用字符串 "abc" 当密钥),记得对密钥长度取模:
byte ^= key_str[i % key_str.length()]
加解密函数要不要封装成可重用的接口
要,但别过度设计。一个接受 std::istream& / std::ostream& 和 unsigned char key 的函数就够用,便于测试和复用。
示例骨架:
void xor_crypt(std::istream& in, std::ostream& out, unsigned char key) {
char byte;
while (in.get(byte)) {
byte ^= key;
out.put(byte);
}
}
// 调用: xor_crypt(fin, fout, 0x9e);
这个版本简单、无内存拷贝、支持任意流(包括 std::cin/std::cout 测试),但要注意 in.get() 返回 int,需判断 == EOF 才更健壮;实际项目中建议改用 in.read() + gcount() 更可控。
加密本身不解决密钥分发和完整性校验,异或只是基础工具;真正容易被忽略的是:文件末尾的字节对齐、密钥硬编码在代码里、没做读写失败检查——这些比算法本身更容易让整个方案失效。



















