C++11新特性转换GB2312(ANSI)、UTF8、Unicode编码文件

C++11新特性转换GB2312(ANSI)、UTF8、Unicode编码文件

文本的编码方式有很多,参见我另一篇编码简介,文本编码众多,国内用的最多的也就是GB2312或GBK、UTF8(网页居多)、Unicode等。

常用文件编码

目前,大多程序采用Unicode版本,以后也会更为普及,对于本地文件中,大多为ANSI编码的文件,Windows新建个文本,记事本输入几个英文和汉字,保存。这就是默认ANSI编码的。只不过在不同地区和版本的操作系统中,所采用的扩展编码方式不同。关于有些软件,sublime text3,无bom UTF8推崇狂,ANSI的文件,打开中文必定乱码一会,然后会推荐你用utf8(100%),就是无bom,为什么会这样?,因为ANSI和UFT8无bom的文件头没有标志。不能一定判断。

在多数西方国家,不用想,就是默认的ANSI编码,在简体中文Windows操作系统中,ANSI 编码代表 GBK 编码;在繁体中文Windows操作系统中,ANSI编码代表Big5;在日文Windows操作系统中,ANSI 编码代表 Shift_JIS 编码。

当你的程序读取读取这些文本,并保持在char 数组里,并没有什么不妥,需要得到wchar宽字节的,常用的MultiByteToWideChar转换即可。
但是还有许多非ANSI的文件,比如网页保持的文件,Java的源码文件等等。有utf8和Unicode的编码,如果你还直接读取文本,肯定会出现中文乱码(utf8)或者只能读到一个英文字符(Unicode)等。

这些编码文件的特征

怎么去读,先了解这些文件的存储特征,才能按格式读取和转换。

ANSI的特征

ANSI不用多说,ASCII在内的1字节字符128个,即char型的正数,汉字2字节,第一个字节是0X80以上,即char型负数第一字节,文件开头没有标志,直接是内容。直接读取,计算机会结合本地的编码(如GBK进行显示)。

Unicode特征

Unicode开始启用2个字节表示。衍生出USC-2LE 、USC-2BE、UTF8等等。

1、USC2-LE和USC-2BE

这个就是固定的2字节表示字符,包括英文字符也是2字节。开头有特征:
以0xFFEF(小端)和0xEFFF(大端)开头为标志

2、UTF8

utf8是变长的编码,英文字符还有1字节,汉字和其他各国字符用2字节或者3字节。
UTF-8编码的分为带BOM和不带BOM的,BOM(Byte Order Mark)就是文件开头的标志了。
带BOM的UTF-8文件是:开头三字节,EE BB EF
不带BOM的UTF-8,开头为特征,直接是内容,造成和ANSI的一样。

读取各种编码文件并转化成whar型字符串

  1. ANSI: 直接内容
  • Unicode:2字节:FFEF(小端)、EFFF(大端)开头
  • UTF-8(BOM) :3字节:EE BB EF 开头
  • UTF-8(100%): 直接内容

三种编码比较

我写了一个类来进行自动识别和转换,采用了C++11的新特性进行转换。扔掉MultiByteToWideChar吧,拥抱新变化。

//.h文件#include <codecvt>/*  解码不同编码的文本文件类*/typedef enum tagTextCodeType

{

    TextUnkonw = -1,

    TextANSI = 0,

    TextUTF8,

    TextUNICODE,

    TextUNICODE_BIG

}TextCodeType;

class CDecodeTextFile

{public:

    CDecodeTextFile();

    ~CDecodeTextFile();//interface

    wstring DecodeFileToWstring(const wchar_t *filePath);

    string DecodeFileToString(const wchar_t *filePath);

private:

    TextCodeType GetFileEncodeType(const wchar_t *filePath);

 

    wstring ReadString(TextCodeType type);

    wstring ReadAnsiString();

    wstring ReadUtf8String();

    wstring ReadUnicodeString();

    wstring ReadUnicodeBigString();

private:

    FILE  *m_fp;

};

实现

//.cpp文件

CDecodeTextFile::CDecodeTextFile():

m_fp(NULL)

{

 

}

 

CDecodeTextFile::~CDecodeTextFile()

{

    if (m_fp)

        fclose(m_fp);

}

 

 

wstring CDecodeTextFile::DecodeFileToWstring(const wchar_t *filePath)

{

    TextCodeType type = GetFileEncodeType(filePath);

    return ReadString(type);

}

string CDecodeTextFile::DecodeFileToString(const wchar_t *filePath)

{

    string Dst = "";

    return Dst;

}

 

TextCodeType CDecodeTextFile::GetFileEncodeType(const wchar_t *filePath)

{

    unsigned char headBuf[3] = {0};

    TextCodeType type = TextUnkonw;

    

    m_fp = _wfopen(filePath, L"r");

    if (!m_fp) return type;

 

    fseek(m_fp, 0, SEEK_SET);

    fread(headBuf, 3, 1, m_fp);

 

    if (headBuf[0] == 0xEF && headBuf[1] == 0xBB && headBuf[2] == 0xBF)     //utf8-bom 文件开头:FF BB BF,不带bom稍后解决

        type = TextUTF8;

    else if (headBuf[0] == 0xFF && headBuf[1] == 0xFE)      //小端Unicode  文件开头:FF FE  intel x86架构自身是小端存储,可直接读取

        type = TextUNICODE;

        else if (headBuf[0] == 0xFE && headBuf[1] == 0xFF)  //大端Unicode  文件开头:FE FF

            type = TextUNICODE_BIG;

            else

                type = TextANSI;    //ansi或者unf8 无bom

    

    return type;

}

 

 

wstring CDecodeTextFile::ReadString(TextCodeType type)

{

    if (!m_fp) return NULL;

    switch (type)

    {

    case TextANSI:

        return ReadAnsiString();

        break;

    case TextUTF8:      //无bom

        return ReadUtf8String();

        break;

    case TextUNICODE:

        return ReadUnicodeString();

        break;

    case TextUNICODE_BIG:

        return ReadUnicodeBigString();

        break;

    }

 

 

    return NULL;

}

 

wstring CDecodeTextFile::ReadAnsiString()

{

    const char* GBK_LOCALE_NAME = ".936"; //GBK在windows下的locale name

 

    wstring_convert<codecvt_byname<wchar_t, char, mbstate_t>> Conver_GBK(new codecvt_byname<wchar_t, char, mbstate_t>(GBK_LOCALE_NAME));    //GBK - whar

 

    fseek(m_fp, 0, SEEK_END);

    int fileSize = ftell(m_fp);

    char *buf = new char[fileSize+1];

    memset(buf, 0, fileSize+1);

 

    fseek(m_fp, 0, SEEK_SET);

    fread(buf, sizeof(char), fileSize, m_fp);

    

    wstring wDst = Conver_GBK.from_bytes(buf);      //GBK转Unicode

 

    delete buf;

    return wDst;

}

wstring CDecodeTextFile::ReadUtf8String()

{

    std::wstring_convert<std::codecvt_utf8<wchar_t>> Conver_UTF8;   //utf8-wchar

 

    char  headBuf[3] = { 0 };

 

    fseek(m_fp, 0, SEEK_END);

    int fileSize = ftell(m_fp);

 

    fseek(m_fp, 0, SEEK_SET);

    fread(headBuf, sizeof(char), 3, m_fp);

    if (headBuf[0] == 0xEF && headBuf[1] == 0xBB && headBuf[2] == 0xBF)     //带 Bom

        fseek(m_fp, 3, SEEK_SET);

    else

        fseek(m_fp, 0, SEEK_SET);

 

    char *buf = new char[fileSize];

    memset(buf, 0, fileSize);

 

    fseek(m_fp, 0, SEEK_SET);

    fread(buf, sizeof(char), fileSize, m_fp);

 

    wstring wDst = Conver_UTF8.from_bytes(buf);     //GBK转Unicode

 

    delete buf;

    return wDst;

 

}

wstring CDecodeTextFile::ReadUnicodeString()

{

    fseek(m_fp, 0, SEEK_END);

    int fileSize = ftell(m_fp);

 

    wchar_t *buf = new wchar_t[fileSize / 2];

    memset((void *)buf, 0, fileSize);

 

    fseek(m_fp, 2, SEEK_SET);

    fread(buf, sizeof(char), fileSize, m_fp);

 

    wstring wDst = buf;

 

    delete buf;

    return wDst;

}

wstring CDecodeTextFile::ReadUnicodeBigString()

{

    wstring wDst= L"";

    return wDst;

}

主要介绍:C++11中,GBK-whar相互转换 和utf8-wchar的相互转换

std::wstring_convert<std::codecvt_utf8<wchar_t>> Conver_UTF8;    //utf8-wchar

//GBK在linux下的locale名可能是"zh_CN.GBK"const char* GBK_LOCALE_NAME = ".936"; //GBK在windows下的locale name

wstring_convert<codecvt_byname<wchar_t, char, mbstate_t>> Conver_GBK(new codecvt_byname<wchar_t, char, mbstate_t>(GBK_LOCALE_NAME));    //GBK - whar

从源码可以看出,Unicode小端存储的直接读入转换即可,是因为IntelX86计算机本身也是小端存储,无障碍读取,大端由于存储正好相反,不能直接读取,稍后再完善。
还有怎么判断ANSI和无BOM的UTF8。需要根据内容来判断,稍后完善。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 将UTF-8编码转换ANSI编码是一种常见的转换需求。UTF-8是一种变长编码方式,能够表示全球各种文字字符,而ANSI则是一种单字节编码,通常用于英语等西方语言。下面是一种将UTF-8转换ANSI的方法: 1. 首先,明确一点,UTF-8编码中的字符可能需要多个字节来表示,而ANSI编码只需要一个字节。因此,在转换过程中,如果UTF-8编码中的字符超出了ANSI编码范围,将无法直接进行转换。 2. 创建一个空字符串,用于存储转换后的ANSI编码。 3. 遍历UTF-8编码的每个字节: - 如果字节的最高位是0,表示该字节单独可以表示一个ANSI字符,直接将该字节添加到ANSI编码中。 - 如果字节的最高位是1,表示该字节与后续的字节一起组成一个UTF-8字符。读取后续的字节,构成完整的UTF-8字符。 - 判断UTF-8字符是否超出了ANSI编码范围。如果超出了,则不能进行转换。 - 如果没有超出ANSI编码范围,找到对应的ANSI字符,将其添加到ANSI编码中。 4. 返回转换后的ANSI编码。 需要注意的是,由于ANSI编码只能表示一部分字符,因此某些UTF-8字符可能无法转换为对应的ANSI字符。在实际转换时,可能需要根据具体的需求和使用环境进行处理和调整。 ### 回答2: C和UTF-8是不同的字符编码标准。C是一种早期的编程语言,它使用的是ANSI字符集作为默认的字符编码ANSI字符集使用1个字节来表示一个字符,总共有256个字符,包括标点符号、数字和一些基本的拉丁字母。UTF-8是一种现代的字符编码标准,它是Unicode的一种实现方式,使用1到4个字节来表示一个字符,总共可以表示超过1百万个字符。 要将UTF-8编码的字符转换ANSI编码,需要注意的是ANSI字符集无法表示一些特殊的字符,因此一些不在ANSI字符集中的字符可能会丢失或替换为问号或其他无效字符。可以使用一些文本编辑软件或编程语言提供的转换函数来实现这个转换过程。 然而,需要注意的是,由于ANSI字符集的限制和UTF-8编码字符集的广泛使用,将UTF-8转换ANSI可能会导致信息的丢失或错误的表示,特别是对于包含非拉丁字母或特殊符号的文本。因此,更推荐使用支持UTF-8的字符编码,以保留原始文本的完整性和准确性。 ### 回答3: 将UTF-8编码转换ANSI编码需要使用特定的软件或工具。由于UTF-8编码包含了更多的字符和字符集,而ANSI编码只包含较少的字符集,所以转换时可能会有一些字符无法转换或出现乱码的情况。 首先,可以使用一些文本编辑器软件来进行这种转换。比如在Windows操作系统中,可以使用记事本打开UTF-8编码的文本文件,然后选择“另存为”选项,将编码格式选择为ANSI,然后保存即可。这样就将UTF-8编码转换为了ANSI编码。但需要注意的是,如果文本中包含某些特殊字符,转换后可能会出现乱码或字符丢失的情况。 另外,也可以使用一些第三方的编码转换工具来进行转换。这些工具可以将UTF-8编码的文本文件导入,然后选择转换ANSI编码,最后保存转换后的文件。这种方式相对来说更加灵活,可以对特定字符进行自定义的转换设置。 无论是使用文本编辑器还是编码转换工具,都需要注意转换后的结果可能不完全准确,特别是对于一些特殊字符和格式的处理。因此,在进行编码转换时,应该仔细检查转换后的文件,确保没有出现乱码或字符丢失的情况。对于一些重要的数据和文件,最好备份原始的UTF-8编码版本,以防止转换后无法还原的情况发生。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值