欢迎加入鸿蒙PC开发者社区,共同打造开发者工具生态:鸿蒙PC开发者社区:https://harmonypc.csdn.net/

项目开源地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_jieba

欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper

这篇文章记录的是一次把 Python 中文分词三方库 jieba 接入 HarmonyOS PC / 鸿蒙 PC 应用的完整过程,包括踩到的几个真实问题和最终怎么修好的。

jieba 和很多桌面软件不一样,它本身不是一个现成的 GUI 程序,而是 Python 生态里最常用的中文分词库之一。它可以做精确分词、全模式分词、搜索引擎分词,也能做词性标注和关键词提取。也就是说,这次适配的重点不是“把一个窗口搬到鸿蒙 PC 上”,而是要解决一个更通用的问题:

鸿蒙 PC 应用如何调用 HNP Python 里的 Python 三方库,并把它做成真实用户可以操作的工具?

最终我们在项目里新增了一个 examples/harmony_pc/ 示例工程。它是一个面向鸿蒙 PC 用户的中文分词工具:用户可以直接输入或从文件载入一段中文文本,选择分词 / 词性标注 / 关键词提取,再把结果导出成文本文件。

在这里插入图片描述

一、项目背景:jieba 是什么

jieba(结巴分词)是 Python 生态中使用面非常广的中文分词库,几乎是中文 NLP 入门必用的工具。它常见的能力包括:

  • 精确模式分词:尽量准确地切分句子,适合文本分析;
  • 全模式分词:把句子里所有能成词的片段都切出来;
  • 搜索引擎模式分词:在精确模式基础上,对长词再切一刀,适合建索引;
  • 词性标注:给每个词标上名词、动词、地名等词性;
  • 关键词提取:用 TF-IDF 或 TextRank 算法从文本里挑出关键词。

这次鸿蒙 PC 示例就是把 jieba 包装成一个普通用户能直接用的小工具:粘贴一段中文,选一个操作,点一下就能看到分词结果,还能把结果导出。

这里有一个对适配非常友好的关键点:jieba 是纯 Python 库,只依赖 Python 标准库,没有 NumPy、OpenBLAS、FFmpeg 这类需要本地编译的依赖(它的 paddle 模式是可选的,本示例不启用)。这一点后面会反复用到,它直接决定了我们可以走一条比图像、科学计算类库更简单、更稳的适配路线。

二、路线选择:不重写 jieba,而是通过 HNP Python 调用

适配前先分析了两条路线。

第一条是把中文分词能力用 ArkTS 或 Native 重写。中文分词涉及词典、HMM 模型、Viterbi 解码、TF-IDF / TextRank 等一整套算法,重写成本高、易出错,不适合第一版适配。

第二条是保留 Python 三方库,让鸿蒙应用通过 HNP Python 调用 jieba。鸿蒙 PC 上可以准备 HNP Python 环境,应用侧只负责输入文本、组织参数、展示结果,真正的分词仍然交给 Python。

最终采用第二条路线:

鸿蒙 ArkTS 页面
  -> JiebaHnpClient 写入 JSON 请求
  -> Native/N-API 模块启动 HNP Python(放在 TaskPool 工作线程)
  -> jieba_hnp_bridge.py import jieba
  -> jieba 执行分词 / 词性标注 / 关键词提取
  -> Python stdout 返回 JSON
  -> ArkTS 解析结果并导出文件

这条路线的好处是:

  • 不需要重写 jieba;
  • 复用 Python 生态里成熟的中文分词能力;
  • 适合 Python 三方库向鸿蒙 PC 工具化迁移;
  • 后续要加新能力时,主要扩展 Python bridge 和 ArkTS client 即可;
  • Native 层只做桥接,不承担分词业务逻辑。

而且因为 jieba 是纯 Python,没有平台相关的编译产物,我们还能再进一步:把 jieba 的源码和词典直接打包进 HAP,让设备端连 pip install jieba 都省掉。这一点在第五节细说。

三、先确认 HNP Python 能跑通(必须先装「Python 安装器」)

在写鸿蒙 UI 之前,第一步不是画页面,而是确认目标设备上的 HNP Python 能不能正常启动。这个 HAP 示例负责页面、文件选择、Native 桥接和调用流程,但真正执行分词的,是设备里的 HNP Python。

⚠️ 重点:鸿蒙 PC 上默认不一定有可供应用调用的 Python 环境。普通用户在使用这个软件之前,必须先在鸿蒙 PC 的应用市场里搜索并安装「Python 安装器」,再用它把 Python 运行环境装到鸿蒙 PC 上。 没有这一步,应用启动 HNP Python 会直接失败。

1. 在鸿蒙 PC 应用市场安装「Python 安装器」

打开鸿蒙 PC 的应用市场,搜索 Python 安装器 并安装。安装完成后打开它,按页面提示完成 Python 运行环境安装。这一步做完,设备上才会出现本示例默认使用的 HNP Python 路径:

/data/service/hnp/python.org/python_3.12/bin/python3.12

在这里插入图片描述

2. 确认 HNP Python 能正常启动

在鸿蒙 PC 的 HiShell 或系统终端里执行:

/data/service/hnp/python.org/python_3.12/bin/python3.12 --version

能看到 Python 版本号,说明基础环境就绪。如果提示文件不存在,通常是「Python 安装器」还没装,或者还没完成 Python 环境初始化。

3. jieba 不需要在设备上单独安装

这一点和大多数 Python 三方库适配不同。像 imageio、fontTools 这类库,通常还要用同一个 HNP Python 去 pip install。但 jieba 是纯 Python 库,我们直接把它随应用打包了,所以设备端不需要 pip install jieba,只要 python3.12 能跑起来就行。

如果你确实想用设备端 pip 安装的 jieba,把示例里 rawfile/python/jieba/ 目录删掉即可,worker 会回退到 HNP Python 里已安装的 jieba。

4. 本地命令行自检

在接 ArkTS 之前,可以先在鸿蒙 PC 的 HNP Python 里跑一遍命令行自检:

cd examples/harmony_pc
export PYTHONDONTWRITEBYTECODE=1
export JIEBA_CACHE_DIR=./jieba-cache
python3.12 jieba_hnp_demo.py

如果这里失败,说明问题还在 Python 环境阶段,鸿蒙应用里调用也不会成功。先把 HNP Python 侧跑通,后面的 ArkTS 和 Native 桥接才有意义。

四、新增鸿蒙 PC 示例工程

适配完成后,项目新增了鸿蒙 PC 示例目录:

examples/harmony_pc/
  AppScope/
  entry/
  hvigor/
  build-profile.json5
  oh-package.json5
  jieba_hnp_bridge.py
  jieba_hnp_demo.py
  run_hishell.sh
  sample_request.json

真正的鸿蒙应用工程就在 examples/harmony_pc。几个关键文件:

entry/src/main/ets/pages/Index.ets
entry/src/main/ets/jieba/JiebaHnpClient.ets
entry/src/main/ets/native/JiebaHnpNative.ets
entry/src/main/ets/util/RawFileSync.ets
entry/src/main/cpp/napi_init.cpp
entry/src/main/cpp/types/libjieba_hnp_bridge/
entry/src/main/resources/rawfile/python/jieba_hnp_bridge.py
entry/src/main/resources/rawfile/python/jieba/        ← 打包进来的 jieba 源码与词典

各文件职责很清楚:

  • Index.ets:鸿蒙 PC 页面,负责输入 / 载入文本、选择操作、展示结果、导出;
  • JiebaHnpClient.ets:ArkTS 业务封装,把分词操作变成 JSON 请求;
  • JiebaHnpNative.ets:声明 Native 模块方法,并提供 TaskPool 并发函数;
  • RawFileSync.ets:负责 rawfile 同步、URI 文件复制和沙箱文件读写;
  • napi_init.cpp:通过 N-API 暴露 runRequestJson(),并启动 HNP Python;
  • jieba_hnp_bridge.py:真正 import jieba 并执行分词任务的 Python worker。

这里没有改写上游 jieba 源码,而是在外面加了一层鸿蒙 PC 调用壳。边界清晰:jieba 保持原有 Python 库形态,鸿蒙适配逻辑集中在 examples/harmony_pc/

在这里插入图片描述

五、把 jieba 打包进 HAP:纯 Python 库的独特优势

这是这次适配里和别的 Python 三方库最不一样的地方。

因为 jieba 没有任何需要编译的扩展,我们可以把它的源码和词典直接放进应用资源里,随 HAP 一起分发。这样做的好处是:用户装好应用就能用,不依赖设备能不能联网、能不能 pip。

打包内容放在:

entry/src/main/resources/rawfile/python/jieba/

应用启动时 JiebaHnpClient.prepare() 会把整个 python/ 目录(含 jieba 包)同步到应用沙箱;Python worker 在 import jieba 前,把脚本所在目录插入 sys.path,于是命中的就是这份打包的 jieba:

def _ensure_bundled_jieba_on_path() -> None:
    bridge_dir = os.path.dirname(os.path.abspath(__file__))
    if bridge_dir not in sys.path:
        sys.path.insert(0, bridge_dir)

为了控制体积(jieba 完整包含词典约 37MB),打包时排除了两类文件,最终约 18MB:

  • lac_small/:paddle 模式用的模型,本示例不启用,排除后省约 12MB;
  • 各模块的 *.p 文件:jieba 仅在 Jython 上用它们,鸿蒙的 CPython 走 .py 版本,排除后再省约 7MB。

更新这份打包的 jieba(比如换版本)时,从仓库根目录重新同步即可:

cd /path/to/jieba
rm -rf examples/harmony_pc/entry/src/main/resources/rawfile/python/jieba
cp -R jieba examples/harmony_pc/entry/src/main/resources/rawfile/python/jieba
rm -rf examples/harmony_pc/entry/src/main/resources/rawfile/python/jieba/lac_small
find examples/harmony_pc/entry/src/main/resources/rawfile/python/jieba -name '*.p' -type f -delete

还有一个沙箱里的小细节:jieba 第一次加载会把词典缓存写成 jieba.cache。沙箱默认临时目录可能受限,所以 worker 通过环境变量 JIEBA_CACHE_DIR 把缓存指到应用沙箱里的可写目录,避免首次加载失败。

六、ArkTS 页面:从测试 demo 改成真实用户流程

页面拆成几个清楚的区域:输入文本、操作选择、参数、开始处理、处理结果、导出结果。

用户的真实使用顺序是:

  1. 在「输入文本」框直接输入 / 粘贴中文,或点「从文件载入」选择一个 .txt
  2. 在「操作」里选精确模式 / 全模式 / 搜索引擎 / 词性标注 / 关键词·TF-IDF / 关键词·TextRank;
  3. 分词类可切换「新词发现(HMM)」,关键词类可调「关键词数量」;
  4. 点「开始处理」,结果显示在下方;
  5. 点「导出结果」,通过保存选择器选择位置,把结果写成文本文件。

「从文件载入」和「导出结果」都走鸿蒙文件选择器,返回的是 URI。Python 三方库更习惯处理真实文件路径,所以这里统一按这个流程处理文件:

用户选择的文件 URI
  -> RawFileSync.copyFile() 复制到应用沙箱
  -> Python 读取沙箱文件
  -> Python 写入沙箱输出
  -> RawFileSync.copyFile() 复制到用户选择的保存 URI

页面不把内部沙箱路径暴露给用户,用户看到的是「已就绪」「正在处理」「处理完成」这类自然状态。

在这里插入图片描述

七、ArkTS Client:把分词操作变成 JSON 请求

JiebaHnpClient.ets 是应用侧调用 Python 的封装层。它不直接分词,而是组织请求、准备目录、调用 Native、解析返回值。页面只需要调用 cut()tag()keywords() 这几个方法。

比如精确分词会生成这样的 JSON 请求:

{
  "op": "cut",
  "text": "我来到北京清华大学,热爱自然语言处理和中文分词技术。",
  "mode": "accurate",
  "hmm": true,
  "output": "/应用沙箱/outputs/xxx-result.txt"
}

关键词提取会生成:

{
  "op": "keywords",
  "text": "……",
  "method": "tfidf",
  "topK": 20
}

prepare() 负责建目录并把打包的 jieba 同步到沙箱。因为词典较大(约 18MB),同步只在每个会话里做一次,避免每次请求都重复复制。

八、Native/N-API:启动 HNP Python 进程

ArkTS 不能直接 import jieba,中间需要 Native/N-API 桥接。napi_init.cpp 暴露了 runRequestJson(pythonPath, bridgeScriptPath, requestJsonPath, workingDir),它拼出 HNP Python 启动命令并捕获 stdout。

这里和图像 / 科学计算类库有个明显区别:jieba 没有 NumPy / OpenBLAS 依赖,所以不会触发 SIGSYS(SYS_SECCOMP) 这类沙箱崩溃,Native 层也就不需要设置 OPENBLAS_NUM_THREADS 等一堆规避线程的环境变量。 命令很干净,核心就是:

cd <workingDir>
PYTHONDONTWRITEBYTECODE=1 JIEBA_CACHE_DIR=<workingDir>/jieba-cache \
  <pythonPath> <bridgeScriptPath> --request <requestJsonPath> 2>&1

成功时返回 JSON(含 ok、词数、分词结果、预览文本等),失败时也返回 JSON(含错误类型、错误信息、traceback),方便页面展示。

九、Python bridge:真正调用 jieba

真正分词的是 jieba_hnp_bridge.py,它读取 JSON 请求,按 op 分发:

OPS = {
    "demo": op_demo,
    "cut": op_cut,
    "tag": op_tag,
    "keywords": op_keywords,
}

分词对应:

words = jieba.lcut(text, cut_all=False, HMM=hmm)   # 精确模式
words = jieba.lcut(text, cut_all=True)              # 全模式
words = jieba.lcut_for_search(text)                # 搜索引擎模式

词性标注用 jieba.posseg,关键词提取用 jieba.analyse(TF-IDF 与 TextRank)。worker 最后把结果用 print(json.dumps(...)) 打到 stdout,Native 捕获后返回给 ArkTS。

十、真机调试踩的三个坑(这部分才是“实战”)

代码写完、能构建,不代表真机上就能用。把应用装到鸿蒙 PC 真机上点「开始处理」,我们连续踩了三个坑,逐个修好后才真正可用。

坑一:ModuleNotFoundError: No module named ‘jieba’

第一次真机运行,点「开始处理」后界面直接弹出红色「处理失败」,结果区是一段 Python traceback:

File ".../jieba-hnp/python/jieba_hnp_bridge.py", line 39, in <module>
    import jieba
ModuleNotFoundError: No module named 'jieba'

原因很清楚:设备上的 HNP Python 没有装 jieba。这正好印证了前面那条铁律——HNP Python 环境里必须能 import 到这个库。

修复方式就是第五节讲的:既然 jieba 是纯 Python,干脆把它打包进 HAP,再在 import jieba 前把脚本目录插进 sys.path。改完之后,应用不依赖设备 pip,开箱即用。

坑二:界面弹出「应用无响应」(ANR)

打包 jieba 之后分词能跑了,但很快又遇到一个体验问题:连续点几次「开始处理」,系统弹出「jieba 分词没有响应 / 是否将其关闭?」。

原因是:Native 的 runRequestJson 内部用 popen 启动 HNP Python 并同步等待结果,而我们一开始是直接在 UI 线程调用它的。jieba 首次加载词典、Python 进程启动都要时间,UI 线程被卡住,系统就判定应用无响应。

修复方式是把这个同步调用放到 ArkTS 的 TaskPool 工作线程:

@Concurrent
export function runRequestConcurrent(
  pythonPath: string, bridgeScriptPath: string,
  requestJsonPath: string, workingDir: string): string {
  const bridge = nativeBridge as JiebaHnpNativeBridge;
  return bridge.runRequestJson(pythonPath, bridgeScriptPath, requestJsonPath, workingDir);
}
async runRequest(request, requestName): Promise<JiebaHnpCallResult> {
  this.prepare();
  const requestPath = this.writeRequestFile(requestName, request);
  const raw = await taskpool.execute(
    runRequestConcurrent, this.pythonPath,
    this.paths.bridgeScriptPath, requestPath, this.paths.workDir) as string;
  return JiebaHnpClient.toResult(raw);
}

这里有个 ArkTS 细节:@Concurrent 函数只能引用 import 变量或局部变量,不能直接用模块级派生常量,所以要在函数内部对导入的 nativeBridge 做一次本地类型转换。

改完之后,处理过程中界面会正常显示「正在处理」、控件变灰禁用,处理完恢复,不再卡死,也不再弹 ANR。

坑三:结果区出现一堆 SyntaxWarning,JSON 解析失败

异步修好后又发现:分词虽然成功(状态显示「处理完成」),但结果区显示的不是漂亮的分词结果,而是一堆:

.../jieba/__init__.py:44: SyntaxWarning: invalid escape sequence '\.'
  re_han_default = re.compile("([一-...])", re.U)
...

原因有两层:jieba 0.42.1 是为较老的 Python 写的,它的正则字符串在新版 Python(3.12)上会触发 SyntaxWarning;而这些警告默认走 stderr,Native 层又用 2>&1 把 stderr 合并进了 stdout,于是警告被插在 JSON 前面,ArkTS 的 JSON.parse 直接失败、回退成显示原始文本。

这次从两端一起修(防御纵深):

  • Python 侧:import jiebawarnings.filterwarnings("ignore") 屏蔽 SyntaxWarning,导入后 logging.disable(logging.WARNING) 关掉 jieba 的运行日志;
  • ArkTS 侧:解析输出时不假设整段都是 JSON,而是从后往前找出第一行能被解析成 JSON 的内容(worker 保证 JSON 是最后打印的一整行),任何前置噪声都不影响。

改完之后,输出是干净的单行 JSON,结果区正常显示分词结果。

十一、构建验证和真机测试

工程可以用 DevEco Studio 直接构建运行(推荐普通用户用这种方式:打开 examples/harmony_pc,连上鸿蒙 PC 真机,点运行)。第一次打开需要在 File > Project Structure > Signing Configs 里勾选「Automatically generate signature」完成自动签名。

也可以用命令行构建签名 HAP:

DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk \
/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw \
  --mode module -p product=default -p module=entry@default \
  assembleHap --no-daemon

构建成功会看到:

Finished :entry:default@CompileArkTS
Finished :entry:default@PackageHap
Finished :entry:default@SignHap
Finished :entry:assembleHap
BUILD SUCCESSFUL

装到真机并启动:

HDC=/Applications/DevEco-Studio.app/Contents/sdk/default/openharmony/toolchains/hdc
$HDC install -r entry/build/default/outputs/default/entry-default-signed.hap
$HDC shell aa start -a EntryAbility -b org.jieba.hnp.example

在鸿蒙 PC 真机(HarmonyOS 6.0.2 / API 22)上,下列功能都验证正常:

  • 精确模式分词、全模式分词、搜索引擎分词;
  • 词性标注;
  • 关键词提取(TF-IDF、TextRank);
  • 处理结果导出为文本文件;
  • 处理过程中界面保持响应,不再出现「应用无响应」;
  • 输出为干净 JSON(已屏蔽 SyntaxWarning 和运行日志)。

导出后,结果文件会写到用户在保存选择器里选择的位置,比如桌面或文档目录。

在这里插入图片描述

十二、这次适配的结果与方法论

完成后,这个示例已经不是一个验证按钮,而是一个接近真实使用场景的鸿蒙 PC 中文分词工具:

  • 用户直接输入或从文件载入中文文本;
  • 一个下拉框切换六种分词 / 标注 / 关键词能力;
  • 结果可导出,导出走系统保存选择器;
  • jieba 随应用打包,设备端不用 pip;
  • 处理放在工作线程,界面不卡;
  • 输出干净,不被库的日志 / 警告污染。

从这次适配可以总结出一套通用的 Python 三方库鸿蒙 PC 接入思路:

先在 HNP Python 中验证三方库
  -> 再写 Python worker 承接 JSON 请求
  -> 用 N-API 启动 Python 并捕获 stdout
  -> 调用放到 TaskPool 工作线程,避免 UI 卡死
  -> ArkTS Client 封装业务方法
  -> ArkUI 页面走真实输入 / 文件选择 / 保存流程
  -> 最后用真机和 DevEco 构建验证闭环

对 jieba 这种纯 Python 库,还可以额外把库本体打包进 HAP,省掉设备端安装。这条路线比重写底层算法更稳,也更容易推广到其他 Python 工具库(如 pandas、scikit-learn、requests 等)。如果要继续扩展,可以在这个基础上增加自定义词典、批量分词、TF-IDF/TextRank 参数调节、停用词管理等能力。

最后再强调一遍给最终用户的前置条件:在鸿蒙 PC 上使用这个软件前,请先到鸿蒙 PC 应用市场下载「Python 安装器」,用它把 Python 环境装好,应用才能正常调用 HNP Python 完成分词。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐