基于ESP8266的UTF-8向GB2312转码


前言

现在大部分人都会用到esp8266的功能,可能大家会发现esp8266获取到的网页信息在只有在Arduino的串口可以输出,如果选择了其他串口助手就会乱码,这个是因为Arduino和大部分网页(如心知天气)采用的都是UTF-8编码,但是我们的汉字是GB2312编码。因此进行转码比较重要,尤其显示汉字的时候,便于直接承袭GB2312的汉字字库。因此笔者在这里实现了一下UTF8向GB2312的文件转码。


主要的转码方式首先转码为UTF8向Uniocde转码,这个部分可以直接转码,有固定的格式。但是Unicode没办法了,只能进行查表法进行查表才可以转换为GB2312。

一、UTF8转码为Unicode

首先了解一下UTF8编码的格式,首先英文的Ascii码的范围是0-127,最高位为0.因此UTF8最高位为1进行区分。这样可以判断编码方式。UTF8是变长度的编码方式,因此第一步是确定编码的方式。通过字节的最高位的1的个数判断,如110x xxxx就是2字节,1110 xxxx就是3字节。后面紧跟的其他字节最高位统一为10xxxxxxx。因此Unicode完成UTF8格式后开始填充,注意,这里填充的是从后面几个字节开始填充,意思从低向高,不足补0;


// #txt---  
   |  Unicode符号范围      |  UTF-8编码方式  
 n |  (十六进制)           | (二进制)  
---+-----------------------+------------------------------------------------------  
 1 | 0000 0000 - 0000 007F |                                              0xxxxxxx  
 2 | 0000 0080 - 0000 07FF |                                     110xxxxx 10xxxxxx  
 3 | 0000 0800 - 0000 FFFF |                            1110xxxx 10xxxxxx 10xxxxxx  
 4 | 0001 0000 - 0010 FFFF |                   11110xxx 10xxxxxx 10xxxxxx 10xxxxxx  
 5 | 0020 0000 - 03FF FFFF |          111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx  
 6 | 0400 0000 - 7FFF FFFF | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx  
// #txt---end  
//获取UTF8的字节数
char enc_get_utf8_size(char utf8){
  char temp=0;
  int i;
  for(i=0;i<6;i++){
    if(utf8 &(0x80>>i)){
      temp++;//判断最高位几个1,就是几个字节,注意没有1代表0个字节
    }else{
      break;
    }    
  }
  return temp;
}
//进行UTF8向Unicode转码,返回的是UTF8的字节数
char enc_utf8_to_unicode_one(unsigned char* pInput, unsigned char *Unic)
{
  
    // b1 表示UTF-8编码的pInput中的高字节, b2 表示次高字节, ...
    char b1, b2, b3, b4, b5, b6;
  
    *Unic = 0x0; // 把 *Unic 初始化为全零
    char utfbytes = enc_get_utf8_size(*pInput);
    unsigned char *pOutput = (unsigned char *) Unic;
    switch ( utfbytes )
    {
        case 0:
            *pOutput     = *pInput;
            utfbytes++;//0代表1个字节,需要手动加一
            break;
        case 2:
            b1 = *pInput;
            b2 = *(pInput + 1);
            if ( (b2 & 0xE0) != 0x80 )
                return 0;
            *pOutput     = (b1 << 6) + (b2 & 0x3F);
            *(pOutput+1) = (b1 >> 2) & 0x07;
            break;
        case 3:
            b1 = *pInput;
            b2 = *(pInput + 1);
            b3 = *(pInput + 2);
            if ( ((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80) )
                return 0;
            *pOutput     = (b2 << 6) + (b3 & 0x3F);
            *(pOutput+1) = (b1 << 4) + ((b2 >> 2) & 0x0F);
            break;
        case 4:
            b1 = *pInput;
            b2 = *(pInput + 1);
            b3 = *(pInput + 2);
            b4 = *(pInput + 3);
            if ( ((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80)
                    || ((b4 & 0xC0) != 0x80) )
                return 0;
            *pOutput     = (b3 << 6) + (b4 & 0x3F);
            *(pOutput+1) = (b2 << 4) + ((b3 >> 2) & 0x0F);
            *(pOutput+2) = ((b1 << 2) & 0x1C)  + ((b2 >> 4) & 0x03);
            break;
        case 5:
            b1 = *pInput;
            b2 = *(pInput + 1);
            b3 = *(pInput + 2);
            b4 = *(pInput + 3);
            b5 = *(pInput + 4);
            if ( ((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80)
                    || ((b4 & 0xC0) != 0x80) || ((b5 & 0xC0) != 0x80) )
                return 0;
            *pOutput     = (b4 << 6) + (b5 & 0x3F);
            *(pOutput+1) = (b3 << 4) + ((b4 >> 2) & 0x0F);
            *(pOutput+2) = (b2 << 2) + ((b3 >> 4) & 0x03);
            *(pOutput+3) = (b1 << 6);
            break;
        case 6:
            b1 = *pInput;
            b2 = *(pInput + 1);
            b3 = *(pInput + 2);
            b4 = *(pInput + 3);
            b5 = *(pInput + 4);
            b6 = *(pInput + 5);
            if ( ((b2 & 0xC0) != 0x80) || ((b3 & 0xC0) != 0x80)
                    || ((b4 & 0xC0) != 0x80) || ((b5 & 0xC0) != 0x80)
                    || ((b6 & 0xC0) != 0x80) )
                return 0;
            *pOutput     = (b5 << 6) + (b6 & 0x3F);
            *(pOutput+1) = (b5 << 4) + ((b6 >> 2) & 0x0F);
            *(pOutput+2) = (b3 << 2) + ((b4 >> 4) & 0x03);
            *(pOutput+3) = ((b1 << 6) & 0x40) + (b2 & 0x3F);
            break;
        default:
            return 0;
            break;
    }
    return utfbytes;
}

二、Unicode转GB2312

1.添加对应查找表

如果使用过文件操作系统的会发现,官方给了一个互转的表,这里直接拿出来用。使用该工具进行转换bin文件。
在这里插入图片描述
之后上传到ESP8266的文件系统,就可以查询这个文件了。
在这里插入图片描述

//dir==1,代表oem2uni,0代表uni2oem
//使用前先挂载文件操作系统
unsigned short  read_oem2uni(int i,unsigned int dir)//dir==1,代表oem2uni,0代表uni2oem
{
  if(dir)
  {
    File file = SPIFFS.open("/oem2uni.bin", "r");
    if(!file)
    {
        Serial.print("erro");  
    }
    unsigned short Temp;
    unsigned char temp_l,temp_h;
    file.seek(i * 2, SeekSet);
    file.read(&temp_l,1);
    file.read(&temp_h,1);
    Temp=(temp_h<<8)+temp_l;
    file.close();
    return  Temp; 
  }
  else
  {
    File file = SPIFFS.open("/uni2oem.bin", "r");
    if(!file)
    {
        Serial.print("erro");  
    }
    unsigned short Temp;
    unsigned char temp_l,temp_h;
    file.seek(i * 2, SeekSet);
    file.read(&temp_l,1);
    file.read(&temp_h,1);
    Temp=(temp_h<<8)+temp_l;
    file.close();
    return  Temp;      
  } 
}

2.添加查找算法

查找算法直接移植文件操作系统的就好了,其实也比较简单,主要是二分法查找。

unsigned short ff_convert (  /* Converted code, 0 means conversion error */
  unsigned short  chr,  /* Character code to be converted */
  unsigned int  dir   /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */
)
{
  unsigned short c;
  int i, n, li, hi;
  if (chr < 0x80) { /* ASCII */
    c = chr;
  } else {
    if (dir) {    /* OEMCP to unicode */
      hi = 21792;
    } else {    /* Unicode to OEMCP */
      hi = 21792;
    }
    li = 0;
    for (n = 16; n; n--) {
      i = li + (hi - li) / 2;
      if (chr == read_oem2uni(2*i,dir)) break;
      if (chr > read_oem2uni(2*i,dir))
        li = i;
      else
        hi = i;
        //Serial.println("Times");
       // Serial.println(n);
        //Serial.println(2*i);
       // Serial.println(read_oem2uni(2*i,dir));
    }
    c = n ? read_oem2uni(2*i+1,dir) : 0;
  }

  return c;
}

三、转码整合

首先看一下Unicode的范围,可以看到汉字基本上都是两个字节。
在这里插入图片描述

unsigned short u8f_gb2312(unsigned  char* u8f_buf,unsigned char* gb2312_buf)
{
    int i=0;
    int j=0;
    int k=0;
    unsigned char buf[8];
    while(u8f_buf[i]!=0x00)
    {
        for(j=0;j<8;j++)
        {
          buf[j]=0x00;
        }
        i+=enc_utf8_to_unicode_one((u8f_buf+i),buf);
        for(j=0;j<8;j++)
        {
           if(buf[j]==0x00)
           {
              break;
           }
        }
        if(j==1) gb2312_buf[k++]=buf[0];//如果是ASCII直接取
        else //如果不是ASCII码,就需要进行变换了
        {
          unsigned short temp1=ff_convert((buf[1]<<8)+buf[0],0) ;
          gb2312_buf[k++]=temp1>>8;
          gb2312_buf[k++]=temp1;
        }
    }
    gb2312_buf[k++]=0x00;
    return k;
}

四、测试

#include "utf8.h"
#include "FS.h"
unsigned char x[50];
void setup() {
  Serial.begin(115200);
    if(!SPIFFS.begin()){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }
}
void loop() {
    Serial.println("UTF8:");
    Serial.println("中文测试");
    Serial.println("GB2312");
    int len=u8f_gb2312((unsigned char *)"中文测试",x);
    Serial.write(x,len);
    Serial.println("");
    delay(1000);
}

在这里插入图片描述
在这里插入图片描述
可以看到Arduino支持的是UTF8,而不是GB2312。经过我们的转码,也成功转换了GB2312编码。从两个串口助手可以发现。当然程序有些问题没修复,就是Unicode的长度没判断,就是比较简单的把内部非0的取出来。后面有时间再修复把!
下载连接提供一下,下载积分是0。
https://download.csdn.net/download/qq_15504787/17154038

  • 9
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值