1. 系统文件对话框(CFileDialog)
CFileDialog类的构造函数如下:
CFileDialog( BOOL bOpenFileDialog,
LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY |
OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL
);
其中处理第一个以外都是默认参数。
第一个参数如果是TRUE就是系统打开对话框,带FALSE就是系统另存为对话框。
这两个对话框属于模态对话框.
系统打开对话框如下:
void CSystemWinDlg::OnBnClickedBntChoice()
{
// TODO: 在此添加控件通知处理程序代码
CFileDialog dlg(TRUE);
dlg.DoModal();
}
系统另存为对话框如下:
void CSystemWinDlg::OnBnClickedBntChoice()
{
// TODO: 在此添加控件通知处理程序代码
CFileDialog dlg(FALSE);
dlg.DoModal();
}
构造函数中:
- 第二个参数代表对话框默认打开的文件.
- 第三个参数代表默认文件名
- 第四个参数dwFlags代表了系统对话框的属性,eg(可以选择多个文件,文件必须存在等等)
- lpszFilter:是过滤器,代表对话框可以过滤掉一些文件。这个参数类型是字符串。
这个字符串格式如下:文本|过滤类型(多个过滤类型用分号隔开)|文本|过滤类型||(||代表结束)
eg:
void CSystemWinDlg::OnBnClickedBntChoice()
{
// TODO: 在此添加控件通知处理程序代码
CFileDialog dlg(FALSE, L"txt", L"Test", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT
, L"文本文件(*.txt)|*.txt|音乐文件(*.mp3、*.wav)|(*.mp3;*.wav)|所有文件(*.*)|*.*||");
dlg.DoModal();
}
成员函数:
- GetFileName:获取选择的文件名字
- GetPathName:获取选择文件的路径
- GetFileExt:获取文件拓展名
- GetFileTitle:获取文件名
- GetStartPosition:返回POSITION类型,返回文件首位置。适合用在系统对话框选择可以多选文件时。
- GetNextPathName:获得POSITION位置的文件名,同时会让POSITION自动+1。(类似迭代器)
void CSystemWinDlg::OnBnClickedBntChoice()
{
// TODO: 在此添加控件通知处理程序代码
CFileDialog dlg(TRUE, L"txt", L"Test", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT| OFN_ALLOWMULTISELECT
, L"文本文件(*.txt)|*.txt|音乐文件(*.mp3、*.wav)|(*.mp3;*.wav)|所有文件(*.*)|*.*||");
dlg.DoModal();
POSITION pos = dlg.GetStartPosition();
while (pos) {
CString str = dlg.GetNextPathName(pos);
MessageBox(str);
}
}
2. 字符编码
-
多字节编码:为了扩展编码可以表示的字符,引入了双字节字符集。如果第一个字节为0x81->0x9F之间 或 0xE0->0xFC之间需要检测下一个字节,拼接起来才能确定这个文字。
-
Unicode编码:在1988年创立。主要分为UTF-8、UTF-16、UTF-32
- UTF-8:有一些字符编码为一个字节,有些编码为二个字节,有些编码为三个字节
- UTF-16:将所有的字符编码为两个字节
- UTF-32:将所有字符编码为四个字节
Unicode编码需要在字符串、字符前加L。代表宽字符类型wchar_t,Window默认按照UTF-16进行编码(Uncode)。每个宽字符两个字节。
在MFC中
CHAR=>char
WCHAR=>wchar_t
TCHAR:如果定义了Unicode编码TCHAR=>wchar_t
如果没有定义Unicode编码这个工程是ANSI工程(多字节编码),TCHAR=>char
通用宏TEXT、_T
-
TEXT在Unicode编码中:
TEXT <=> __TEXT <=> L##quote -
TEXT在ANSI工程中:
TEXT <=> __TEXT <=> quote
所以采用TEXT宏可以自动适配工程使用的编码集
同样_T这个宏功能与TEXT宏作用相同。
C语言对应宽字符函数对应:
ANSI: strlen、strcpy、strcmp、strcat
Unicode:wcslen、wcscpy、wcscmp、wcscat(宽字符)
字符集转化
字符集转化主要使用两个函数:WideCharToMultiByte和MultiByteToWideChar
字符集转化大致分为如下部分,Unicode编码就是UTF-16编码
- ANSI <=> Unicode
- ANSI <=> UTF-8
- Unicode <=> UTF-8
ANSI转化Unicode核心代码:
#pragma once
#include"framework.h"
class ChangeCharSet{
private:
wchar_t* w_charbuff;
char* charbuff;
public:
ChangeCharSet()
:w_charbuff(nullptr), charbuff(nullptr)
{}
~ChangeCharSet() {
if (w_charbuff != nullptr) {
delete[] w_charbuff;
w_charbuff = nullptr;
}
if (charbuff != nullptr) {
delete[] charbuff;
charbuff = nullptr;
}
}
//ANSI=>Unicode
wchar_t* AnsiToUnicode(const char* str) {
//-1会自动推导字符串长度
if (w_charbuff != nullptr) {
delete[] w_charbuff;
w_charbuff = nullptr;
}
DWORD dsize = ::MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
w_charbuff = new wchar_t[dsize];
::MultiByteToWideChar(CP_ACP, 0, str, -1, w_charbuff, dsize);
return w_charbuff;
}
//Uncode=>ANSI
char* UnicodeToAnsi(const wchar_t* w_str) {
if (charbuff != nullptr) {
delete[] charbuff;
charbuff = nullptr;
}
DWORD dsize = ::WideCharToMultiByte(CP_ACP, 0, w_str, -1, NULL, 0, NULL, NULL);
charbuff = new char[dsize];
::WideCharToMultiByte(CP_ACP, 0, w_str, -1, charbuff, dsize, NULL, NULL);
return charbuff;
}
};
Unicode转化为UTF-8:
class ChangeCharSet{
private:
wchar_t* w_charbuff;
char* charbuff;
public:
ChangeCharSet()
:w_charbuff(nullptr), charbuff(nullptr)
{}
~ChangeCharSet() {
if (w_charbuff != nullptr) {
delete[] w_charbuff;
w_charbuff = nullptr;
}
if (charbuff != nullptr) {
delete[] charbuff;
charbuff = nullptr;
}
}
//Unicode=>UTF-8
char* UnicodeToUTF_8(const wchar_t* w_str) {
if (charbuff != nullptr) {
delete[] charbuff;
charbuff = nullptr;
}
DWORD dsize = ::WideCharToMultiByte(CP_UTF8, 0, w_charbuff, -1, NULL, 0, NULL, NULL);
charbuff = new char[dsize];
::WideCharToMultiByte(CP_UTF8, 0, w_charbuff, -1, charbuff, dsize, NULL, NULL);
return charbuff;
}
//UTF-8=>Unicode
wchar_t* UTF_8ToUnicode(const char* str) {
if (w_charbuff != nullptr) {
delete[] w_charbuff;
w_charbuff = nullptr;
}
DWORD dsize = ::MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
w_charbuff = new wchar_t[dsize];
::MultiByteToWideChar(CP_UTF8, 0, str, -1, w_charbuff, dsize);
return w_charbuff;
}
}
ANSI转化为UTF-8:
ANSI不能直接转化为UTF-8、ANSI需要先转化为Unicode,再由Unicode转化为UTF-8。
可以直接复用上面的成员函数。
class ChangeCharSet{
private:
wchar_t* w_charbuff;
char* charbuff;
public:
ChangeCharSet()
:w_charbuff(nullptr), charbuff(nullptr)
{}
~ChangeCharSet() {
if (w_charbuff != nullptr) {
delete[] w_charbuff;
w_charbuff = nullptr;
}
if (charbuff != nullptr) {
delete[] charbuff;
charbuff = nullptr;
}
}
//ANSI=>Unicode
wchar_t* AnsiToUnicode(const char* str) {
//-1会自动推导字符串长度
if (w_charbuff != nullptr) {
delete[] w_charbuff;
w_charbuff = nullptr;
}
DWORD dsize = ::MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
w_charbuff = new wchar_t[dsize];
::MultiByteToWideChar(CP_ACP, 0, str, -1, w_charbuff, dsize);
return w_charbuff;
}
//Uncode=>ANSI
char* UnicodeToAnsi(const wchar_t* w_str) {
if (charbuff != nullptr) {
delete[] charbuff;
charbuff = nullptr;
}
DWORD dsize = ::WideCharToMultiByte(CP_ACP, 0, w_str, -1, NULL, 0, NULL, NULL);
charbuff = new char[dsize];
::WideCharToMultiByte(CP_ACP, 0, w_str, -1, charbuff, dsize, NULL, NULL);
return charbuff;
}
//Unicode=>UTF-8
char* UnicodeToUTF_8(const wchar_t* w_str) {
if (charbuff != nullptr) {
delete[] charbuff;
charbuff = nullptr;
}
DWORD dsize = ::WideCharToMultiByte(CP_UTF8, 0, w_charbuff, -1, NULL, 0, NULL, NULL);
charbuff = new char[dsize];
::WideCharToMultiByte(CP_UTF8, 0, w_charbuff, -1, charbuff, dsize, NULL, NULL);
return charbuff;
}
//UTF-8=>Unicode
wchar_t* UTF_8ToUnicode(const char* str) {
if (w_charbuff != nullptr) {
delete[] w_charbuff;
w_charbuff = nullptr;
}
DWORD dsize = ::MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
w_charbuff = new wchar_t[dsize];
::MultiByteToWideChar(CP_UTF8, 0, str, -1, w_charbuff, dsize);
return w_charbuff;
}
//ANSI=>UTF-8
char* AnsiToUTF_8(const char* str) {
//ANSI=>Uncode
w_charbuff = AnsiToUnicode(str);
//Unicode=>UTF-8
charbuff = UnicodeToUTF_8(w_charbuff);
return charbuff;
}
//UTF-8=>ANSI
char* UTF_8ToAnsi(const char* str) {
//UTF-8->Unicode
w_charbuff = UTF_8ToUnicode(str);
//Unicode->ANSI
charbuff = UnicodeToAnsi(w_charbuff);
return charbuff;
}
};
3. DEMO Windows记事本
需要注意:
记事本内的文字编码是Unicode,保存记事本内容后,保存文件编码为ANSI。
平台:windows 10
开发工具:Visual studio 2022
核心功能:保存txt文件,读取txt文件。更改记事本内容的颜色,大小。替换查找记事本的文本
核心功能代码:
// NotePadeDemoDlg.cpp: 实现文件
//
#include "framework.h"
#include "NotePadeDemo.h"
#include "NotePadeDemoDlg.h"
#include "afxdialogex.h"
#include"ChangeCharSet.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
//向系统注册消息
static UINT NEAR WM_FINDREPLACE = ::RegisterWindowMessage(FINDMSGSTRING);
// CNotePadeDemoDlg 对话框
CNotePadeDemoDlg::CNotePadeDemoDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_NOTEPADEDEMO_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_choice = RGB(0, 0, 0);//默认选择颜色是黑色
m_flag = FALSE;
nStartIndex = 0;
nEndIndex = 0;
}
void CNotePadeDemoDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CNotePadeDemoDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDOK, &CNotePadeDemoDlg::OnBnClickedOk)
ON_WM_SIZE()
ON_COMMAND(ID_OPEN, &CNotePadeDemoDlg::OnMenuOpen)
ON_COMMAND(ID_SAVE_AS, &CNotePadeDemoDlg::OnMenuSaveAs)
ON_COMMAND(ID_CHANGE_COLOUR, &CNotePadeDemoDlg::OnChangeColour)
ON_WM_CTLCOLOR()
ON_COMMAND(ID_CHANGE_TITLE, &CNotePadeDemoDlg::OnChangeTitle)
ON_COMMAND(ID_EXIT, &CNotePadeDemoDlg::OnExit)
ON_COMMAND(ID_FIND, &CNotePadeDemoDlg::OnFind)
ON_COMMAND(ID_REPLACE, &CNotePadeDemoDlg::OnReplace)
ON_REGISTERED_MESSAGE(WM_FINDREPLACE, &CNotePadeDemoDlg::OnFindReplace)
END_MESSAGE_MAP()
// CNotePadeDemoDlg 消息处理程序
BOOL CNotePadeDemoDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
//
//设置字体
HFONT hfont = CreateFont(32, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET
, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH
, L"黑体");
//获取控件
HWND hwnd = ::GetDlgItem(this->m_hWnd, IDC_EDIT);
//设置字体大小
::SendMessage(hwnd, WM_SETFONT, WPARAM(hfont), 0);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CNotePadeDemoDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CNotePadeDemoDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CNotePadeDemoDlg::OnBnClickedOk()
{
// TODO: 在此添加控件通知处理程序代码
//CDialogEx::OnOK();
}
void CNotePadeDemoDlg::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);
// TODO: 在此处添加消息处理程序代码
//获取控件
CWnd* wnd = GetDlgItem(IDC_EDIT);
if (wnd != NULL) {
wnd->MoveWindow(0, 0, cx, cy);
}
}
//打开
void CNotePadeDemoDlg::OnMenuOpen()
{
// TODO: 在此添加命令处理程序代码
CFileDialog dlg(TRUE);
if (IDCANCEL == dlg.DoModal()) {
return;
}
CString strpath = dlg.GetPathName();
//打开文件,以读取的方式打开文件,读取文件编码为ANSI
CFile file;
if (FALSE == file.Open(strpath, CFile::modeRead)) {
MessageBox(TEXT("打开文件失败"), TEXT("警告"), MB_OK | MB_ICONWARNING);
return;
}
char buff[255] = { 0 };
CStringA msg;
//一次读取一个字符
while (file.Read(buff, 1)) {
msg += buff;
}
ChangeCharSet charset;
wchar_t* title_msg = charset.AnsiToUnicode(msg);
//设置窗口
SetDlgItemText(IDC_EDIT, title_msg);
//设置窗口标题
CString title;
title.Format(TEXT("NodePadeDemo ——%s[%d字节]"), dlg.GetFileName(), file.GetLength());
SetWindowText(title);
file.Close();
}
//另存为
void CNotePadeDemoDlg::OnMenuSaveAs()
{
// TODO: 在此添加命令处理程序代码
CFileDialog dlg(FALSE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, NULL);
if (IDCANCEL == dlg.DoModal()) {
return;
}
CString path = dlg.GetPathName();
CFile file;
if (IDCANCEL == file.Open(path, CFile::modeCreate | CFile::modeWrite)) {
MessageBox(TEXT("保存文件失败"), TEXT("警告"), MB_OK | MB_ICONWARNING);
return;
}
//读取控件上的所有文本
CString msg;
GetDlgItemText(IDC_EDIT, msg);
ChangeCharSet charset;
char* title_msg = charset.UnicodeToAnsi(msg);
//宽字符写入
file.Write(title_msg, strlen(title_msg));
file.Close();
//退出程序
CDialogEx::OnOK();
}
void CNotePadeDemoDlg::OnChangeColour()
{
// TODO: 在此添加命令处理程序代码
//弹出颜色系统框
CColorDialog color;
if (IDCANCEL == color.DoModal()) {
return;
}
//获取选择的颜色
this->m_choice = color.GetColor();
//更改控件的颜色,通过发送WM_CTLCOLOR消息来改变颜色
}
HBRUSH CNotePadeDemoDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
switch (pWnd->GetDlgCtrlID()){
case IDC_EDIT:
{
pDC->SetTextColor(m_choice);
break;
}
default:
break;
}
return hbr;
}
void CNotePadeDemoDlg::OnChangeTitle()
{
// TODO: 在此添加命令处理程序代码
CFontDialog dlg;
if (IDCANCEL == dlg.DoModal()) {
return;
}
//创建选择的字体
CFont font;
font.CreatePointFont(dlg.GetSize(), dlg.GetFaceName());
//设置字体
GetDlgItem(IDC_EDIT)->SetFont(&font);
}
void CNotePadeDemoDlg::OnExit()
{
//退出程序
CDialogEx::OnOK();
}
//查找对话框
void CNotePadeDemoDlg::OnFind()
{
//非模态对话框
CFindReplaceDialog* dlg = new CFindReplaceDialog;
//TRUE代表是查找框,FALSE代表是替换框
dlg->Create(TRUE, NULL, NULL);
dlg->ShowWindow(SW_SHOW);
//不需要delete,CFindReplaceDialog类内部处理了。不会导致内存泄漏
//此外,还需要向系统注册一个消息
this->m_flag = TRUE;
}
void CNotePadeDemoDlg::OnReplace()
{
//非模态对话框
CFindReplaceDialog* dlg = new CFindReplaceDialog;
//TRUE代表是查找框,FALSE代表是替换框
dlg->Create(FALSE, NULL, NULL);
dlg->ShowWindow(SW_SHOW);
this->m_flag = FALSE;
}
//处理查找和替换操作
LRESULT CNotePadeDemoDlg::OnFindReplace(WPARAM wparam, LPARAM lparam) {
CFindReplaceDialog* lpfindReplace = CFindReplaceDialog::GetNotifier(lparam);
if (lpfindReplace == nullptr) {
return 0;
}
//如果要退出窗口
if (lpfindReplace->IsTerminating()) {
return 0;
}
//获取需要查找字符串
CString str_find = lpfindReplace->GetFindString();
//获取要替换的字符串
CString str_replaces = lpfindReplace->GetReplaceString();
BOOL SearchDownFlag = lpfindReplace->SearchDown();//判断是否向下查找,TRUE代表向下查找
BOOL MatchCass = lpfindReplace->MatchCase();//判断是否区分大小
//获取记事本内的文本
CString strContent;
GetDlgItemText(IDC_EDIT, strContent);
//获取记事本文本控件
CEdit* lpEdit = (CEdit*)GetDlgItem(IDC_EDIT);
//查找
if (this->m_flag == TRUE) {
//获取光标起始,结束位置
lpEdit->GetSel(this->nStartIndex, this->nEndIndex);
if (SearchDownFlag) {
//向下查找
//从结束位置开始向下找
this->nStartIndex = strContent.Find(str_find, this->nEndIndex);
if (this->nStartIndex != -1) {
//找到了,标记这个字符串
lpEdit->SetSel(this->nStartIndex, this->nStartIndex + str_find.GetLength());
lpEdit->SetFocus();
}
else {
从头开始找
//this->nStartIndex = strContent.Find(str_find, 0);
//文中没有这个字符串
CString msg;
msg.Format(L"在下文中没有找到%s这个字符串", str_find.GetString());
MessageBox(msg, L"警告", MB_OK|MB_ICONWARNING);
return 0;
//else {
// //找到了,标记这个字符串
// MessageBox(L"在上文中找到这个字符串", L"提示");
// lpEdit->SetSel(this->nStartIndex, this->nStartIndex + str_find.GetLength());
// lpEdit->SetFocus();
//}
}
}
else {
//向上查找,反转字符串
strContent = strContent.MakeReverse();
CString str_refind = str_find.MakeReverse();
this->nStartIndex = strContent.Find(str_refind, strContent.GetLength() - this->nStartIndex);
if (this->nStartIndex != -1) {
//设置光标
this->nEndIndex = strContent.GetLength() - this->nStartIndex;
lpEdit->SetSel(nEndIndex - str_find.GetLength(), nEndIndex);
lpEdit->SetFocus();
}
else {
CString msg;
msg.Format(L"在上文中没有找到%s这个字符串", str_find.GetString());
MessageBox(msg, L"警告", MB_OK | MB_ICONWARNING);
return 0;
}
}
}
//替换
else {
if (lpfindReplace->ReplaceCurrent()) {
this->nStartIndex = strContent.Find(str_find);
if (this->nStartIndex == -1) {
MessageBox(L"查不到这个字符串", L"警告", MB_OK);
return 0;
}
else {
this->nEndIndex = this->nStartIndex + str_find.GetLength();
//字符串剪切
CString left = strContent.Left(nStartIndex);
CString right = strContent.Right(strContent.GetLength() - nEndIndex);
strContent = left + str_replaces + right;
lpEdit->SetWindowText(strContent);
}
}
//替换全部
if (lpfindReplace->ReplaceAll()) {
int ntimes = strContent.Replace(str_find, str_replaces);
lpEdit->SetWindowText(strContent);
CString msg;
msg.Format(L"替换成功,替换了%d处", ntimes);
MessageBox(msg, L"提示", MB_OK);
}
}
return 0;
}
运行效果:
完整代码位置:
Github