c语言实现utf-8编码解码器
这个例程是在书上看到的,感觉很有意思。
简单介绍一下utf-8编码,utf-8编码是一种变长的编码方式,长度为1-4字节。
当码长为1字节的时候,兼容ascii编码,格式为0xxxxxxx (x处表示有效位)
当码长为2字节的时候,格式为110xxxxx 10xxxxxx
高字节的110表示码长为2字节,低字节的10为低字节标志位,下同
当码长为3字节的时候,格式为1110xxxx 10xxxxxx 10xxxxxx
当码长为4字节的时候,格式为11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
我们先来看一下头文件 unicodeUtf8.h
/*这段代码是utf8编码解码器头文件,在书p66*/
#ifndef _INCLUDE_UTF8_H
#define _INCLUDE_UTF8_H
#include <stdint.h>
uint32_t getByteNumOfEncodeUtf8(int value);
uint32_t getByteNumOfDecodeUtf8(uint8_t byte);
uint8_t encodeUtf8(uint8_t* buf, int value);
int decodeUtf8(const uint8_t* bytePtr, uint32_t length);
#endif
getByteNumOfEncodeUtf8 计算编码后的字节长度
getByteNumOfDecodeUtf8 计算解码后的字节长度
encodeUtf8 编码
decodeUtf8 解码
具体的实现代码
/*这段代码是utf8编码解码器具体实现代码,在书p61*/
#include "unicodeUtf8.h"
#include "common.h"/*详细的ascii码编码规则见书P60*///返回value按照utf8编码后的字节数
uint32_t getByteNumOfEncodeUtf8(int value){ASSERT(value>0, "can't encode negative value!\n"); //只能对正数编码//单个ASCII码字符需要1个字节if(value <= 0x7f){return 1;}//此范围需要2字节if(value <= 0x7ff){return 2;}//此范围需要3字节if(value <= 0xffff){return 3;}//此范围需要4字节if(value <= 0x10ffff){return 4;}//超过范围返回0return 0;
}//把value编码为utf8后写入缓冲区buf,返回写入的字节数
//详细的解释在书p65
uint8_t encodeUtf8(uint8_t* buf, int value){ASSERT(value>0, "can't encode negative value!\n"); //只能对正数编码//按照大端字节序写入缓冲区(低地址对应高数据位)if(value <= 0x7f){*buf = value & 0x7f; //讲value存入内存中return 1;} else if(value <= 0x7ff){//这种情况下utf8格式为110xxxxx 10xxxxxx//先在低地址处写入高字节110xxxxx//11000000 | ((value & 0111 1100 0000)>>6)*buf++ = 0xc0 | ((value & 0x7c0)>>6);//再在高地址处写入低字节10xxxxxx//1000 0000 | (value & 0011 1111)*buf = 0x80 | (value & 0x3f);return 2;}else if(value <= 0xffff){//这种情况下utf8格式为1110xxxx 10xxxxxx 10xxxxxx//先在低地址写入高字节1110xxxx*buf++ = 0xe0 | ((value & 0xf0000)>>12);//然后在中地址写入中字节*buf++ = 0x80 | ((value & 0xfc0)>>6);//最后在高地址写入低字节*buf = 0x80 | (value & 0x3f);return 3;}else if(value <= 0x10ffff){//在这种情况下utf8格式为11110xxx 10xxxxxx 10xxxxxx 10xxxxxx//先在低地址写入高字节11110xxx*buf++ = 0xf0 | ((value & 0x1c0000)>>18);//再在第二段地址写入第二段字节10xxxxxx*buf++ = 0x80 | ((value & 0x3f00)>>12);//然后在第三段地址写入第三段字节10xxxxxx*buf++ = 0x80 | ((value & 0xfc0)>>6);//最后在高地址写入低字节10xxxxxx*buf = 0x80 | (value & 0x3f);return 4;}NOT_REACHED(); //如果程序运行到这里就是错了return 0;
}//返回解码utf8的字节数
uint32_t getByteNumOfDecodeUtf8(uint8_t byte){//byte应该是utf8的最高1字节,如果指向了utf8编码后面低字节部分则返回0if((byte & 0xc0)==0x80) return 0; //1000 0000if((byte & 0xf8)==0xf0) return 4; //1111 0000if((byte & 0xf0)==0xf0) return 3; //1110 0000if((byte & 0xe0)==0xc0) return 2; //1100 0000return 1; //ASCII码
}//解码以bytePtr为起始地址的utf8序列,其最大长度为length,若不是utf8序列就返回-1
int decodeUtf8(const uint8_t* bytePtr, uint32_t length){//若是1字节的ascii码: 0xxxxxxxif(*bytePtr <= 0x7f) return *bytePtr;int value;uint32_t remainingBytes;//先读取高1字节//根据高1字节的高n位判断相应字节数的utf8编码if((*bytePtr & 0xe0)==0xc0){//若是2字节的utf8value = *bytePtr & 0x1f; //记录后面的5位有效位remainingBytes = 1;} else if((*bytePtr & 0xf0)==0xe0){//若是3字节的utf8value = *bytePtr & 0x0f; //记录后面的4位有效位remainingBytes = 2;}else if((*bytePtr & 0xf8)==0xf0){//若是4字节的utf8value = *bytePtr & 0x07;remainingBytes = 3;}else {return -1;} //非法编码//如果utf8被斩断了就不再读下去了if(remainingBytes > length - 1){return -1;}//再读取低字节中的数据while(remainingBytes > 0){bytePtr++;remainingBytes--;//高两位必须是10if((*bytePtr & 0xc0) != 0x80){return -1;}//从次高字节往低字节读value = value << 6 | (*bytePtr & 0x3f); //value左移6为写入6位有效位}return value; //返回解码得到的value值
}
下面我就简单介绍一下编码和解码的算法
编码
我们以3字节码长为例, 当我们的value是12-16位二进制数的时候就应该编码成3字节
我们采用大端字节序,也就是数据位的高位存放在内存的低地址处,数据位的地位存放在内存的高地址处
所以我们先存高1字节,高1字节的格式为 1110 xxxx ,有4位有效为,这4位有效位是value的高4位
我们把value当16位二进制数来看
首先用 value & 0xf000 (也就是 1111 0000 0000 0000) 然后右移12位来得到高4位
之后用0xe0(即1110 0000)或 来得到头标志1110
编码第二个字节的时候也是同样的道理 我们要将value 的第7-12为写入编码中
首先 value & 0xfc0 (即1111 1100 0000)来获得7-12位 然后右移6位
之后用0x80 (即1000 0000)来得到次字节表示10
最后使用第6位写入编码
先用value & 0x3f ( 即0011 1111 )来获得低6位
然后使用0x80获得头10
解码
解码的时候我们先获取高1字节来判断后面有几个字节,然后再继续解码
如果只有一个字节那么它就是ascii码,直接返回即可
如果是两个字节,也就是说 (*bytePtr & 0xe0) == 0xc0 的时候我们还要继续往后读一个字节,此时最高位上有5位有效位
value = *bytePtr & 0x1f 得到5位有效位
三字节和四字节同理,并且有效位逐个减小一位
之说以用0xe0与是因为头标志为110,之后才是有效的数据位
在我们读后面的字节的时候用 (*bytePtr & 0xc0) == 0x80 来判断它是正确编码
然后读取第6位最为有效位写入value中
先 *bytePtr & 0x3f 来获取低6位, 然后value 左移6位再或上得到的低6为即可
参考书籍 《基于c语言自制编程语言》作者 郑钢
c语言实现utf-8编码解码器相关推荐
- HTML CSS杂记
之html (超文本标记语言)css (层叠样式表)杂记 ----------------------------------------------------------------------- ...
- Native Rss Reader 的资料
转:http://hi.baidu.com/mikyliang/blog/item/11d420d3135832013af3cf19.html RSS文档的构成 2007-05-04 14:58 RS ...
- c语言输出字符的utf码,C语言里如何把GBK码转换为UTF8?
C语言里如何把GBK码转换为UTF8? (2013-03-04 19:41:31) 标签: it 一.利用iconv函数族进行编码转换 在LINUX上进行编码转换时,既可以利用iconv函数族编程实现 ...
- Java MorseCoder - Java 语言实现的摩尔斯电码编码解码器
最近在看<编码-隐匿在计算机软件背后的语言>这本书,看到了一张译码表很有意思: 不免让我会想起本科在学编码的那段轻松岁月,于是就去查了如何用Java代码实现这个摩尔的编码和译码过程,代码如 ...
- c语言 utf 8转字符串,如何将UTF-8字节[]转换为字符串?
我有一个byte[]数组,它是从一个我所知道的包含UTF-8的文件中加载的. 在一些调试代码中,我需要将其转换为字符串. 是否有一个班轮可以做到这一点? 在幕后 ,它应该只是一个分配和一个内存复制 , ...
- python语言type board_菜鸟学Python,双手奉上老司机给上路新手总结的Python实战问题……...
针对Python这一话题每天后台都会有不少小伙伴提出问题,下面我就将这些问题进行汇整,产出"Python实战问题篇",我认为这些问题非常具有代表性,希望可以帮到大家. 第一类问题: ...
- Swift教程Swift语言快速入门(内部资料)
Swift语言快速入门(内部资料) 试读下载地址:http://pan.baidu.com/s/1eQCGRHw 前言Swift教程Swift语言快速入门(内部资料)Swift教程Swift语言快速入 ...
- python语言中文社区-python中用中文
广告关闭 2017年12月,云+社区对外发布,从最开始的技术博客到现在拥有多个社区产品.未来,我们一起乘风破浪,创造无限可能. 花下猫语:在 python 中是否可以实现中文数字的四则运算呢? 答案是 ...
- python语言中文社区-python解决中文
广告关闭 腾讯云双11爆品提前享,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高满返5000元! python 添加中文注释时出现运行失败. 需要在顶部设置编码. # coding ...
最新文章
- Spring单实例、多线程安全、事务解析
- 在Docker里使用(支持镜像继承的)supervisor管理进程(转)
- oracle主目录自动检测,ORACLE ADDM数据库自动诊断测试
- 关于Android的EditText焦点问题
- Python教程:collections的deque()方法
- kodexplorer开源网盘php程序配置解析
- BZOJ4545: DQS的trie 广义后缀自动机_LCT
- 微信小程序之二维仿射变换
- python机器学习案例系列教程——逻辑分类/逻辑回归LR/一般线性回归(softmax回归)
- spring+hibernate 下载
- SpringMVC的乱码问题解决
- Excel转PDF方法
- 李子奈《计量经济学》第四版笔记和课后答案
- 人脸识别门禁系统需求分析文档
- java代理模式租房案例
- 动态域名解析ipv6 群辉dnspod_群晖设置ipv6动态域名
- 教师资格综合素质知识要点记录
- 【2021全国高校计算机能力挑战赛Python题目】17.学科竞赛 现有六门功课(语文、数学、物理、化学、政治、历史)的成绩,现在需要从中选拔优秀同学参加如下学科竞赛
- python pyecharts 画图 饼图柱状图
- 发现一个叫阿尔法城的小站(以后此贴为我记录日常常用网址的帖子了)
热门文章
- Cutting a Rod
- php 工资条系统下载,发工资条软件
- 使用Linux版印象笔记nixnote2
- SCOI2014 方伯伯的玉米田 题解
- 安卓模拟器刷小米系统_米柚模拟器下载_米柚手游模拟器(在电脑上玩遍小米所有手游) 2.1.9.9 官方版_极速下载站...
- PS生成动态的二维码
- 网易视频云:流媒体服务器原理和架构解析
- 腾讯企鹅辅导 H5 性能极致优化
- SpringBoot--多线程07
- OC循环渐进:文件管理--计算文件大小的五种方式