【示例】VC打开任意编码的文本文件,以及一个编码转换类
acmilan2015/07/12软件综合 IP:四川
【示例程序:VC打开任意编码的文本文件

用Visual C++ 2010写的示例程序:

Capturedshdfhdfh.png

常用代码页
437 英语
932 日语
936 简体中文(GB2312)
949 朝鲜语
950 繁体中文(Big-5)
54936 简体中文(GB18030)
65001 UTF-8

下载:
attachment icon MfcMyGb18030.rar 687.84KB RAR 22次下载
<code class="lang-cpp">#include "MyStringCP.h"
     
void CMfcMyGb18030Dlg::OnBnClickedSaveFile()
{
    UpdateData(TRUE);
    try {
             
        CFileDialog dlg(FALSE, _T(".txt"), _T("result.txt"));
        if (dlg.DoModal() != IDOK)
            return;
        CFile outfile(dlg.GetPathName(), CFile::modeWrite|CFile::modeCreate);
        CPString strcp(m_dstcp, m_text, false);
        outfile.Write((LPCSTR)strcp, strcp.GetLength());
        outfile.Close();
    } catch (CException *e) {
        MessageBox(_T("文件保存错误!"));
    }
}
     
     
void CMfcMyGb18030Dlg::OnBnClickedOpenFile()
{
    UpdateData(TRUE);
    try {
        CFileDialog dlg(TRUE, _T(".txt"), _T("result.txt"));
        if (dlg.DoModal() != IDOK)
            return;
        CFile infile(dlg.GetPathName(), CFile::modeRead);
        CPString instr(m_srccp);
        ULONGLONG inlength = infile.GetLength();
        LPSTR inbuf = instr.GetBufferSetLength(inlength);
        infile.Read(inbuf, inlength);
        instr.ReleaseBuffer(inlength);
        m_text = instr.Get();
        infile.Close();
        UpdateData(FALSE);
    } catch (CException *e) {
        MessageBox(_T("文件打开错误!"));
    }
}</code>

【编码转换类】

这是一个字符串实现类,包装了CStringA和MultiByteToWideChar、WideCharToMultiByte函数,可以实现方便的编码互转,便于与各种蛋疼的编码问题打交道。
使用这个类时不建议编译ANSI(MBCS)应用程序,因为ANSI版WinAPI实际上仅支持DBCS双字节字符集(即仅支持GBK、Big5等2个字节的字符集,不支持GB18030、UTF-8等超过2个字节的字符集),对编码转换的兼容性会变差,并且也有额外的开销。

构造函数
CPString strloc(代码页, 字符串, 是否原始字符串);
CPString strloc1(); // 初始化空的UTF-8字符串
CPString strloc2(54936); // 初始化空的GB18030字符串
CPString strloc3(54936, _T("我是要转码的字符串\r\n")); // 初始化GB18030字符串,进行转码
CPString strloc4(54936, "我是要转码的字符串\r\n", true); // 初始化GB18030字符串,不进行转码
CPString strloc5(65001, strloc5); // 将strloc5转码成UTF-8
CPString strloc6(65001, strloc5, true); // 将strloc5的内容原封不动复制,并设定代码页为UTF-8

与使用UTF-16、ACP-ANSI的CString、其它CPString互转
CString str = XXXXXXXXXt(); // 转码到CString
XXXXXXXXXom(str); // 从CString转码
XXXXXXXXXXom(strloc); // 互相转码

复制、转换和赋值
CPString strloc2 = strloc; // 不转码,只是整体复制(包括代码页、内容等)
CStringA strraw = strloc; // 同上,不转码(只复制内容)

<code class="lang-cpp">// MyStringCP.h : 任意代码页字符串
                     
#pragma once
                     
#include <windows.h>
                     
#ifndef _AFX
#include <atlstr.h>
#endif
                     
// 任意MBCS字符集(代码页)字符串类,可转码
// 支持ANSI、GBK、Big-5、GB18030、UTF-8等C兼容字符集
// 不支持UTF-16和UTF-32
class CPString : public CStringA {
public:
    UINT m_codepage; // 代码页
    UINT m_dwflags; // 可选标志,传递给WinAPI的可选参数,建议保持NULL
    LPCSTR m_lpdefaultchar; // 缺省字符,传递给WinAPI的可选参数,建议保持NULL
                     
    // 因为CStringA<->LPCSTR和CStringW<->LPCWSTR可以隐式转换
    // 故参数中均以LPCSTR和LPCWSTR替换CStringA和CStringW
                         
    // 第一个参数ucp是代码页,默认为UTF-8(65001);
    // 第二个参数str是字符串,默认为空字符串;
    // 第三个参数israw指示是否是原始字符串,对于宽字符的情况,该参数不起作用;
    // 后两个参数dwflags和lpdefchar是传递给WinAPI的可选参数,建议保持默认。
    // 此类型必需要有代码页,不能由字符串直接转换而来,因此没有转换构造函数。
    CPString(UINT ucp = CP_UTF8, LPCSTR str = NULL, bool israw = false,
        UINT dwflags = 0, LPCSTR lpdefchar = NULL) {
            m_codepage = ucp;
            m_dwflags = dwflags;
            m_lpdefaultchar = lpdefchar;
            // 注意次序
            if (str != NULL) {
                if (israw)
                    CStringA::operator=(str);
                else
                    FromA(str);
            }
    }
    CPString(UINT ucp, LPCWSTR str, bool reserved1 = false,
        UINT dwflags = 0, LPCSTR lpdefchar = NULL) {
            m_codepage = ucp;
            m_dwflags = dwflags;
            m_lpdefaultchar = lpdefchar;
            // 注意次序
            FromW(str);
    }
    CPString(UINT ucp, const CPString &str, bool israw = false,
        UINT dwflags = 0, LPCSTR lpdefchar = NULL) {
            m_codepage = ucp;
            m_dwflags = dwflags;
            m_lpdefaultchar = lpdefchar;
            // 注意次序
            if (israw)
                CStringA::operator=(str);
            else
                From(str);
    }
                     
    bool From(LPCWSTR tstr) { return FromW(tstr); } // 转码到此对象所设定的代码页
    bool From(LPCSTR tstr) { return FromA(tstr); } // 转码到此对象所设定的代码页
                     
#ifdef UNICODE
    CStringW Get() const { return GetW(); } // 从此对象所设定的代码页转码
#else
    CStringA Get() const { return GetA(); } // 从此对象所设定的代码页转码
#endif
                     
    // 不同代码页互转
    bool From(const CPString &cpstr) {
        if (m_codepage == cpstr.m_codepage) { // 如果代码页相同则不进行转码
            CStringA::operator=(cpstr);
            return true;
        } else {
            return FromW(cpstr.GetW()); // 这样转换开销较大!
        }
    }
                     
    // =====================================================================
    // 字符集限定的函数
                     
    // 写Unicode字符串,若两个皆为0,则返回需要的wchar_t缓冲区大小
    int WriteW(LPWSTR outbuf, int bufsize) const {
        return MultiByteToWideChar(m_codepage, m_dwflags, *this, -1, outbuf, bufsize);
    }
                     
    // 从代码页转码到Unicode
    CStringW GetW() const {
        CStringW wstr;
        int n = WriteW(NULL, 0);
        WriteW(wstr.GetBufferSetLength(n), n);
        wstr.ReleaseBuffer();
        return wstr;
    }
    // 从Unicode转码到代码页
    bool FromW(LPCWSTR wstr) {
        int n = WideCharToMultiByte(m_codepage, m_dwflags, wstr, -1, NULL, 0, m_lpdefaultchar, NULL);
        LPSTR buf = this->GetBufferSetLength(n);
        if (!WideCharToMultiByte(m_codepage, m_dwflags, wstr, -1, buf, n, m_lpdefaultchar, NULL)) {
            buf[0] = '\0'; // 转换不成功则置空
            this->ReleaseBuffer();
            return false;
        }
        this->ReleaseBuffer();
        return true;
    }
                     
    // 从代码页转码到ACP-ANSI(当前代码页),开销较大!
    CStringA GetA() const {
        if (GetACP() == m_codepage) {
            return *this;
        } else
            return (CStringA)GetW(); // ANSI中这样转换开销较大!
    }
    // 从ACP-ANSI(当前代码页)转码到代码页,开销较大!
    bool FromA(LPCSTR thestr) {
        if (GetACP() == m_codepage) {
            CStringA::operator=(thestr);
            return TRUE;
        } else
            return FromW((CStringW)thestr); // ANSI中这样转换开销较大!
    }
};</-></-></atlstr.h></windows.h></code>

[修改于 8年11个月前 - 2015/07/15 01:19:52]

来自:计算机科学 / 软件综合
4
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
acmilan 作者
8年11个月前 修改于 8年11个月前 IP:四川
778422
对于需要字符串处理的WinAPI,系统大多会同时提供ANSI和Unicode版本的函数(有少数例外),分别以A和W结尾(如MessageBoxA和MessageBoxW),在windows.h及其包含文件中,按照是否定义UNICODE宏,将MessageBox转义为MessageBoxA或MessageBoxW。

由于目前基于NT内核的Windows内部是以Unicode作为系统编码的,因此ANSI版本的函数只是将字符串转码,然后调用Unicode版本的WinAPI,调用完毕之后,返回的Unicode字符串再转成ANSI编码。

ANSI版本的WinAPI,以gdi32.dll中的GetGlyphOutlineA为例,在调用对应Unicode版本的函数前,要调用三个函数将ANSI字符串转换为Unicode字符串:GdiGetCodePage、IsDBCSLeadByteEx和MultiByteToWideChar。其中IsDBCSLeadByteEx判断一个字节是否为DBCS(双字节字符集)的前导字节,然而54936(GB18030)或65001(UTF-8)代码页并不是一个DBCS,没有定义LeadByte的范围,因此显然不能用作默认的ANSI代码页。

比如:
<code class="lang-cpp">#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
   
int _tmain(int argc, _TCHAR* argv[])
{
    printf("是否前导字节(936):%d\n", IsDBCSLeadByteEx(936, '\xb1'));
    printf("是否前导字节(950):%d\n", IsDBCSLeadByteEx(950, '\xb1'));
    printf("是否前导字节(54936):%d\n", IsDBCSLeadByteEx(54936, '\xb1'));
    printf("是否前导字节(65001):%d\n", IsDBCSLeadByteEx(65001, '\xb1'));
    return 0;
}</stdio.h></windows.h></code>
输出:
前导字节.png

但是WinAPI是可以用作控制台代码页的,方法很简单,在控制台中输入chcp 54936和chcp 65001,并把字体改成Lucida Console或Consolas即可,不能用点阵字体,否则遇到中文会报错。

参考资料:XXXXXXXXXXXXXXXXXXXXXX/XXXXXXXXml
-
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
acmilan作者
8年11个月前 IP:四川
778752
对于处理中文的程序,最好使用GB18030编码保存文件。
GB18030保存中文一般只需要2个字节,UTF-8保存中文需要3个字节,使用GB18030保存中文可以节省不少空间。

对于处理国际字符的程序,最好使用UTF-8编码保存文件。
UTF-16容错性比UTF-8要差,不适合保存文件,UTF-16偏移一个字节就全乱码了,UTF-8偏移一个字节最多产生3个无效字符而已。
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
acmilan作者
8年11个月前 IP:四川
778779
Windows下的转码函数MultiByteToWideChar和WideCharToMultiByte,我们一般只用第1356四个参数,其它参数按照推荐值填写即可。其中第2个参数dwFlags、第7个参数lpDefaultChar,第8个lpUsedDefaultChar最好保持全0,因为有的代码页并不支持这三个参数,填写了会转码失败。第4个参数是输入字符串的大小,一般填-1,让系统判断它的大小。

MultiByteToWideChar的用法:
MultiByteToWideChar(代码页, 0, 输入字符串, -1, NULL, 0) // 返回所需缓冲区总大小(wchar_t的数量,包括末尾0)
MultiByteToWideChar(代码页, 0, 输入字符串, -1, 缓冲区, 缓冲区总大小) // 将char*转换为wchar_t*,若失败返回0

WideCharToMultiByte用法:
WideCharToMultiByte(代码页,  0, 输入字符串, -1, NULL, 0, NULL, NULL) // 返回所需缓冲区总大小(char的数量,包括末尾0)
WideCharToMultiByte(代码页,  0, 输入字符串, -1, 缓冲区, 缓冲区总大小, NULL, NULL) // 将wchar_t*转换为char*,若失败返回0

一个轻量级转码示例程序:
<code class="lang-cpp">#include <windows.h>
#include <stdio.h>
#include <locale.h>
            
int from_codepage(UINT cp, LPCSTR str, LPWSTR wbuf, int num)
{
    return MultiByteToWideChar(cp, 0, str, -1, wbuf, num);
}
            
int to_codepage(UINT cp, LPCWSTR wstr, LPSTR buf, int num)
{
    return WideCharToMultiByte(cp, 0, wstr, -1, buf, num, NULL, NULL);
}
            
int main(int argc, char* argv[])
{
    setlocale(LC_ALL, ""); // 初始化C运行时使用本地编码
    wchar_t wstr[] = L"我是宽字符串";
    char str[] = "我是MBCS字符串";
            
    UINT codepage = GetACP(); // 获取程序当前的ANSI字符集
    if (codepage == 936 && IsValidCodePage(54936)) // 如果为GB2312且GB18030有效
        codepage = 54936; // 替换为GB18030
    printf("使用代码页:%d\n", codepage);
            
    // 转码成宽字符串
    int wcount = from_codepage(codepage, str, NULL, 0); // 获取所需缓冲区大小(包括结束0)
    wchar_t *wbuf = new wchar_t[wcount];
    from_codepage(codepage, str, wbuf, wcount); // 正式转码
    printf("%ls\n", wbuf);
            
    // 转码成MBCS字符串
    int count = to_codepage(codepage, wstr, NULL, 0); // 获取所需缓冲区大小(包括结束0)
    char *buf = new char[count];
    to_codepage(codepage, wstr, buf, count); // 正式转码
    printf("%s\n", buf);
            
    delete[] buf;
    delete[] wbuf;
    return 0;
}</locale.h></stdio.h></windows.h></code>
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
上级专业
同级专业
acmilan
进士 学者 笔友
文章
461
回复
2934
学术分
4
2009/05/30注册,5年3个月前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:邮箱
IP归属地:未同步
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}