https://www.cnblogs.com/2015-16/p/16588327.html
MFC实现Txt04之打开各种编码文本(ANSI,UTF-8,UTF-16)
// 新版本,支持多种Txt文本的读取(UTF-8BOM、ANSI、Unicode16-LE、UTF-8)
// // 以什么格式打开,就以什么格式写回去(没实现这个功能)
bool CTxt0721View::ReadFileContents()
{
CFile read_file;
DWORD file_length; // 文件字节长度
char * file_contents; // 初步读取的文件内容--char
try
{
// 只读模式打开文档
read_file.Open(TxtFileLoad, CFile::modeRead | CFile::typeBinary);
// 获取整个文件的字节长度--逻辑长度
file_length = read_file.GetLength();
// 创建字符串存储内容
file_contents = new char[file_length + 2];
memset(file_contents, 0, sizeof(char)*(file_length + 2));
// 一次性读取整个文件
read_file.Read(file_contents, sizeof(char)*file_length);
// 文件末尾可能不会有\0(就是没有),所以加上结束符
file_contents[file_length] = '\0';
file_contents[file_length + 1] = '\0';
// 关闭文件
read_file.Close();
}
catch (CFileException *e)
{
CString read_errror_str;
read_errror_str.Format(L"保存数据失败的原因是:%d", e->m_cause);
MessageBox(read_errror_str);
read_file.Abort();
e->Delete();
// 读取文件失败
return false;
}
// 获取Txt文件编码格式(UTF-8BOM、ANSI、Unicode16-LE)
WORD wFlag = 0;
memcpy(&wFlag, file_contents, 2);
//CString str;
//str.Format(L"%X%X", LOBYTE(wFlag),HIBYTE(wFlag));
//MessageBox(str);
wchar_t* m_readTxt; // 存储转换后的Unicode16LE字符
if (wFlag == 0xFEFF) // Unicode16-LE
{ // 摊牌--不干了
// MessageBox(L"Unicode16-LE");
//wchar_t* m_readTxt = (LPTSTR)file_contents;
//m_readTxt[file_length / 2] = '\0'; // 不开空间,直接用原来的空间
//TxtFileString = (CString)m_readTxt;
m_readTxt = new wchar_t[file_length / 2 + 1];
_tcscpy_s(m_readTxt, file_length / 2, (LPTSTR)file_contents + 1); // 这个标志无法转,必须干掉
m_readTxt[file_length / 2] = '\0';
TxtFileString = (CString)m_readTxt;
}
else if(wFlag == 0xBBEF) // UTF-8BOM ? UTF-8它不带标志。。需要根据其编码特征判断(有点麻烦
{ // (wFlag == 0xBBEF || IsUTF8(file_contents + 3, file_length - 3))
// MessageBox(L"UTF-8BOM");(LPCSTR)
// int MultiByteToWideChar(UINT CodePage,DWORD dwFlags,LPCSTR lpMultiByteStr, \+
// int cchMultiByte,LPWSTR lpWideCharStr,int cchWideChar);
// 如果函数运行成功,并且cchMultiByte为0,返回值是待转换字符串的缓冲区所需求的宽字符数大小。
// (此种情况用来获取转换所需的wchar_t的个数)
int len = MultiByteToWideChar(CP_UTF8, NULL, file_contents + 3, file_length - 3, NULL, 0);
//分配空间要给'\0'留个空间,MultiByteToWideChar不会给'\0'空间
m_readTxt = new wchar_t[len + 1];
//转换 ------- file_contents + 3, file_length - 3 主要是去掉BOM的三个字节的标志
MultiByteToWideChar(CP_UTF8, NULL, file_contents + 3, file_length - 3, m_readTxt, len);
//最后加上'\0'
m_readTxt[len] = '\0';
//unicode版的MessageBox API
TxtFileString = (CString)m_readTxt;
}
else if (IsUTF8(file_contents + 3, file_length - 3))
{
int len = MultiByteToWideChar(CP_UTF8, NULL, file_contents, file_length, NULL, 0);
m_readTxt = new wchar_t[len + 1];
MultiByteToWideChar(CP_UTF8, NULL, file_contents, file_length, m_readTxt, len);
m_readTxt[len] = '\0';
TxtFileString = (CString)m_readTxt;
}
else // ANSI 问题解决
{
// MessageBox(L"ANSI");
int len = MultiByteToWideChar(CP_ACP, NULL, file_contents, file_length, NULL, 0);
//分配空间要给'\0'留个空间,MultiByteToWideChar不会给'\0'空间
m_readTxt = new wchar_t[len + 1];
//转换
MultiByteToWideChar(CP_ACP, NULL, file_contents, file_length, m_readTxt, len);
//最后加上'\0'
m_readTxt[len] = '\0';
TxtFileString = (CString)m_readTxt;
}
delete[] file_contents;
delete[] m_readTxt;
return true;
}
老规矩,第一部分,先打开文件,然后全部读取出来,这里要不要管编码格式问题呢?
不用,因为此处我们是使用char存下来的,不管你什么格式,在电脑中都是一个一个字节存下来的,现在的读取无非就是把这块内存拿了出来,
然后就是怎么判断属于哪一种编码格式?
有的编码格式会在头部加入标志,比如UTF-16-LE,UTF-8-BOM,但是Windows下的UTF-8和ANSI是没有标志的,暂且不管这两个家伙;
utf-8-BOM 前面有三个标志字节efbbbf ,,, unicode 标志头 fffe 当然这里还要看大端小端存储方式
这两个判断就方便多了,直接对比开头的字节,就知道是哪一个编码格式了,剩下的UTF-8和ANSI都没有标志,只能通过其字符编码特性来判断了,(这里百度查资料)(如何判断一个文本文件内容的编码格式 UTF-8 ? ANSI(GBK))
// 判断是否是UTF-8编码的Txt文本 --https://blog.csdn.net/jiangqin115/article/details/42684017
bool CTxt0721View::IsUTF8(const void* pBuffer, long size)
{
bool IsUTF8 = true;
unsigned char* start = (unsigned char*)pBuffer;
unsigned char* end = (unsigned char*)pBuffer + size;
while (start < end)
{
if (*start < 0x80) // (10000000): 值小于0x80的为ASCII字符
{
start++;
}
else if (*start < (0xC0)) // (11000000): 值介于0x80与0xC0之间的为无效UTF-8字符
{
IsUTF8 = false;
break;
}
else if (*start < (0xE0)) // (11100000): 此范围内为2字节UTF-8字符
{
if (start >= end - 1)
{
break;
}
if ((start[1] & (0xC0)) != 0x80)
{
IsUTF8 = false;
break;
}
start += 2;
}
else if (*start < (0xF0)) // (11110000): 此范围内为3字节UTF-8字符
{
if (start >= end - 2)
{
break;
}
if ((start[1] & (0xC0)) != 0x80 || (start[2] & (0xC0)) != 0x80)
{
IsUTF8 = false;
break;
}
start += 3;
}
else
{
IsUTF8 = false;
break;
}
}
return IsUTF8;
}