#include <iostream> #include <fstream> #include <locale> using namespace std; int main() { setlocale(LC_ALL, "chs"); // 设置字符编码环境 char buf; int x; wchar_t y, temp; ifstream is("D:\\存档\\OneDrive\\桌面\\作业\\测试文档.docx", ios::in); while(is.eof() == false) { is.read(&buf,sizeof(buf)); if(int(buf) < 0) { y = 0x0000; y = buf; // 取第一个字节的后4位 y <<= 6; // 为下一个字节的六位准备空间 is.read(&buf,sizeof(buf)); // 取第二个字节的后六位 temp = buf; temp = temp & (63); // 取出temp的后六位 y |= temp; y <<= 6; // 为下一个字节的六位准备空间 is.read(&buf, sizeof(buf)); temp = buf; temp = temp & (63); y |= temp; printf("%S", &y); continue; } else { y = buf; printf("%s", &y); } } cout << endl; is.close(); return 0; }
在解决这个问题之前,我们需要先熟悉一下utf-8编码,大多数人认为中文在utf-8编码 里只占两个字节,其实这么表述是有问题的,真正的utf-8编码中,中文占据三个字节, 只不过只有两个字节是用来表示是哪个汉字的而已,具体详见下面这位大佬的文章
大佬文章
从上面的文章和这张图可以知道,ascii 表里面有的字符用的是第一种一个字节的编码方式, 而汉字用的是第三种编码方式,所以如果文档中只包含字符和汉字这两种字符的话,只需要 分类处理这两种情况就行了,而从图中可以发现中文编码和其他字符的不同之处: 其他编码(只包含ascii里的字符)的字节都是以0开头的 中文编码的三个字节都是从1开始的 所以,先将一个字节的内容读出来,之后将这个字节强转成为有符号数 int 类型,那么就会 出现所有的中文字符都是负数,所有的非中文字符都是正数,所以,对于强转之后是正数的 情况,直接输出就可以了,对于强转之后是负数的情况,就读取三个字节,之后将1号字节的 后四位取出来,2号字节的后六位取出来,3号字节的后六位取出来,并且将这些取出来的 二进制位重新拼接成为一个16位的字符,之后输出的就是汉字了,如果想要将汉字重新写回 文档并且不发生乱码的话,只需要将是汉字的部分重新构造成上面图中的三个字节编码的格式 写回文档就可以了
这份代码里面的第一行代码是用来设置字符编码环境的 wchar_t 是宽字符,占据16位二进制位,定义原型是 typedef short wchar_t; sprintf(%S, ..) 中的大S是专门用来输出宽字符的输出格式,这个格式必须在设置完编码格式之后才有效 c++的移位运算是算数移位 10进制的63是 0011 1111,只是c++里面不允许直接使用二进制值,所以我将这串二进制转换成为了十进制