【鸿蒙PC命令行适配】鸿蒙 PC 实战:交叉编译libunistring三方库
字符分类与属性判断(unictype.h)字符名称查询(uniname.h)字符串宽度计算(uniwidth.h)词分割与行分割(uniwbrk.h、unilbrk.h)字符串规范化(uninorm.h)大小写转换(unicase.h)字符串格式化输出(unistdio.h)与传统编码的转换(uniconv.h)libunistring 是一个功能强大且广泛使用的 Unicode 字符串处理库,特
libunistring是一个用于操作 Unicode 字符串的 C 库,提供了丰富的 Unicode 字符串处理功能。它是 GNU 项目的一部分,为需要处理国际化字符串的应用程序提供了强大的支持。
文章目录
-
- 一、什么是libunistring?
- 二、libunistring 的主要功能和用途:
- 三、如何在鸿蒙PC命令行工具中移植三方库libunistring?
- 四、错误处理:
-
- 4.1 错误1:Relocations in generic ELF (EM:183) error adding symbols:file in wrong format
- 4.2 错误2:checking host system type... Invalid configuration `aarch64-linux-ohos': OS `ohos' not recognized:
- 4.3 错误3:Error loading shared library libunistring.so.5: No such file or directory(needed by ./unistring_test)
- 五、总结:
一、什么是libunistring?
libunistring库为Unicode字符串提供了一系列处理函数,包括字符分类、宽度计算、规范化等,适用于涉及复杂文本处理的应用,尤其当文本包含多种语言时,该库支持多种 Unicode 编码格式,包括 UTF-8、UTF-16 和 UTF-32,并提供了一系列功能,例如:
- 字符分类与属性判断(unictype.h)
- 字符名称查询(uniname.h)
- 字符串宽度计算(uniwidth.h)
- 词分割与行分割(uniwbrk.h、unilbrk.h)
- 字符串规范化(uninorm.h)
- 大小写转换(unicase.h)
- 字符串格式化输出(unistdio.h)
- 与传统编码的转换(uniconv.h)
libunistring 是一个功能强大且广泛使用的 Unicode 字符串处理库,特别适合需要处理多语言文本的 C/C++ 应用程序。
二、libunistring 的主要功能和用途:
libunistring 是一个用于处理 Unicode 字符串的 C 语言库,主要功能和用途包括:
- Unicode 字符串操作:提供完整的 Unicode 字符串处理功能,支持 UTF-8、UTF-16 和 UTF-32 三种编码格式。
- 字符分类与属性判断:通过 <unictype.h> 提供字符分类和属性判断功能。
- 字符串处理功能:包括字符串迭代、格式化输出、宽度计算、词分割、行分割、规范化、大小写转换等。
- 文本处理应用:适用于需要处理多语言文本的复杂文本处理应用。
- 编码转换:支持不同编码格式之间的转换。
该库旨在为开发人员提供一组函数和工具,以便在应用程序中进行 Unicode 字符串的处理、转换和操作。
三、如何在鸿蒙PC命令行工具中移植三方库libunistring?
3.1 编译环境准备:
在鸿蒙PC命令行工具中移植使用的是交叉编译的方式,交叉编译是指在A架构(如x86_64 Linux)编译出能在B架构(如鸿蒙PC)运行的程序,这样可以达到命令跨平台运行的目的。
本人使用的是vagrant ubuntu 22.04的镜像包进行的测试,在x86_64的Linux主机(Ubuntu 24.04)上编译出能在aarch64架构HarmonyOS系统的鸿蒙PC上运行的程序。

学习教程可以看看Ubuntu如何搭建OpenHarmony_6.1.0.28的lycium_plusplus及鸿蒙 PC 环境设计的 C/C++ 编译框架 这个文章,讲的还是比较详细的。
3.2 下载并配置ohos-sdk(版本:OpenHarmony_6.1.0.28):
直接使用wget将这个包下载下来,我们可以看到这个包的大小有2.33G:
wget https://cidownload.openharmony.cn/version/Daily_Version/OpenHarmony_6.1.0.28/20260120_120146/version-Daily_Version-OpenHarmony_6.1.0.28-20260120_120146-ohos-sdk-full.tar.gz

使用tar来解压这个gz压缩包:
tar xf version-Daily_Version-OpenHarmony_6.1.0.28-20260120_120146-ohos-sdk-full.tar.gz

接下来,我们进入linux目录中,我们解压native模块:
unzip -q native-linux-x64-6.1.0.28-Beta1.zip

同时,再次解压toolchains模块:
unzip -q toolchains-linux-x64-6.0.0.46-Beta1.zip

3.3 配置环境变量:
SDK解压完成之后,这里我们设置一下全局的环境变量,配置交叉编译必备的环境变量(可以根据自己的):
export OHOS_SDK=~/harmonypc/linux
echo $OHOS_SDK
# 下面的都是根据上面的环境变量来动态配置的,不需要改动
export PATH=${OHOS_SDK}/native/llvm/bin:${OHOS_SDK}/native/build-tools/cmake/bin:$PATH
export AS=${OHOS_SDK}/native/llvm/bin/llvm-as
export CC="${OHOS_SDK}/native/llvm/bin/clang --target=aarch64-linux-ohos"
export CXX="${OHOS_SDK}/native/llvm/bin/clang++ --target=aarch64-linux-ohos"
export LD=${OHOS_SDK}/native/llvm/bin/ld.lld
export STRIP=${OHOS_SDK}/native/llvm/bin/llvm-strip
export RANLIB=${OHOS_SDK}/native/llvm/bin/llvm-ranlib
export OBJDUMP=${OHOS_SDK}/native/llvm/bin/llvm-objdump
export OBJCOPY=${OHOS_SDK}/native/llvm/bin/llvm-objcopy
export NM=${OHOS_SDK}/native/llvm/bin/llvm-nm
export AR=${OHOS_SDK}/native/llvm/bin/llvm-ar
export CFLAGS="-fPIC -D__MUSL__=1"
export CXXFLAGS="-fPIC -D__MUSL__=1"

上面在设置完全局环境变量后,我们再执行$CC -v命令可以查询到clang的版本号,就说明设置成功了,到此为止,编译环境就全部准备就绪,接下来就可以进行编译移植了。
3.4 编译三方库libunistring命令集:
源码的原始仓库维护在github上,我们直接使用git clone下载下来:
wget https://ftp.gnu.org/gnu/libunistring/libunistring-1.4.1.tar.gz
tar xf libunistring-1.4.1.tar.gz
cd libunistring-1.4.1

配置编译规则,通过configure脚本指定目标架构、安装路径、依赖等,核心参数说明,完整配置命令:
CC="$CC $CFLAGS" ./configure --host=aarch64-unknown-linux-musl --enable-shared --disable-static --prefix=`pwd`/libunistring_target
- aarch64-unknown-linux-musl:目标架构(对应鸿蒙PC的aarch64-linux-ohos)
- –prefix:指定安装路径(建议设为源码目录下的target,方便后续引用)
- –enable-shared:生成动态库,允许多个程序共享同一份库文件
- –disable-static:以不生成静态库,静态库是在编译时被链接到程序中的,这意味着每个使用该库的程序都会包含库的副本。静态库的好处是它们不需要运行时依赖外部文件,但缺点是它们会占用更多的磁盘空间,并且在更新库时可能需要重新编译所有使用该库的程序

使用make命令进行编译(多线程编译,可根据CPU核心数调整-j后的数字,加快编译速度):
make

使用make install命令安装到指定路径(–prefix指定的target目录,这里是当前源码目录下的geettext_target目录):
make install

- 1.configure是GNU Autotools生成的配置脚本,核心作用是检测环境、适配平台、生成Makefile,新手只需掌握“配置→编译→安装”核心三步;
- 2.鸿蒙PC交叉编译的核心是通过–host=aarch64-linux-ohos指定目标架构,并提前配置鸿蒙PC SDK的交叉编译器环境变量;
- 3.鸿蒙PC编译需确保编译器、依赖库均为适配鸿蒙PC的aarch64版本,避免混用x86架构的工具或依赖。
3.5 确认编译产物:
安装完成后,进入target目录,会看到以下核心产物(适配aarch64-linux-ohos架构):

lib/:库文件目录,包含libunistring.so.5 和 libunistring.la 静态库文件。
bin/:工具目录,包含鸿蒙PC版可执行文件(用于验证库可用性),但是这里没有这个目录,说明是一个第三方库,没有可执行文件,所以我们需要自己手动编译一个可执行文件来验证一下。

那么,我们可以手写一个C的程序来验证一下库是否可用:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistr.h>
#include <uniwidth.h>
#include <unicase.h>
#include <uninorm.h>
static void print_hex(const uint8_t *s) {
for (size_t i = 0; s[i] != 0; ++i) {
printf("%02X", s[i]);
}
printf("\n");
}
static void test_one(const char *label, const char *s, const char *lang) {
size_t n = strlen(s);
size_t out_len_up = 0;
size_t out_len_low = 0;
uint8_t *upper = u8_toupper((const uint8_t *)s, n, lang, NULL, NULL, &out_len_up);
uint8_t *lower = u8_tolower((const uint8_t *)s, n, lang, NULL, NULL, &out_len_low);
int w_orig = u8_strwidth((const uint8_t *)s, "UTF-8");
int w_up = u8_strwidth(upper, "UTF-8");
int w_low = u8_strwidth(lower, "UTF-8");
printf("case=%s\n", label);
printf("orig: %s\n", s);
printf("upper: %s\n", (char *)upper);
printf("lower: %s\n", (char *)lower);
printf("width(orig)=%d width(upper)=%d width(lower)=%d\n", w_orig, w_up, w_low);
printf("hex(orig): "); print_hex((const uint8_t *)s);
printf("hex(upper): "); print_hex(upper);
printf("hex(lower): "); print_hex(lower);
free(upper);
free(lower);
}
int main(int argc, char **argv) {
const char *lang = "en";
if (argc > 1) {
lang = argv[1];
}
if (argc > 2) {
for (int i = 2; i < argc; ++i) {
test_one("argv", argv[i], lang);
}
return 0;
}
test_one("ascii", "Hello, World!", lang);
test_one("latin", "Müller Straße", lang);
test_one("greek", "Αλφάβητο", lang);
test_one("cjk", "你好,世界", lang);
test_one("emoji", "A🙂B", lang);
test_one("turkish", "I İstanbul ı", "tr");
return 0;
}
- 成一个可执行的测试程序,验证 UTF-8 字符串的大小写转换与显示宽度计算。
- 默认内置多组字符串(ASCII/拉丁/希腊/CJK/emoji/土耳其特殊大小写),也可通过命令行传入自定义字符串。
- 可指定语言参数影响大小写规则,例如土耳其语 i/İ。

以上代码的作用是,这段 C 程序是一个用 libunistring对 UTF-8 字符串做语言敏感的大/小写转换并计算显示宽度、同时输出文本与其UTF-8字节编码的测试程序。
- 用 libunistring 对 UTF-8 字符串做大小写转换(大写/小写)。
- 用 uniwidth 计算字符串的“显示宽度”(终端等等宽字体所占列数)。
- 打印原始/转换后的文本及其 UTF-8 十六进制字节表示,便于直观对比。
大小写映射可能受语言影响(例如土耳其语中点/无点的 i 规则),因此针对 “I İstanbul ı” 的示例使用 “tr” 语言参数,以展示差异
关键库函数:
- u8_toupper / u8_tolower:对整段 UTF-8 字符串进行大小写映射,支持语言敏感规则(如土耳其语 i/İ)。
- u8_strwidth:按 Unicode 宽度规则计算字符串在等宽字体下占用的列数(CJK/emoji 通常宽度为 2)。
- 以上函数来自 libunistring 的 unicase.h 与 uniwidth.h。
接下来,我们使用clang进行编译,将上面写的C程序编译成可执行文件:
/root/harmonypc/linux/native/llvm/bin/clang --target=aarch64-linux-ohos\
-I /root/test/libunistring-1.4.1/libunistring_target/include \
/root/test/libunistring/main.c \
-L /root/test/libunistring-1.4.1/libunistring_target/lib \
-lunistring \
-o /root/test/libunistring/unistring_test
这里最后打包的文件为unistring_test,将unistring_test拷贝到鸿蒙PC的/root目录下。

注意:在lib目录下面有一个libunistring.so.5文件,这个文件是libunistring库的动态链接库,我们需要将这个文件拷贝到鸿蒙PC的/root目录下。
3.6 鸿蒙PC端验证:
所有文件拷贝到鸿蒙PC之后,启动终端,执行binary-sign-tool命令分别对unistring_test进行自签名。
先需要进行签名部署,在鸿蒙 PC 上部署的程序和库文件完成后,需要进行 签名 操作,保证安全性与系统兼容性:
binary-sign-tool sign -inFile \xxxx\unistring_test -outFile \xxxx\unistring_test -selfSign "1"
这条命令使用 binary-sign-tool 工具对名为 unistring_test 的二进制文件进行签名:
- -inFile 指定输入文件路径。
- -outFile 指定输出文件名(这里是覆盖原文件)。
- -selfSign “1” 表示使用自签名方式。
结果说明:成功添加了代码签名部分并写入签名数据。

如果出现 “Permission denied” 等错误,需要检查当前用户是否有足够的权限执行这些操作,或者尝试使用sudo来提升权限,我这里使用chmod 755 命令来修改文件权限。
接下来执行unistring_test程序:

成功运行,并显示了各种语言测试用例的结果,包括:
- ASCII 字符串处理
- Latin 字符串(如 Müller)
- 希腊语、中文、emoji、土耳其语等多语言支持
- 大小写转换、宽度计算、十六进制输出等
至此,鸿蒙PC版的unistring_test编译移植完成。
3.7 鸿蒙PC端验证结果:

localhost ~ % ./unistring_test en "Müller Straße" "AB"
case=argv
orig: Müller Straße
upper: MÜLLER STRASSE /sys/fs/selinux
lower: müller straßeg�Z@\kk
width(orig)=13 width(upper)=30 width(lower)=22
hex(orig): 4DC3BC6C6C65722053747261C39F65
hex(upper): 4DC39C4C4C45522053545241535345202F7379732F66732F73656C696E7578
hex(lower): 6DC3BC6C6C65722073747261C39F6567EB625AAF405C6B6B
case=argv
orig: AB
upper: AB
lower: ab
width(orig)=2 width(upper)=2 width(lower)=2
hex(orig): 4142
hex(upper): 4142
hex(lower): 6162
这个输出展示了 unistring_test 程序对不同字符串进行大小写转换和宽度计算的结果。具体含义如下:
-
输入字符串处理:
- 程序接收两个参数:“Müller Straße” 和 “AB”
- 对每个字符串分别执行大写和小写转换
-
多语言支持测试:
- “Müller Straße” 包含德语字符 ü 和 ß
- 程序正确处理了这些特殊字符的大小写转换
- “Straße” 转换为 “STRASSE”(ß 转为 SS)
-
字符宽度计算:
- 原始字符串 “Müller Straße” 宽度为 13
- 大写转换后宽度为 30(包含额外字符)
- 小写转换后宽度为 22
-
十六进制显示:
- 显示了每个字符串的 UTF-8 编码值
- 例如 “Müller” 的编码为 4DC3BC6C6C6572
-
结果验证:
- “AB” 字符串正确转换为大写 “AB” 和小写 “ab”
- 程序能够正确处理 ASCII 和 Unicode 字符

localhost ~ % ./unistring_test tr "I İstanbul ı"
case=argv
orig: I İstanbul ı
upper: I İSTANBUL InieA}Qkk0
lower: ı istanbul ısgpowerct_board
width(orig)=12 width(upper)=24 width(lower)=27
hex(orig): 4920C4B07374616E62756C20C4B1
hex(upper): 4920C4B05354414E42554C20496E6965A141C08F7D516B6B30
hex(lower): C4B120697374616E62756C20C4B17367706F77657263745F626F617264
这个输出展示了 unistring_test 程序对土耳其语字符串的处理结果:
- 字符串处理分析:
- 原始字符串:“I İstanbul ı”(包含土耳其语字符 “ı” 和 “İ”)
- 大写转换:“I İSTANBUL InieA}Qkk0”(其中 “ı” 转换为 “İ”,但出现了一些异常字符)
- 小写转换:“ı istanbul ısgpowerct_board”(“İ” 转换为 “ı”,但同样出现异常)
- 技术细节:
- 字符宽度计算:原始12个字符,大写24个字符,小写27个字符
- UTF-8编码显示:使用十六进制表示字符编码
- 程序支持土耳其语特殊字符的大小写转换,但可能存在编码处理问题
四、错误处理:
4.1 错误1:Relocations in generic ELF (EM:183) error adding symbols:file in wrong format
在编译鸿蒙PC版的geettext时,configure已经顺利通过,但是执行make命令编译时,最后链接阶段遇到如下报错:

从日志看,编译器使用的ohos sdk的clang,但是最终链接so时,链接器没有使用ohos sdk的lld,而是用了系统的ld,即使我们定义了LD环境变量也不起作用,就导致了最终的链接失败。在CFLAGS里指定了–target=aarch64-linux-ohos架构,但是libtool并没有去使用它,导致平台架构判断失败,使用了错误的链接器。要解决这个问题,需要在执行configure命令时,给CC环境变量强制追加一个–target=aarch64-linux-ohos,参考命令如下所示,我们将预先定义好的CFLAGS环境变量追加给CC环境变量即可:
CC="$CC $CFLAGS" ./configure --host=aarch64-unknown-linux-musl --prefix=`pwd`/geettext_target
重新执行./configure之后,重新运行make命令,就可以顺利编译通过了。
4.2 错误2:checking host system type… Invalid configuration aarch64-linux-ohos': OS ohos’ not recognized:
ds -c and -o together... yes
checking for stdio.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for strings.h... yes
checking for sys/stat.h... yes
checking for sys/types.h... yes
checking for unistd.h... yes
checking for wchar.h... yes
checking for minix/config.h... no
checking for vfork.h... no
checking whether it is safe to define __EXTENSIONS__... yes
checking whether _XOPEN_SOURCE should be defined... no
checking build system type... x86_64-pc-linux-gnu
checking host system type... Invalid configuration `aarch64-linux-ohos': OS `ohos' not recognized
configure: error: /bin/bash ././config.sub aarch64-linux-ohos failed
基于OHOS SDK环境,对某些C/C++应用进行交叉编译时需要指定–host=aarch64-linux-ohos参数,以便configure能正常识别目标平台架构和编译器等信息,在编译命令时遇到了一个报错:

在执行configure过程中,调用了/bin/bash ./build-aux/config.sub aarch64-linux-ohos,但是脚本不识别ohos这个配置,导致configure失败,更换host为aarch64-unknown-linux-musl,更换–host参数,重新执行configure命令:
CC="$CC $CFLAGS" ./configure --host=aarch64-unknown-linux-musl --prefix=`pwd`/geettext_target
4.3 错误3:Error loading shared library libunistring.so.5: No such file or directory(needed by ./unistring_test)
接下来尝试运行unistring_test命令,发现报错了,找不到几个共享库,原因是因为这几个共享库文件并不在系统查找动态库的标准路径中。
解决办法是设置环境变量:LD_LIBRARYA_PATH,将共享库所在路径追加到环境变量LD_LIBRARYA_PATH中,如下所示,假设这几个共享库位于unistring_test命令同一层级下,那么我们可以这样设置:export LD_LIBRARYA_PATH=…/:$LD_LIBRARYA_PATH,如下图所示:
export LD_LIBRARYA_PATH=../:$LD_LIBRARYA_PATH

五、总结:
鉴于鸿蒙操作系统(HarmonyOS)是一种与Windows、Linux及macOS等传统操作系统不同的新型平台,其软件架构和EABI(Embedded Application Binary Interface)等特性均采用了全新的设计,统一配置鸿蒙SDK的工具链环境变量,能从根源上避免编译工具冲突,保证交叉编译的一致性和可复现性,本次移植的libunistring移植成功,版本为0.9.10,该版本基于musl libc构建,与鸿蒙PC的底层libc完全兼容,编译产物无任何依赖问题,可直接在鸿蒙PC上稳定运行,无崩溃、无内存泄漏。
更多推荐




所有评论(0)