热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于解决乱码问题的一点探索之二(涉及Unicode(utf-16)和GBK)

在上篇日志中(链接),我们讨论了utf-8编码和GBK编码之间转化的乱码问题,这一篇我们讨论Unicode(utf-16编码方式)与GBK编码之间转换的乱码问题。在Windows系

    在上篇日志中(链接),我们讨论了utf-8编码和GBK编码之间转化的乱码问题,这一篇我们讨论Unicode(utf-16编码方式)与GBK编码之间转换的乱码问题。

    在Windows系统自带的记事本中,我们按照图中所示使用Unicode编码保存。

image

    在Visual Studio 2005中,单击“文件|高级保存选项”中选择Unicode-代码页1200。

image

文件中只有乱码与ASCII码

    按照上一篇日志中的方法,我们使用WinHex软件查看文件的16进制数据,如下:

image

    其中,头两个字节“FF FE”代表utf-16的BOM码,从D4开始就是数据,即:

0xd4    0x00    0xda    0x00    
0xb4    0x00    0xcb    0x00    
0xcc    0x00    0xed    0x00    
0xbc    0x00    0xd3    0x00    
0xd7    0x00    0xa8    0x00    
0xd3    0x00    0xc3    0x00    
0xb4    0x00    0xfa    0x00    
0xc2    0x00    0xeb    0x00    
0xba    0x00    0xcd    0x00    
0x2f    0x00    
0xbb    0x00    0xf2    0x00    
0xb5    0x00    0xf7    0x00    
0xd3    0x00    0xc3    0x00    
0xbb    0x00    0xf9    0x00    
0xc0    0x00    0xe0    0x00

    我们同时找出正确字符的GBK编码值

0xd4    0xda    
0xb4    0xcb    
0xcc    0xed    
0xbc    0xd3    
0xd7    0xa8    
0xd3    0xc3    
0xb4    0xfa    
0xc2    0xeb    
0xba    0xcd    
0x2f    
0xbb    0xf2    
0xb5    0xf7    
0xd3    0xc3    
0xbb    0xf9    
0xc0    0xe0

    我们很容易发现,只要将偶数列中的0x00数消除就可以了,当然,我们格式也从utf-8变为GBK了。

    对应的C语言程序如下:

#include 
#include 

int main(int argc, char const *argv[])
{
    FILE* fp;
    FILE* fp2;
    //打开存储乱码的文件,utf-16编码格式,二进制打开
    if((fp2=fopen("BadCode.txt","rb+"))==NULL)
    {
        printf("Open Source File Failed!\n");
        system("pause");
        exit(1);
    }
    //打开、新建存储处理后数据的文件
    if((fp=fopen("BadCodeH.txt","w+"))==NULL)
    {
        printf("Open/Create Destination File Failed!\n");
        system("pause");
        exit(1);
    }

    //乱码第一个高位字节
    unsigned ch;
    //乱码第一个低位字节
    unsigned cl;
    //乱码第二个高位字节
    unsigned ch2;
    //乱码第二个低位字节
    unsigned cl2;

    ch=fgetc(fp2);
    //丢弃BOM字节信息
    if(ch==0xff)
    {
        fgetc(fp2);
        ch=fgetc(fp2);
    }

    cl=fgetc(fp2);
    while(!feof(fp2))
    {
        //乱码,接着读取两个字节,再丢弃偶数字节
        if (ch>0x7f && cl==0x00)
        {
            ch2=fgetc(fp2);
            cl2=fgetc(fp2);
            fputc(ch,fp);
            fputc(ch2,fp);
        }
        //ASCII码,丢弃偶数字节
        else
        {
            fputc(ch,fp);
            
        }
        ch=fgetc(fp2);
        cl=fgetc(fp2);
    }
    fclose(fp);
    fclose(fp2);
    system("pause");
    return 0;
}

运行结果如下:

image

更一般的情况(文件中有正常的中文字符,乱码和ASCII字符)

    和上一篇日志中分析的差不多,对于正常的utf-16编码的字符,我们只要将其转换为GBK编码输出就可以了,需要注意的是,正常的utf-16字符编码在文件中的存储方式:高位字节存放编码的后两位,低位字节存放编码的前两位。

C语言程序如下,戳此处下载UnicodeToGBK.txt文件:

#include 
#include 

//读取utf-16和GBK转换表中的数据
bool ReadMap(unsigned* mapValue)
{
    //声明文件指针
    FILE* fp;
    
    //以可读写方式打开映射数据的文本文件
    if (NULL == (fp = fopen("UnicodeToGBK.txt", "r+")))
    {
        printf("Error!");
        system("pause");
        return false;
    }
    

    //存储Unicode的16进制数据的字符串
    char utfStr[4];
    //存储gbk的16进制数据的字符串
    char gbkStr[4];
    //存储Unicode16进制数据
    unsigned utfId;
    //存储gbk的16进制数据
    unsigned gbkId;
    //处理字符的临时变量
    char c;
    //读取数据

    while(!feof(fp))
    {
        //读Unicode值的字符串
        fread(utfStr,4,1,fp);
        //转换为整型
        sscanf(utfStr,"%4x",&utfId);
        fgetc(fp);
        //读gbk值的字符串
        fread(gbkStr,4,1,fp);
        //转化为整型
        sscanf(gbkStr,"%4x",&gbkId);
        fgetc(fp);

        //赋值
        mapValue[utfId]=gbkId;    
    }
    

    fclose(fp);
    return true;
}

int main(int argc, char const *argv[])
{
    FILE* fp;
    FILE* fp2;
    //打开存储乱码的文件,utf-16编码格式,二进制打开
    if((fp2=fopen("BadCode.txt","rb+"))==NULL)
    {
        printf("Open Source File Failed!\n");
        system("pause");
        exit(1);
    }
    //打开、新建存储处理后数据的文件
    if((fp=fopen("BadCodeH.txt","w+"))==NULL)
    {
        printf("Open/Create Destination File Failed!\n");
        system("pause");
        exit(1);
    }

    unsigned mapValue[65536];
    if(!ReadMap(mapValue))
    {
        printf("Convert Failed!\n");
        system("pause");
        exit(1);
    }
    //乱码第一个高位字节
    unsigned ch;
    //乱码第一个低位字节
    unsigned cl;
    //乱码第二个高位字节
    unsigned ch2;
    //乱码第二个低位字节
    unsigned cl2;
    //存储utf-16编码的值
    unsigned utf;
    //存储gbk编码的值
    unsigned gbk;

    ch=fgetc(fp2);
    //丢弃BOM字节信息
    if(ch==0xff)
    {
        fgetc(fp2);
        ch=fgetc(fp2);
    }

    cl=fgetc(fp2);
    while(!feof(fp2))
    {
        //乱码,接着读取两个字节,再丢弃偶数字节
        if (ch>0x7f && cl==0x00)
        {
            ch2=fgetc(fp2);
            cl2=fgetc(fp2);
            fputc(ch,fp);
            fputc(ch2,fp);
        }
        //ASCII码,丢弃偶数字节
        else if(ch<=0x7f && cl==0x00)
        {
            fputc(ch,fp);
            
        }
        //否则就是正常字符,进行编码转换后输出
        else
        {
            
            utf=cl*256+ch;
            gbk=mapValue[utf];            
            fputc(gbk/256,fp);
            fputc(gbk%256,fp);
        }
        ch=fgetc(fp2);
        cl=fgetc(fp2);
    }
    fclose(fp);
    fclose(fp2);
    system("pause");
    return 0;
}

例子如下:

image

    未来我会将两篇文章中的程序进行整合,写一个图形界面的程序出来。

    若有错误,恳请各位大侠指教~~


推荐阅读
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • 本文介绍了PE文件结构中的导出表的解析方法,包括获取区段头表、遍历查找所在的区段等步骤。通过该方法可以准确地解析PE文件中的导出表信息。 ... [详细]
  • 1、工具VS2015OpenCV3.20下载地址:https:sourceforge.netprojectsopencvlibrary2、步骤1.下载工具ÿ ... [详细]
  • 1关于字符串相邻的两个或多个字符串字面值(引号引起来的字符)将会自动连接到一起:str_catpython!str_cat输出:python!把很长 ... [详细]
  • 深入理解计算机系统之链接(一)
    程序是怎样运行的写好的c程序怎样运行的呢?答案是一个写好的程序要先经过语言预处理器,编译器,汇编器和链接器生成最后的可执行文件,然后加载器将可执行文件加载到内存中才能运行。这里以一 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • c语言\n不换行,c语言printf不换行
    本文目录一览:1、C语言不换行输入2、c语言的 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 本文整理了315道Python基础题目及答案,帮助读者检验学习成果。文章介绍了学习Python的途径、Python与其他编程语言的对比、解释型和编译型编程语言的简述、Python解释器的种类和特点、位和字节的关系、以及至少5个PEP8规范。对于想要检验自己学习成果的读者,这些题目将是一个不错的选择。请注意,答案在视频中,本文不提供答案。 ... [详细]
  • Python脚本编写创建输出数据库并添加模型和场数据的方法
    本文介绍了使用Python脚本编写创建输出数据库并添加模型数据和场数据的方法。首先导入相应模块,然后创建输出数据库并添加材料属性、截面、部件实例、分析步和帧、节点和单元等对象。接着向输出数据库中添加场数据和历程数据,本例中只添加了节点位移。最后保存数据库文件并关闭文件。文章还提供了部分代码和Abaqus操作步骤。另外,作者还建立了关于Abaqus的学习交流群,欢迎加入并提问。 ... [详细]
  • 本文介绍了使用Rust语言编写、保存和编译程序的简单步骤。首先,打开记事本文件并编写程序代码,然后将代码保存到一个以.rs为扩展名的文件中。接下来,使用rustc命令来编译运行程序。最后,通过命令行运行编译后的程序,得到输出结果。如果遇到编译错误,可以下载Build Tools for Visual Studio 2017来解决。 ... [详细]
  • 完成字符串和时间对象的转化(DateFormat)、(以及日历Calendar用法)
    DateFormat 和SimpleDateFormat示例(时间格式的书写)packagecn.date;importjava.text.DateFormat;importjav ... [详细]
  • 开发笔记:对称加密详解,以及JAVA简单实现
     (原)常用的加密有3种1、正向加密,如MD5,加密后密文固定,目前还没办法破解,但是可以能过数据库撞库有一定概率找到,不过现 ... [详细]
author-avatar
Robin Lu
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有