前面提到,我们设计的Txt程序只能读取UTF-16-LE编码的文本,因为设定的就是按照这个格式来读取的,所以如果用这个程序去打开其他字符编码的Txt,那显然会出现乱码,这是不能被容忍的,哪能管得了用户呢,我们只能做兼容。。。。


这里只需要对函数bool CTxt0721View::ReadFileContents()进行修改就可,所有文本读取都是通过这个函数实现的,只需要在这里判断出不同的编码格式,然后采取不同方式读取即可,最后还是把这些文本转化成UTF-16-LE来处理,(主要是为了使用CString,这也是为了配合VS都使用宽字节)

先放代码:

 1 // 新版本,支持多种Txt文本的读取(UTF-8BOM、ANSI、Unicode16-LE、UTF-8)
 2 //            // 以什么格式打开,就以什么格式写回去(没实现这个功能)
 3 bool CTxt0721View::ReadFileContents()
 4 {
 5     CFile read_file;
 6     DWORD file_length;            // 文件字节长度
 7     char * file_contents;        // 初步读取的文件内容--char
 8 
 9     try
10     {
11         // 只读模式打开文档
12         read_file.Open(TxtFileLoad, CFile::modeRead | CFile::typeBinary);
13         // 获取整个文件的字节长度--逻辑长度
14         file_length = read_file.GetLength();
15         // 创建字符串存储内容
16         file_contents = new char[file_length + 2];
17         memset(file_contents, 0, sizeof(char)*(file_length + 2));
18         // 一次性读取整个文件
19         read_file.Read(file_contents, sizeof(char)*file_length);
20         // 文件末尾可能不会有\0(就是没有),所以加上结束符
21         file_contents[file_length] = '\0';
22         file_contents[file_length + 1] = '\0';
23         // 关闭文件
24         read_file.Close();
25     }
26     catch (CFileException *e)
27     {
28         CString read_errror_str;
29         read_errror_str.Format(L"保存数据失败的原因是:%d", e->m_cause);
30         MessageBox(read_errror_str);
31         read_file.Abort();
32         e->Delete();
33         // 读取文件失败
34         return false;
35     }
36 
37     // 获取Txt文件编码格式(UTF-8BOM、ANSI、Unicode16-LE)
38     WORD wFlag = 0;
39     memcpy(&wFlag, file_contents, 2);
40 
41     //CString str;
42     //str.Format(L"%X%X", LOBYTE(wFlag),HIBYTE(wFlag));
43     //MessageBox(str);
44 
45     wchar_t* m_readTxt;                    // 存储转换后的Unicode16LE字符
46 
47     if (wFlag == 0xFEFF)                // Unicode16-LE
48     {                                    // 摊牌--不干了
49 //        MessageBox(L"Unicode16-LE");
50         //wchar_t* m_readTxt = (LPTSTR)file_contents;
51         //m_readTxt[file_length / 2] = '\0';            // 不开空间,直接用原来的空间 
52         //TxtFileString = (CString)m_readTxt;            
53         m_readTxt = new wchar_t[file_length / 2 + 1];
54         _tcscpy_s(m_readTxt, file_length / 2, (LPTSTR)file_contents + 1);            // 这个标志无法转,必须干掉
55         m_readTxt[file_length / 2] = '\0';
56         TxtFileString = (CString)m_readTxt;
57     }
58     else if(wFlag == 0xBBEF)            // UTF-8BOM   ?   UTF-8它不带标志。。需要根据其编码特征判断(有点麻烦
59     {                // (wFlag == 0xBBEF || IsUTF8(file_contents + 3, file_length - 3))
60 //        MessageBox(L"UTF-8BOM");(LPCSTR)
61         // int MultiByteToWideChar(UINT CodePage,DWORD dwFlags,LPCSTR lpMultiByteStr,                        \+
62         // int cchMultiByte,LPWSTR lpWideCharStr,int cchWideChar);
63         // 如果函数运行成功,并且cchMultiByte为0,返回值是待转换字符串的缓冲区所需求的宽字符数大小。
64         // (此种情况用来获取转换所需的wchar_t的个数)
65         int len = MultiByteToWideChar(CP_UTF8, NULL, file_contents + 3, file_length - 3, NULL, 0);
66         //分配空间要给'\0'留个空间,MultiByteToWideChar不会给'\0'空间
67         m_readTxt = new wchar_t[len + 1];
68         //转换 ------- file_contents + 3, file_length - 3 主要是去掉BOM的三个字节的标志
69         MultiByteToWideChar(CP_UTF8, NULL, file_contents + 3, file_length - 3, m_readTxt, len);
70         //最后加上'\0'
71         m_readTxt[len] = '\0';
72         //unicode版的MessageBox API
73         TxtFileString = (CString)m_readTxt;
74     }
75     else if (IsUTF8(file_contents + 3, file_length - 3))
76     {
77         int len = MultiByteToWideChar(CP_UTF8, NULL, file_contents, file_length, NULL, 0);
78         m_readTxt = new wchar_t[len + 1];
79         MultiByteToWideChar(CP_UTF8, NULL, file_contents, file_length, m_readTxt, len);
80         m_readTxt[len] = '\0';
81         TxtFileString = (CString)m_readTxt;
82     }
83     else                                // ANSI   问题解决
84     {
85 //        MessageBox(L"ANSI");
86         int len = MultiByteToWideChar(CP_ACP, NULL, file_contents, file_length, NULL, 0);
87         //分配空间要给'\0'留个空间,MultiByteToWideChar不会给'\0'空间
88         m_readTxt = new wchar_t[len + 1];
89         //转换
90         MultiByteToWideChar(CP_ACP, NULL, file_contents, file_length, m_readTxt, len);
91         //最后加上'\0'
92         m_readTxt[len] = '\0';
93         TxtFileString = (CString)m_readTxt;
94     }
95     delete[] file_contents;
96     delete[] m_readTxt;
97     return true;
98 }
老规矩,第一部分,先打开文件,然后全部读取出来,这里要不要管编码格式问题呢?
不用,因为此处我们是使用char存下来的,不管你什么格式,在电脑中都是一个一个字节存下来的,现在的读取无非就是把这块内存拿了出来,
然后就是怎么判断属于哪一种编码格式?
有的编码格式会在头部加入标志,比如UTF-16-LE,UTF-8-BOM,但是Windows下的UTF-8和ANSI是没有标志的,暂且不管这两个家伙;
utf-8-BOM 前面有三个标志字节efbbbf       ,,,   unicode 标志头 fffe       当然这里还要看大端小端存储方式
这两个判断就方便多了,直接对比开头的字节,就知道是哪一个编码格式了,剩下的UTF-8和ANSI都没有标志,只能通过其字符编码特性来判断了,(这里百度查资料)(如何判断一个文本文件内容的编码格式 UTF-8 ? ANSI(GBK)
 1 // 判断是否是UTF-8编码的Txt文本 --https://blog.csdn.net/jiangqin115/article/details/42684017
 2 bool CTxt0721View::IsUTF8(const void* pBuffer, long size)
 3 {
 4     bool IsUTF8 = true;
 5     unsigned char* start = (unsigned char*)pBuffer;
 6     unsigned char* end = (unsigned char*)pBuffer + size;
 7     while (start < end)
 8     {
 9         if (*start < 0x80) // (10000000): 值小于0x80的为ASCII字符  
10         {
11             start++;
12         }
13         else if (*start < (0xC0)) // (11000000): 值介于0x80与0xC0之间的为无效UTF-8字符  
14         {
15             IsUTF8 = false;
16             break;
17         }
18         else if (*start < (0xE0)) // (11100000): 此范围内为2字节UTF-8字符  
19         {
20             if (start >= end - 1)
21             {
22                 break;
23             }
24             if ((start[1] & (0xC0)) != 0x80)
25             {
26                 IsUTF8 = false;
27                 break;
28             }
29             start += 2;
30         }
31         else if (*start < (0xF0)) // (11110000): 此范围内为3字节UTF-8字符  
32         {
33             if (start >= end - 2)
34             {
35                 break;
36             }
37             if ((start[1] & (0xC0)) != 0x80 || (start[2] & (0xC0)) != 0x80)
38             {
39                 IsUTF8 = false;
40                 break;
41             }
42             start += 3;
43         }
44         else
45         {
46             IsUTF8 = false;
47             break;
48         }
49     }
50     return IsUTF8;
51 }
View Code

解决一部分问题,能够分清楚当前Txt是哪一种编码格式后,还需要对读取的数据进行转码,统一转化为Unicode(UTF-16)编码来处理。

1、如果本来就是UTF-16:那存在char中的数据实际上就是宽字节的格式存储的,可以直接让一个wchar_t指针指向这块空间,它完全符合这种编码格式,在代码中还是重新开辟了空间来存储,同时也为了加上'\0',因为真的会因为这个出现问题(记得去掉头部标志哦,还有加上最后的'\0')

2、如果本来就是UTF-8-BOM:那这里就要进行转码了,因为该编码格式中英文占一个字节,中文三字节,和UTF-16中的统一两字节不符,这里主要是利用MultiByteToWideChar函数了,网上资料很多,就不说了(其实说不出来,哈哈哈哈)(记得去掉头部标志哦,还有加上最后的'\0')

3、UTF-8:转码同UTF-8-BOM,只是它没有三个字节的标志,不需要去掉头部标志(加上最后的'\0')

4、ANSI:没人认领的就是它了,还是MultiByteToWideChar函数,其实用的还是挺多的,有机会会去详细了解的,现在就算了

问题解决了,就这样吧,累了累了。。。

其实MultiByteToWideChar说简单也简单,说难也难,主要在于你是否搞清楚了字节长度、字符长度、宽字节这些问题,主要是空间开辟的够不够,会不会爆

 

2022-08-17(0810.。。)

posted on 2022-08-17 23:00  夜_归_人  阅读(1147)  评论(0编辑  收藏  举报