关于Unicode与ANSI编码转换技术和文本文件的识别、无BOM头的Utf8的编码识别算法
今日单词:
Parse:解析,(协议解析)
codec:编码器 n.
code:代码 n.
Wide:宽的 adj.
automatically:自动地 adv.
require:需要 v.
1、MultiByte和WideChar
a)ANSI 和 UTF8 都是MultiByte(俗称窄字节)
b)UTF16大端和小端都是WideChar(俗称宽字节)
Win32 API提供了一些函数来转换文本编码。下面是一些常用的转换函数:
MultiByteToWideChar:将ANSI编码的文本转换为Unicode编码的文本
WideCharToMultiByte:将Unicode编码的文本转换为ANSI或Utf-8编码的文本
MultiByteToWideChar和WideCharToMultiByte的组合:将ANSI编码的文本转换为UTF-8编码的文本
以下是使用这些函数进行转换的示例代码:
2、文本文件编码识别:
a)ANSI没有头部标识
b)U16大端和小端的头部标识:0xFFFE 和 0xFEFF
c)U8的头部标识:0xEF BB BF
WideCharToMultiByte 函数是用于将宽字符编码转换为多字节编码的函数,其参数如下:int WideCharToMultiByte(UINT CodePage, // [in] 多字节编码的代码页标识符 ANSI 或UTF8 目标要输出的种类DWORD dwFlags, // [in] 转换标志LPCWCH lpWideCharStr, // [in] 宽字符字符串指针 来源文字int cchWideChar, // [in] 宽字符字符串的字符数 带入-1代表自动计算长度,除非截取LPSTR lpMultiByteStr, // [out] 多字节字符串指针 目标空间 int cbMultiByte, // [in] 多字节字符串缓冲区的大小 空间长度(边界限制)LPCCH lpDefaultChar, // [in] 用于表示未转换字符的默认字符指针LPBOOL lpUsedDefaultChar // [out] 指针,用于指示是否使用了默认字符来转换字符
);int n = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL); n = WideCharToMultiByte(CP_ACP, 0, str, -1, p, n, NULL, NULL);
参数具体含义如下:CodePage:多字节编码的代码页标识符,用于指定要使用的多字节编码类型,例如 CP_ACP、CP_UTF8等。
dwFlags:转换标志,用于指定转换方式和处理方式。常用的标志有以下几个:WC_COMPOSITECHECK:检查是否存在复合字符,如果存在则将其转换为单个字符。WC_DEFAULTCHAR:用默认字符替换无法转换的字符。WC_NO_BEST_FIT_CHARS:禁用最佳匹配模式,如果无法精确匹配则返回错误。
lpWideCharStr:宽字符字符串指针,指向要转换的宽字符编码字符串。
cchWideChar:宽字符字符串的字符数,以字符为单位,表示要转换的宽字符字符串的长度。
lpMultiByteStr:多字节字符串指针,指向存储转换后的多字节编码字符串的缓冲区。
cbMultiByte:多字节字符串缓冲区的大小,以字节为单位表示存储转换后的多字节编码字符串的缓冲区大小。
lpDefaultChar:用于表示未转换字符的默认字符指针,如果设置了 WC_DEFAULTCHAR 标志,则用此字符替换无法转换的字符。
lpUsedDefaultChar:指针用于指示是否使用了默认字符来转换字符,如果使用了则该值为TRUE,否则为FALSE。
MultiByteToWideChar 函数用于将多字节字符转换为宽字符的函数。int MultiByteToWideChar(UINT CodePage, // 源字符集的代码页标识符DWORD dwFlags, // 转换标志LPCSTR lpMultiByteStr, // 指向多字节字符串的指针int cbMultiByte, // 多字节字符串的字节数LPWSTR lpWideCharStr, // 指向宽字符缓冲区的指针int cchWideChar // 宽字符缓冲区的大小
);int n = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);n = MultiByteToWideChar(CP_UTF8, 0, str, -1,p, n);CodePage:源字符集的代码页标识符,可以使用常量或数字作为参数。例如,CP_UTF8 表示 UTF-8 编码,CP_ACP 表示当前 ANSI 代码页,936 表示简体中文 GBK 编码等。
dwFlags:转换标志,可以使用常量或数字作为参数。例如,0 表示使用默认行为,MB_ERR_INVALID_CHARS 表示遇到无效的多字节字符时引发错误等。
lpMultiByteStr:指向多字节字符串的指针。
cbMultiByte:多字节字符串的字节数,不包括结尾的空字符。如果此值为-1,则假定字符串以空结束,并自动计算长度。
lpWideCharStr:指向宽字符缓冲区的指针。
cchWideChar:宽字符缓冲区的大小,以字符为单位。如果此参数为 0,则函数将返回需要的缓冲区大小,不执行转换操作。
注意:MultiByteToWideChar 函数的返回值表示转换后的宽字符数,不包括结尾的空字符。如果转换失败,则返回值为 0,并可以使用 GetLastError 函数获取错误代码。
项目代码
codec.cpp 编码格式转换
#include<windows.h>
bool CheckUtf8(LPCSTR p) //无BOM头的Utf8的编码识别算法
{auto q = p;while (*p){BYTE c = *p;BYTE x = 0x80;int n = 0;while ((c & x) == x)++n, x >>= 1;if (n == 1 || n > 4)return false;++p;while (--n>0){c = *p++;if (c >> 6 != 2) //00000010return false;}}return true;
}
void ConvertBig(LPSTR p) //UTF16大端转换
//将一个以null结尾的字符串中的每两个字符进行交换,即将每两个字符的顺序逆序
{while (*(WORD*)p) //双字节的0结尾结束{*p = *p ^ p[1]; //没有中间变量的反转p[1] = *p ^ p[1];*p = *p ^ p[1];p += 2;}/* 采用了异或运算(^)来实现交换两个字符的位置。具体来说它首先将p所指向的字符与下一个字符进行异或运算,并将结果保存到p所指向的字符中。然后将下一个字符和上一个字符进行异或运算,并将结果保存到下一个字符中。最后再将p所指向的字符和下一个字符进行异或运算,并将结果保存到p所指向的字符中 */}
char* UnicodeToANSI(const wchar_t* str)
{int n = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL); //第一次求长度 带L是Unicode的格式auto p = new char[n + 1];n = WideCharToMultiByte(CP_ACP, 0, str, -1, p, n, NULL, NULL); //第二次填充 p, n 你申请的空间,边界限制p[n] = 0; //结尾return p;
} //算出长度 申请空间 填充长度wchar_t* UTF8ToUnicode(const char* str)
{int n = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);auto p = new wchar_t[n + 1];n = MultiByteToWideChar(CP_UTF8, 0, str, -1,p, n);p[n] = 0;return p;
}
char* UTF8ToANSI(const char* str)
{auto p = UTF8ToUnicode(str);auto q= UnicodeToANSI(p);delete[] p;return q;
}
Notepad.cpp
#define _CRT_SECURE_NO_WARNINGS
#include<windows.h>
#include"resource.h"
#include<stdio.h>
void ConvertBig(LPSTR p);
char* UnicodeToANSI(const wchar_t* str);
char* UTF8ToANSI(const char* str);
bool CheckUtf8(LPCSTR p);
void ParseText(HWND hwndDlg, LPSTR p)
{char* q = nullptr;switch (*(WORD*)p){case 0xFFFE: //BE 大端 大端与小端之间完全颠倒 每个字节之间完全的进行反转 翻转以后就说小端的代码 ConvertBig(p);case 0xFEFF: //LE 小端q = UnicodeToANSI(LPWSTR(p+2));SetDlgItemText(hwndDlg, IDC_TEXT, q);delete[] q;return;case 0xBBEF: //UTF8 BOMif (p[2]==(char)0xBF){q=UTF8ToANSI(p + 3);SetDlgItemText(hwndDlg, IDC_TEXT, q);delete[] q;return;}return;}if (CheckUtf8(p)){q = UTF8ToANSI(p);SetDlgItemText(hwndDlg, IDC_TEXT, q);delete[] q;}elseSetDlgItemText(hwndDlg, IDC_TEXT, p);
}
int GetFileSize(FILE* pf)
{long m = ftell(pf);fseek(pf, 0, SEEK_END);long n = ftell(pf);fseek(pf, m, SEEK_SET);return n;
}
void ReadTextFile(HWND hwndDlg, LPCSTR sFile)
{FILE *pf = fopen(sFile, "rb");if (!pf)return;int nSize = GetFileSize(pf);if (nSize>0){char* p = new char[nSize + 2];auto n= fread(p, 1, nSize, pf);p[n] = 0;p[n + 1] = 0;ParseText(hwndDlg, p);// SetDlgItemText(hwndDlg, IDC_TEXT, p);delete[] p;}fclose(pf);
}void onDropFile(HWND hwndDlg, HDROP hDrop)
{ char s[MAX_PATH];int nCount = DragQueryFile(hDrop, 0, s, sizeof(s)); //取第一个文件名 如果要去最后一个,先求出总数再ncount-1//SetDlgItemText(hwndDlg, IDC_TEXT, s);ReadTextFile(hwndDlg, s);DragFinish(hDrop);
}INT_PTR CALLBACK theProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{switch (uMsg){case WM_DROPFILES:MessageBox(hwndDlg, "拖入文件成功", "提示", 0);onDropFile(hwndDlg, (HDROP)wParam);break;case WM_COMMAND:{switch (LOWORD(wParam)){case IDCANCEL:EndDialog(hwndDlg, 88);break;}}}return 0;
}
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{DialogBox(hInstance, (LPCSTR)IDD_NOTE_DLG, NULL, theProc);return 0;
}
本文链接:https://my.lmcjl.com/post/12752.html
展开阅读全文
4 评论