一、关于window下的一些编码常识
对于一个文本文件,可保存为ANSI、UTF-8、UTF-16 LE、UTF-16BE及 带有BOM的UTF-8
1)先说说啥是BOM?
BOM,是Byte Order Mark 的缩写,就是字节序标记。
2)UTF-8 的 编码规则
在前面文字提到如何解读UTF-8, 这里再回顾并补充一些。
UTF-8 是 遵从 unicode 标准的(也就是是说,UTF-8表示的字符编码在unicode编码表中)
但是,UTF-8 是变长(1~4字节)编码的,如何实现变长呢?
还是举个例子,对于“中”这个汉字,对应 unicode 编码表,其字符编码值为 0x4E 0x2D
没错,汉字是2个字节表示的,4E 2D 刚好两字节。来看看UTF-8 对“中”字的编码,如下
E 4 B 8 A D
1110 0100 1011 1000 1010 1101
1110 前导三个1,表示用三个字节表示该字符, B8 AD 两个字节的高两位都是10,起标识作用,去掉这些标识后
0100 1110 0010 1101 ---> 即,4E 2D, 刚好组成16位,表示“中”的编码值
3)UTF-8 BOM是怎么回事?
明确1)2)两点后,不难发现,带BOM的UTF-8,其BOM只是作为UTF-8编码方式的标识而已。
不信看看,在window下,以记事本写一个“中”字,然后分别另存为UTF-8 和 带BOM的UTF-8,
再通过UE以十六进制查看,如下图

接着,按UTF-8的编码规则,对EF BB BF 解析可得到,其编码值为 FE FF(可以自己尝试下)
那FE FF在unicode编码表中对应什么字符呢?
对应ZERO WIDTH NO-BREAK SPACE"字符,表示这是一个不存在的字符。
UCS规范建议我们在传输字节流前,先传输 字符"ZERO WIDTH NO-BREAK SPACE"。这样如果接收者收到FEFF,
就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little- Endian的。因此字符
"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。
因此,UTF8 BOM 是以(EF BB BF)字节开头的文本流。
UTF-8的一些个人理解:
UTF-8 以 8 位为一个编码单元,每个字符会由 1 到 4 个单元表示。因此,一个字节根本不存在字节序问题
所以,UTF-8可以带BOM,也可不带(推荐)。个人觉得,带BOM只是更方便软件识别文件的编码方式。当然
不带BOM也可识别,没那么直观、简单判断而已。
另外,既然UTF-8是以一个字节为单位来传输,那么即使丢失了一些字节,也不会影响到后面的解码。
就如“中”字的UTF-8编码来说,假如丢失了第一个字节,那么B8 AD的二进制为 1011 1000 1010 1101
10 是个标志而已,那么就解读为 11 10,00 10, 1101 ---> 0E, 2D.其实这个字乱码了,但是后续的字节流
一点都不受影响,肯定能正确解析。而像UTF-16,如果前面字节丢失了,肯定会影响后续的解码的。
但实际,字节流都会有校验一步的,检测到字节丢失,肯定会丢弃重传。所以大可放心接收到的字节流的准确性。
4)UTF-16 LE 和 UTF-16 BE
有了前面的理解,这两个不难理解,LE: 小端 BE:大端,要区分两者,BOM标志不同
文本开头FEFF,就表明这个字节流是Big-Endian的;而如果是 FFFE,就表明这个字节流是Little- Endian的。
因此,总结来说,BOM对于UTF-16被用作字节序的标识,而对于UTF-8是标识UTF-8编码,可有可无
5)ANSI 编码
在简体中文window环境下,当保存为ANSI编码文件时,对于字母,ANSI会以ASCII方式一个字节存储编码值,
而对于汉字,则以GBK编码两字节大小存储一个汉字。
好了,通过对window下的编码讨论,也发现了一个问题。对于一个文本文件,自然先要判断编码方式,然后
去掉这些标志字符(BOM)以获取正确的文本数据。因此很有必要划分出一个“编码子模块”,用于解析这些文件的编码
然后才能正确提取后面的编码值,后面的获取点阵和显示点阵才能正确进行下去。
二、编码子模块介绍
遵循前面两个子模块的介绍,编码子模块的框图如下

三、源码实现与编译检查
encoding_manager.h
#ifndef _ENCODING_MANAGER_H
#define _ENCODING_MANAGER_H
#include <fonts_manager.h>
#include <disp_manager.h>
typedef struct EncodingOpr{
char* name;
int iHeadLen;
PT_FontOpr ptFontOprSupportedHead;
int (*isSupport)(unsigned char* pucBufHead);
int (*GetCodeFrmBuf)(unsigned char* pucBufStart, unsigned char*pucBufEnd,unsigned int *pdwCode);
struct EncodingOpr* ptNext;
}T_EncodingOpr, *PT_EncodingOpr;
int RegisterEncodingOpr(PT_EncodingOpr ptEncodingOpr);
void ShowEncodingOpr(void);
PT_DispOpr GetDispOpr(char *pcName);
PT_EncodingOpr SelectEncodingOprForFile(unsigned char *pucFileBufHead);
int AddFontOprForEncoding(PT_EncodingOpr ptEncodingOpr, PT_FontOpr ptFontOpr);
int DelFontOprFrmEncoding(PT_EncodingOpr ptEncodingOpr, PT_FontOpr ptFontOpr);
int EncodingInit(void);
int AsciiEncodingInit(void);
int Utf16beEncodingInit(void);
int Utf16leEncodingInit(void);
int Utf8EncodingInit(void);
#endif
encoding_manager.c
#include <config.h>
#include <encoding_manager.h>
#include <string.h>
#include <stdlib.h>
static PT_EncodingOpr g_ptEncodingOprHead;
int RegisterEncodingOpr(PT_EncodingOpr ptEncodingOpr)
{
PT_EncodingOpr ptTmp;
if(!g_ptEncodingOprHead)
{
g_ptEncodingOprHead = ptEncodingOpr;
ptEncodingOpr->ptNext = NULL;
}
else
{
ptTmp = g_ptEncodingOprHead;
while(ptTmp->ptNext)
{
ptTmp = ptTmp->ptNext;
}
ptTmp->ptNext = ptEncodingOpr;
ptEncodingOpr->ptNext = NULL;
}
return 0;
}
void ShowEncodingOpr(void)
{
int i = 0;
PT_EncodingOpr ptTmp = g_ptEncodingOprHead;
while(ptTmp)
{
printf("%02d %s\n", i++, ptTmp->name);
ptTmp = ptTmp->ptNext;
}
}
PT_EncodingOpr SelectEncodingOprForFile(unsigned char* pucFileBufHead)
{
PT_EncodingOpr ptTmp = g_ptEncodingOprHead;
while(ptTmp)
{
if(ptTmp->isSupport(pucFileBufHead))
return ptTmp;
else
ptTmp = ptTmp->ptNext;
}
return NULL;
}
int AddFontOprForEncoding(PT_EncodingOpr ptEncodingOpr, PT_FontOpr ptFontOpr)
{
PT_FontOpr ptFontOprCpy;
if(!ptEncodingOpr || !ptFontOpr)
return -1;
else
{
ptFontOprCpy = malloc(sizeof(T_FontOpr));
if(!ptFontOprCpy)
{
return -1;
}
else
{
memcpy(ptFontOprCpy, ptFontOpr, sizeof(T_FontOpr));
ptFontOprCpy->ptNext = ptEncodingOpr->ptFontOprSupportedHead;
ptEncodingOpr->ptFontOprSupportedHead = ptFontOprCpy;
return 0;
}
}
}
int DelFontOprFrmEncoding(PT_EncodingOpr ptEncodingOpr, PT_FontOpr ptFontOpr)
{
PT_FontOpr ptTmp;
PT_FontOpr ptPre;
if(!ptEncodingOpr || !ptFontOpr)
return -1;
else
{
ptTmp = ptEncodingOpr->ptFontOprSupportedHead;
if(strcmp(ptTmp->name, ptFontOpr->name) == 0)
{
ptEncodingOpr->ptFontOprSupportedHead = ptTmp->ptNext;
free(ptTmp);
return 0;
}
ptPre = ptEncodingOpr->ptFontOprSupportedHead;
ptTmp = ptPre->ptNext;
while(ptTmp)
{
if(strcmp(ptTmp->name, ptFontOpr->name) == 0)
{
ptPre->ptNext = ptTmp->ptNext;
free(ptTmp);
return 0;
}
else
{
ptPre = ptTmp;
ptTmp = ptTmp->ptNext;
}
}
return -1;
}
}
int EncodingInit(void)
{
int iError;
iError = AsciiEncodingInit();
if(iError)
{
DBG_PRINTF("AsciiEncodingInit error!\n");
return -1;
}
iError = Utf16leEncodingInit();
if (iError)
{
DBG_PRINTF("Utf16leEncodingInit error!\n");
return -1;
}
iError = Utf16beEncodingInit();
if (iError)
{
DBG_PRINTF("Utf16beEncodingInit error!\n");
return -1;
}
iError = Utf8EncodingInit();
if (iError)
{
DBG_PRINTF("Utf8EncodingInit error!\n");
return -1;
}
return 0;
}
ascii_encoding.c
#include <config.h>
#include <encoding_manager.h>
#include <string.h>
static int isAsciiCoding(unsigned char *pucBufHead);
static int AsciiGetCodeFrmBuf(unsigned char *pucBufStart, unsigned char *pucBufEnd, unsigned int *pdwCode);
static T_EncodingOpr g_tAsciiEncodingOpr = {
.name = "ascii",
.iHeadLen = 0,
.isSupport = isAsciiCoding,
.GetCodeFrmBuf = AsciiGetCodeFrmBuf,
};
static int isAsciiCoding(unsigned char * pucBufHead)
{
const char aStrUtf8[] = {0xEF, 0xBB, 0xBF, 0};
const char aStrUtf16le[] = {0xFF, 0xFE, 0};
const char aStrUtf16be[] = {0xFE, 0xFF, 0};
if(strncmp((const char*)pucBufHead, aStrUtf8, 3) == 0)
{
return 0;
}
else if(strncmp((const char*)pucBufHead, aStrUtf16le, 2) == 0)
{
return 0;
}
else if(strncmp((const char*)pucBufHead, aStrUtf16be, 2) == 0)
{
return 0;
}else
{
return 1;
}
}
static int AsciiGetCodeFrmBuf(unsigned char * pucBufStart, unsigned char * pucBufEnd, unsigned int * pdwCode)
{
unsigned char *pucBuf = pucBufStart;
unsigned char c = *pucBuf;
if((pucBuf < pucBufEnd) && (c < (unsigned char)0x80))
{
*pdwCode = (unsigned)c;
return 1;
}
if(((pucBuf + 1) < pucBufEnd) && (c >= (unsigned char)0x80))
{
*pdwCode = pucBuf[0] + (((unsigned int)pucBuf[1]) << 8);
return 2;
}
if(pucBuf < pucBufEnd)
{
*pdwCode = (unsigned int)c;
return 1;
}
else
{
return 0;
}
}
int AsciiEncodingInit(void)
{
AddFontOprForEncoding(&g_tAsciiEncodingOpr, GetFontOpr("freetype"));
AddFontOprForEncoding(&g_tAsciiEncodingOpr, GetFontOpr("ascii"));
AddFontOprForEncoding(&g_tAsciiEncodingOpr, GetFontOpr("gbk"));
return RegisterEncodingOpr(&g_tAsciiEncodingOpr);
}
utf-8_encoding.c
#include <config.h>
#include <encoding_manager.h>
#include <string.h>
static int isUtf8Coding(unsigned char *pucBufHead);
static int Utf8GetCodeFrmBuf(unsigned char *pucBufStart, unsigned char *pucBufEnd, unsigned int *pdwCode);
static T_EncodingOpr g_tUtf8EncodingOpr = {
.name = "utf-8",
.iHeadLen = 3,
.isSupport = isUtf8Coding,
.GetCodeFrmBuf = Utf8GetCodeFrmBuf,
};
static int isUtf8Coding(unsigned char * pucBufHead)
{
const char aStrUtf8[] = {0xEF, 0xBB, 0xBF, 0};
if(strncmp((const char*)pucBufHead, aStrUtf8, 3) == 0)
{
return 1;
}
else
{
return 0;
}
}
static int GetPreOneBits(unsigned char ucVal)
{
int i;
int j = 0;
for(i = 7; i >= 0; i--)
{
if(!(ucVal & (1<<i)))
break;
else
j++;
}
return j;
}
static int Utf8GetCodeFrmBuf(unsigned char * pucBufStart, unsigned char * pucBufEnd, unsigned int * pdwCode)
{
int i;
int iNum;
unsigned char ucVal;
unsigned int dwSum = 0;
if(pucBufStart >= pucBufEnd)
{
return 0;
}
ucVal = pucBufStart[0];
iNum = GetPreOneBits(pucBufStart[0]);
if((pucBufStart + iNum) > pucBufEnd)
{
return 0;
}
if(iNum == 0)
{
*pdwCode = pucBufStart[0];
return 1;
}
else
{
ucVal = ucVal << iNum;
ucVal = ucVal >> iNum;
dwSum += ucVal;
for(i = 1; i < iNum; i++)
{
ucVal = pucBufStart[i] & 0x3f;
dwSum = dwSum << 6;
dwSum += ucVal;
}
*pdwCode = dwSum;
return iNum;
}
}
int Utf8EncodingInit(void)
{
AddFontOprForEncoding(&g_tUtf8EncodingOpr, GetFontOpr("freetype"));
AddFontOprForEncoding(&g_tUtf8EncodingOpr, GetFontOpr("ascii"));
return RegisterEncodingOpr(&g_tUtf8EncodingOpr);
}
utf-16le_encoding.c
#include <config.h>
#include <encoding_manager.h>
#include <string.h>
static int isUtf16leCoding(unsigned char*pucBufHead);
static int Utf16leGetCodeFrmBuf(unsigned char *pucBufStart, unsigned char *pucBufEnd, unsigned int *pdwCode);
static T_EncodingOpr g_tUtf16leEncodingOpr = {
.name = "utf-16le",
.iHeadLen = 2,
.isSupport = isUtf16leCoding,
.GetCodeFrmBuf = Utf16leGetCodeFrmBuf,
};
static int isUtf16leCoding(unsigned char * pucBufHead)
{
const char aStrUtf16le[] = {0xFF, 0xFE, 0};
if(strncmp((const char*)pucBufHead, aStrUtf16le, 2) == 0)
{
return 1;
}
else
{
return 0;
}
}
static int Utf16leGetCodeFrmBuf(unsigned char * pucBufStart, unsigned char * pucBufEnd, unsigned int * pdwCode)
{
if(pucBufStart + 1 < pucBufEnd)
{
*pdwCode = (((unsigned int)pucBufStart[1])<<8) + pucBufStart[0];
return 2;
}
else
{
return 0;
}
}
int Utf16leEncodingInit(void)
{
AddFontOprForEncoding(&g_tUtf16leEncodingOpr, GetFontOpr("freetype"));
AddFontOprForEncoding(&g_tUtf16leEncodingOpr, GetFontOpr("ascii"));
return RegisterEncodingOpr(&g_tUtf16leEncodingOpr);
}
utf16-be_encoding.c
#include <config.h>
#include <encoding_manager.h>
#include <string.h>
static int isUtf16beCoding(unsigned char*pucBufHead);
static int Utf16beGetCodeFrmBuf(unsigned char *pucBufStart, unsigned char *pucBufEnd, unsigned int *pdwCode);
static T_EncodingOpr g_tUtf16beEncodingOpr = {
.name = "utf-16be",
.iHeadLen = 2,
.isSupport = isUtf16beCoding,
.GetCodeFrmBuf = Utf16beGetCodeFrmBuf,
};
static int isUtf16beCoding(unsigned char * pucBufHead)
{
const char aStrUtf16be[] = {0xFE, 0xFF, 0};
if(strncmp((const char*)pucBufHead, aStrUtf16be, 2) == 0)
{
return 1;
}
else
{
return 0;
}
}
static int Utf16beGetCodeFrmBuf(unsigned char * pucBufStart, unsigned char * pucBufEnd, unsigned int * pdwCode)
{
if(pucBufStart + 1 < pucBufEnd)
{
*pdwCode = (((unsigned int)pucBufStart[0])<<8) + pucBufStart[1];
return 2;
}
else
{
return 0;
}
}
int Utf16beEncodingInit(void)
{
AddFontOprForEncoding(&g_tUtf16beEncodingOpr, GetFontOpr("freetype"));
AddFontOprForEncoding(&g_tUtf16beEncodingOpr, GetFontOpr("ascii"));
return RegisterEncodingOpr(&g_tUtf16beEncodingOpr);
}
编译检测语法/书写有无错误

注意:编译时,也要编译点阵子模块,因为两者是是有关联的,如EncodingOpr结构体有PT_FontOpr 指针,指向该编码
支持的字体文件。比如说,对于ASCII,不仅支持ascii字体文件,还有gbk和freetype两种字体文件,即对应于UTF-16le、
UTF-16be、UTF-8这三者编码方式。这三者都能表达ascii字符,只是编码方式不同,需分别识别再提取有效的文本数据。
OK,三大子模块讲解完了,也是电子书框架最基本的组件。实际,这三个子模块框架都是一样的。编码并不难,套路都一样。
难点或者电子书的核心点在于下一篇文章的“draw.c”的实现,不仅要综合这三个字模块,还要向上提供换页显示功能。