跳转至

iconv字符集转换

一、字符编码介绍

1.1 字符编码的定义与作用

字符编码(Character encoding)是指将字符集中的字符编码为指定集合中的某一对象(例如:比特模式、自然数序列、8 位组或者电脉冲),以便文本在计算机中存储或通过通信网络传递。常见的例子包括将拉丁字母表编码成摩斯电码和 ASCII,其中 ASCII 使用 7 个或 8 个二进制位进行编码,最多可以给 256 个字符分配数值。

1.2 常见字符编码格式

  • ASCII:使用 7 个或 8 个二进制位进行编码,最多可以给 256 个字符分配数值,包括字母、数字和符号。
  • Unicode:一种国际标准字符集,支持世界上几乎所有的书写系统,包括汉字、日文假名等。
  • UTF-8:Unicode 的一种变长字符编码,由 Ken Thompson 于 1992 年创建,现已标准化为 RFC 3629。UTF-8 用 1 到 6 个字节编码 Unicode 字符。
  • GB2312:中国国家标准字符集,用于简体中文。
  • GBK:在 GB2312 基础上扩展的字符集,支持更多的中文字符。
  • Big5:用于繁体中文的字符集。

1.3 字符编码格式的使用场景和注意事项

在使用不同的字符编码格式时,需要注意以下几点:

  • 兼容性:确保发送端和接收端使用相同的字符编码,避免乱码问题。
  • 转换规则:了解 Unicode 和 UTF-8 之间的转换规则,确保正确编码和解码。
  • 应用场景:根据具体应用场景选择合适的字符编码格式,例如 Web 开发中常用 UTF-8。

二、硬件环境

“古人云:‘工欲善其事,必先利其器。’在深入介绍本功能示例之前,我们首先需要确保以下硬件环境的准备工作已经完成。”

参考:硬件环境清单,准备以及组装好硬件环境。

三、软件环境

“凡事预则立,不预则废。”在详细阐述本功能示例之前,我们需先精心筹备好以下软件环境。

1. Luatools 工具

2. 内核固件文件(底层 core 固件文件):LuatOS-SoC_V10001_Air8101.soc;参考项目使用的内核固件

3. luatos 需要的脚本和资源文件

脚本和资源文件:https://gitee.com/openLuat/LuatOS-Air8101/tree/master/demo/wlan/softAP

lib 脚本文件:使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件;

准备好软件环境之后,接下来查看如何烧录项目文件到 Air8101 开发板,将本篇文章中演示使用的项目文件烧录到 Air8101 开发板中。

四、iconv 基本用法

4.1 本教程实现功能定义

本文演示主要展示了在不同编码格式之间进行转换的工具和方法,包括 Unicode(小端和大端)、GB2312 和 UTF-8 等常见编码格式。通过使用 Lua 语言和 Air8101 核心板,实现了以下功能:

  • Unicode 小端编码与 GB2312 编码之间的转换。
  • Unicode 大端编码与 GB2312 编码之间的转换。
  • Unicode 小端编码与 UTF-8 编码之间的转换。
  • Unicode 大端编码与 UTF-8 编码之间的转换。
  • GB2312 编码与 UTF-8 编码之间的转换。

此外,还提供了一个简单的 Lua 脚本,用于在不同编码之间进行转换测试,并打印出转换后的编码数据。通过这个演示,大家可以了解不同编码格式之间的转换规则,并在实际应用中进行编码转换。

4.2 文章内容应用

Air8101 开发板软硬件资料 :https://docs.openluat.com/air8101/

API 参考 : iconv - iconv 操作

4.3 API 介绍

iconv.open(to, from)

用于创建一个字符编码转换器,以便在不同的字符编码之间进行转换。

注意:当前仅支持如下 8 种编码转换

  • ucs2->utf8
  • ucs2->gb2312
  • gb2312->ucs2
  • gb2312->ucs2be
  • ucs2be->utf8
  • ucs2be->gb2312
  • utf8->ucs2
  • utf8->ucs2be

参数

参数
类型
释义
取值
to
string
目标编码格式的名称
"gb2312" : gb2312 编码
"ucs2" : unicode 小端编码
"ucs2be" : unicode 大端编码
"utf8" : utf8 编码
from
string
源编码格式的名称
"gb2312" : gb2312 编码
"ucs2" : unicode 小端编码
"ucs2be" : unicode 大端编码
"utf8" : utf8 编码

返回值

返回值
类型
释义
取值
cd
userdata
与 iconv() 配合使用可以获取最终编码转换后的字符串
成功时,返回一个转换描述符;如果指定的编码不受支持,返回 `nil`

例子

-- ucs2 转换为 utf8
local cd = iconv.open("utf8", "ucs2")
if not cd then
    log.info("ucs2->utf8", "无法创建编码转换器")
else
    log.info("ucs2->utf8", "ok")
end
-- ucs2 转换为 ucs2be
local cd = iconv.open("ucs2be", "ucs2")
if not cd then
    log.info("ucs2->ucs2be", "无法创建编码转换器")
else
    log.info("ucs2->ucs2be", "ok")
end

cd:iconv(str)

在成功创建转换句柄后,可以使用此方法进行字符编码转换。

参数

参数
类型
释义
取值
str
userdata
转换描述符

由 iconv.open() 返回的转换描述符

返回值

返回值
类型
释义
取值
result
string
最终编码转换后的结果
成功时,返回转换后的字符串;如果转换失败,返回 `nil`

例子

-- ucs2 转换为 utf8
local cd = iconv.open("utf8", "ucs2be")
if not cd then
    log.info("ucs2be->utf8", "无法创建编码转换器")
else
    log.info("ucs2be->utf8", "ok")
end

-- 待转换的 ucs2 大端编码字符串
-- 利用 https://www.toolhelper.cn/EncodeDecode/EncodeDecode 网站进行编码转换,注意,编码忽略 Ascii 字符要取消勾选
local input_str = string.fromHex("4f60597d00200061006900720038003100300031") -- ucs2 大端编码的 "你好 air8101"
-- 执行转换
local output_str= cd:iconv(input_str)
if output_str then
    print("转换成功,结果为:" .. string.toHex(output_str)) -- https://www.toolhelper.cn/EncodeDecode/EncodeDecode 查看 E4BDA0 可知依旧是 "你好 air8101"
else
    print("转换失败")
end

五、iconv 整体演示

5.1 源码

源码 : https://gitee.com/openLuat/LuatOS-Air8101/blob/master/demo/iconv/main.lua

5.2 演示

5.2.1 代码实现

-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "iconv_demo"
VERSION = "1.0"

-- sys库是标配
_G.sys = require("sys")

--- unicode小端编码 转化为 gb2312编码
-- @string ucs2s unicode小端编码数据
-- @return string data,gb2312编码数据
-- @usage local data = common.ucs2ToGb2312(ucs2s)
function ucs2ToGb2312(ucs2s)
    local cd = iconv.open("gb2312", "ucs2")
    return cd:iconv(ucs2s)
end

--- gb2312编码 转化为 unicode小端编码
-- @string gb2312s gb2312编码数据
-- @return string data,unicode小端编码数据
-- @usage local data = common.gb2312ToUcs2(gb2312s)
function gb2312ToUcs2(gb2312s)
    local cd = iconv.open("ucs2", "gb2312")
    return cd:iconv(gb2312s)
end

--- unicode大端编码 转化为 gb2312编码
-- @string ucs2s unicode大端编码数据
-- @return string data,gb2312编码数据
-- @usage data = common.ucs2beToGb2312(ucs2s)
function ucs2beToGb2312(ucs2s)
    local cd = iconv.open("gb2312", "ucs2be")
    return cd:iconv(ucs2s)
end

--- gb2312编码 转化为 unicode大端编码
-- @string gb2312s gb2312编码数据
-- @return string data,unicode大端编码数据
-- @usage local data = common.gb2312ToUcs2be(gb2312s)
function gb2312ToUcs2be(gb2312s)
    local cd = iconv.open("ucs2be", "gb2312")
    return cd:iconv(gb2312s)
end

--- unicode小端编码 转化为 utf8编码
-- @string ucs2s unicode小端编码数据
-- @return string data,utf8编码数据
-- @usage data = common.ucs2ToUtf8(ucs2s)
function ucs2ToUtf8(ucs2s)
    local cd = iconv.open("utf8", "ucs2")
    return cd:iconv(ucs2s)
end

--- utf8编码 转化为 unicode小端编码
-- @string utf8s utf8编码数据
-- @return string data,unicode小端编码数据
-- @usage local data = common.utf8ToUcs2(utf8s)
function utf8ToUcs2(utf8s)
    local cd = iconv.open("ucs2", "utf8")
    return cd:iconv(utf8s)
end

--- unicode大端编码 转化为 utf8编码
-- @string ucs2s unicode大端编码数据
-- @return string data,utf8编码数据
-- @usage data = common.ucs2beToUtf8(ucs2s)
function ucs2beToUtf8(ucs2s)
    local cd = iconv.open("utf8", "ucs2be")
    return cd:iconv(ucs2s)
end

--- utf8编码 转化为 unicode大端编码
-- @string utf8s utf8编码数据
-- @return string data,unicode大端编码数据
-- @usage local data = common.utf8ToUcs2be(utf8s)
function utf8ToUcs2be(utf8s)
    local cd = iconv.open("ucs2be", "utf8")
    return cd:iconv(utf8s)
end

--- utf8编码 转化为 gb2312编码
-- @string utf8s utf8编码数据
-- @return string data,gb2312编码数据
-- @usage local data = common.utf8ToGb2312(utf8s)
function utf8ToGb2312(utf8s)
    local cd = iconv.open("ucs2", "utf8")
    local ucs2s = cd:iconv(utf8s)
    cd = iconv.open("gb2312", "ucs2")
    return cd:iconv(ucs2s)
end

--- gb2312编码 转化为 utf8编码
-- @string gb2312s gb2312编码数据
-- @return string data,utf8编码数据
-- @usage local data = common.gb2312ToUtf8(gb2312s)
function gb2312ToUtf8(gb2312s)
    local cd = iconv.open("ucs2", "gb2312")
    local ucs2s = cd:iconv(gb2312s)
    cd = iconv.open("utf8", "ucs2")
    return cd:iconv(ucs2s)
end

--------------------------------------------------------------------------------------------------------
--[[
函数名:ucs2ToGb2312
功能  :unicode小端编码 转化为 gb2312编码,并打印出gb2312编码数据
参数  :
        ucs2s:unicode小端编码数据,注意输入参数的字节数
返回值:
]]
local function testucs2ToGb2312(ucs2s)
    print("ucs2ToGb2312")
    local gb2312num = ucs2ToGb2312(ucs2s)--调用的是common.ucs2ToGb2312,返回的是编码所对应的字符串
    -- print("gb2312  code:",string.toHex(gb2312num))
    print("gb2312  code:" .. string.toHex(gb2312num))
end

--[[
函数名:gb2312ToUcs2
功能  :gb2312编码 转化为 unicode十六进制小端编码数据并打印
参数  :
        gb2312s:gb2312编码数据,注意输入参数的字节数
返回值:
]]
local function testgb2312ToUcs2(gb2312s)
    print("gb2312ToUcs2")
    local ucs2num = gb2312ToUcs2(gb2312s)
    print("unicode little-endian code:" .. string.toHex(ucs2num)) -- 要将二进制转换为十六进制,否则无法输出
end

--[[
函数名:ucs2beToGb2312
功能  :unicode大端编码 转化为 gb2312编码,并打印出gb2312编码数据,
大端编码数据是与小端编码数据位置调换
参数  :
        ucs2s:unicode大端编码数据,注意输入参数的字节数
返回值:
]]
local function testucs2beToGb2312(ucs2s)
    print("ucs2beToGb2312")
    local gb2312num = ucs2beToGb2312(ucs2s) -- 转化后的数据直接变成字符可以直接输出
    print("gb2312 code :" .. string.toHex(gb2312num))
end

--[[
函数名:gb2312ToUcs2be
功能  :gb2312编码 转化为 unicode大端编码,并打印出unicode大端编码
参数  :
        gb2312s:gb2312编码数据,注意输入参数的字节数
返回值:unicode大端编码数据
]]
function testgb2312ToUcs2be(gb2312s)
    print("gb2312ToUcs2be")
    local ucs2benum = gb2312ToUcs2be(gb2312s)
    print("unicode big-endian code :" .. string.toHex(ucs2benum))
end

--[[
函数名:ucs2ToUtf8
功能  :unicode小端编码 转化为 utf8编码,并打印出utf8十六进制编码数据
参数  :
        ucs2s:unicode小端编码数据,注意输入参数的字节数
返回值:
]]
local function testucs2ToUtf8(ucs2s)
    print("ucs2ToUtf8")
    local utf8num = ucs2ToUtf8(ucs2s)
    print("utf8  code:" .. string.toHex(utf8num))

end

--[[
函数名:utf8ToGb2312
功能  :utf8编码 转化为 gb2312编码,并打印出gb2312编码数据
参数  :
        utf8s:utf8编码数据,注意输入参数的字节数
返回值:
]]
local function testutf8ToGb2312(utf8s)
    print("utf8ToGb2312")
    local gb2312num = utf8ToGb2312(utf8s)
    print("gb2312 code:" .. string.toHex(gb2312num))

end

--[[
函数名:gb2312ToUtf8
功能  :gb2312编码 转化为 utf8编码,并打印出utf8编码数据
参数  :
        gb2312s:gb2312s编码数据,注意输入参数的字节数
返回值:
]]
local function testgb2312ToUtf8(gb2312s)
    print("gb2312ToUtf8")
    local utf8s = gb2312ToUtf8(gb2312s)
    print("utf8s code:" .. utf8s)

end

sys.taskInit(function()

    log.info("iconv", PROJECT, VERSION)

    while 1 do
        sys.wait(1000)
        testucs2ToGb2312(string.fromHex("1162")) -- "1162"是"我"字的ucs2编码
        testgb2312ToUcs2(string.fromHex("CED2")) -- "CED2"是"我"字的gb2312编码
        testucs2beToGb2312(string.fromHex("6211")) -- "6211"是"我"字的ucs2be编码
        testgb2312ToUcs2be(string.fromHex("CED2"))
        testucs2ToUtf8(string.fromHex("1162"))
        testutf8ToGb2312(string.fromHex("E68891")) -- "E68891"是"我"字的utf8编码
        testgb2312ToUtf8(string.fromHex("CED2"))
    end
end)

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!

5.2.2 程序烧录

1. 参考 二章节与三章节完成好硬件和软件环境的搭建。

2. 此时按照如下步骤进行烧录程序。

3. 按照如下步骤将 core 文件和 demo 例程烧录。

4. 我们将波特率设置为 2000000,后续就可以看到如下实验效果

5.2.3 日志分析

5.2.3.1 启动日志

因为我们使用 lua 脚本烧录程序到 Air8101 开发板中,芯片启动前期会存在一些日志打印,这些日志并非用户编写的。为了方便用户快速定位到自己实际编写的内容,因此我在 taskInit 函数中,第一行先打印当前的项目名和版本信息,方便用户快速定位。

PROJECT = "iconv_demo"
VERSION = "1.0"
log.info("iconv", PROJECT, VERSION)
5.2.3.2 编码格式转换日志

1. 当前 demo 中,本质上就是让 GB2312、Utf8、ucs2 小端、ucs2 大端模式的编码转换,我们可以利用如下三个网站分析其日志是否正确。

2. 首先我们看第一条代码,其实就是调用 iconv.opencd.iconv 实现编码转换。这里是将 ucs2 小端编码的 "我" 字转换为 Gb2312 编码的 "我"。

function ucs2ToGb2312(ucs2s)
    local cd = iconv.open("gb2312", "ucs2")
    return cd:iconv(ucs2s)
end

local function testucs2ToGb2312(ucs2s)
    print("ucs2ToGb2312")
    local gb2312num = ucs2ToGb2312(ucs2s)--调用的是common.ucs2ToGb2312,返回的是编码所对应的字符串
    -- print("gb2312  code:",string.toHex(gb2312num))
    print("gb2312  code:" .. string.toHex(gb2312num))
end

testucs2ToGb2312(string.fromHex("1162")) -- "1162"是"我"字的 ucs2 小端编码

3. 我们利用 https://www.toolhelper.cn/EncodeDecode/UnicodeChinese 工具查看一下 ucs2 编码的"我"字具体数值,需要注意,这里最终转换是大端存储的结果,我们后续还需要手动将其变成小端存储,因此上述输入的是 "1162" 而不是 "6211"

4. 我们查阅一下 https://www.toolhelper.cn/Encoding/GB2312 GB2312 编码表,看一下 "我" 字的具体数值。最终可知, GB2312 编码下的 "我" 字具体值为 CED0 + 2 = CED2。

5. 通过上面的分析,我们再来看看打印结果,发现最终结果确实为 CED2,无误。

6. 后续测试方法与上述一致。

六、总结

本文演示通过对不同字符编码格式的介绍和实际转换操作,使得大家更加了解了字符编码在计算机通信中的重要性,以及如何在不同编码格式之间进行转换。演示中使用的工具和硬件环境为 Air780E 核心板和 Lua 编程语言,通过实际编码转换的例子,展示了字符编码转换在实际应用中的重要性。

在演示中,我们介绍了 Unicode(小端和大端)、GB2312 和 UTF-8 等常见编码格式,并展示了如何在 Lua 中使用 iconv 库进行编码转换。通过这个演示,大家可以了解到不同编码格式之间的差异,以及在不同场景下如何选择合适的编码格式。

此外,我们还提供了一个简单的 Lua 脚本,用于在不同编码之间进行转换测试,并打印出转换后的编码数据。这个脚本可以帮助大家更好地理解和掌握字符编码转换的原理和方法。

七、常见问题

7.1 部分编码不能互相转换

当前仅支持如下 8 种编码转换 :

  • ucs2->utf8
  • ucs2->gb2312
  • gb2312->ucs2
  • gb2312->ucs2be
  • ucs2be->utf8
  • ucs2be->gb2312
  • utf8->ucs2
  • utf8->ucs2be

7.2 如何检测当前转换结果是否正确

我们可以利用如下三个网站查看 GB2312 、ucs2 、utf8 的转换结果

7.3 ucs2 与 ucs2be 大小端问题

ucs2be 是大端存储,也就是高字节数据存储在高位,因此中文字符 "你好" 对应的是 "4f60597d"

ucs2 是小端存储,也就是高字节数据存储在低位,因此中文字符 "你好" 对应的是 "604f7d59"

八、扩展

后续扩展在此补充,敬请期待......