aardio 文档

string.wordsSegmenter

基于 Windows 自带的 Windows.Data.Text.WordsSegmenter,对文本执行本地、离线分词。C# 桥接层通过 dotNet.uwpCompiler 编译,aardio 接口只返回普通数组和表,不暴露 .NET / WinRT 代理对象。

运行与构建

快速开始

import string.wordsSegmenter;

// 省略语言或传 null,使用 zh-CN;也可指定 en-US、ja-JP、und 等。
var segmenter,err = string.wordsSegmenter("zh-Hans-CN");
if(!segmenter){
    print(err);
    return;
}

var words,err = segmenter.getWords("我爱北京和上海,Hello world! 👋");
if(words === null){
    print(err);
    return;
}

print(words);
print("实际分词规则:", segmenter.resolvedLanguage);

在测试电脑(Windows 10 19045)上,以上文本返回:

["我", "爱", "北京", "和", "上海", "Hello", "world", "👋"]

Windows 版本及可用语言规则可能影响断词结果。此库保留 Windows 返回的词序、重复项、大小写及符号处理方式,不另做停用词过滤、词性标注或自定义词典处理。词列表不包含所有分隔符,不应通过拼接词列表还原全文。

创建分词器与语言

string.wordsSegmenter(language) 与 string.wordsSegmenter.create(language) 等价。成功返回分词器,失败返回 null,err。

属性 含义
language 只读,创建时请求的语言标记
resolvedLanguage 只读,Windows 实际使用的分词规则语言

API

方法 返回值
getWords(text) UTF-8 词文本的纯数组
getTokens(text) 包含位置、长度和替代形式的纯表数组
getTokenAt(text,startIndex) 包含指定位置的词,或该位置之后的第一个词

三个方法的 text 参数均接受字符串,null 按空文本处理;其他类型返回 null,err。getWords / getTokens 对空文本返回 []。方法调用失败返回 null,err,而 getTokenAt 在合法位置没有后续词时返回 null 且没有错误。

创建分词器约需 16 毫秒,请复用。在测试电脑(Windows 11)上,19 个词的中文句子调用 getWords 或 getTokens 各约 0.5 毫秒,500 个词的 getTokens 约 5 毫秒。

词信息

getTokens 的每项和 getTokenAt 的返回对象均包含:

字段 类型与含义
text Windows 返回的词文本,UTF-8 字符串,可能已归一化
startPosition 在完整原文中的 0 起始 UTF-16 代码单元位置
length 原文片段的 UTF-16 代码单元长度
alternateForms 替代形式的纯数组,没有时为 []

每个替代形式包含 alternateText、startPosition、length、normalizationFormat。位置仍然相对完整原文,而不是相对当前词。normalizationFormat 为 Windows 返回的枚举名称字符串:NotNormalized、Number、Currency、Date 或 Time。

替代形式可能覆盖比当前词更多、更少或相同的原文,其文本可能是数字、日期等的归一化值,不保证等于原文子串。本库不修正 Windows 的识别或归一化结果。

text 本身也可能被 Windows 归一化。例如测试电脑的中文规则会把 e + 组合重音返回为 é,但 length 仍为原文的 2 个 UTF-16 单位。因此不能用 text 的长度替代 length;需要原样文本时请按 startPosition 和 length 从输入中提取。

import string.wordsSegmenter;
var segmenter,err = string.wordsSegmenter("en-US");
if(!segmenter){ print(err); return; }

var tokens,err = segmenter.getTokens("Hello, world!");
if(tokens === null){ print(err); return; }
for i,token in tokens {
    print(token.text, token.startPosition, token.length, token.alternateForms);
}

索引规则与查词

索引不是 aardio 的 UTF-8 字节索引,也不是 Unicode 码点索引。 英文字符及 BMP 汉字通常各占 1 个 UTF-16 单位,👋、𠀀 等非 BMP 字符占 2 个单位;组合字符或复合 emoji 可能占更多单位。

getTokenAt 的 startIndex 必须是非负整数并小于原文的 UTF-16 长度。等于长度、空文本的索引 0、负数、小数及错误类型都返回 null,err。

import string.wordsSegmenter;
var segmenter,err = string.wordsSegmenter("en-US");
if(!segmenter){ print(err); return; }

// "hello, world! " 的索引 5 是逗号:查到后面的 world。
var token,err = segmenter.getTokenAt("hello, world! ",5);
if(token) print(token.text,token.startPosition,token.length); // world,7,5
else if(err) print(err);

// 索引 12 合法,但之后无词:null,无错误。
var missing,err = segmenter.getTokenAt("hello, world! ",12);
print(missing,err);

如需根据位置提取原文,先转 UTF-16,再按字节范围切片:

import string.wordsSegmenter;
var segmenter,err = string.wordsSegmenter("en-US");
if(!segmenter){ print(err); return; }

var text = "👋 中文 abc";
var tokens,err = segmenter.getTokens(text);
if(tokens === null){ print(err); return; }
var wide = string.fromto(text,65001,1200);
for i,token in tokens {
    var firstByte = token.startPosition * 2 + 1;
    var lastByte = (token.startPosition + token.length) * 2;
    var part = raw.slice(wide,firstByte,lastByte);
    print(string.fromto(part,1200,65001));
}

提取、查词和定位时必须使用分词时的同一份原文;不要预先改变换行、大小写或空白后再套用旧位置。

在线程中使用

分词器含有闭包和 .NET 对象,不应跨 aardio 线程传递。在每个工作线程中导入库并创建自己的分词器,只跨线程传递字符串和返回的普通数据。

var words,err = thread.invokeAndWait(function(text){
    import string.wordsSegmenter;
    var segmenter,err = string.wordsSegmenter("zh-CN");
    if(!segmenter) return null,err;
    return segmenter.getWords(text);
},"中文分词示例");
print(words,err);

重建与文件布局

库根目录:~/lib/string/wordsSegmenter/

在安装 Windows SDK 后运行 .res/build.aardio 即可重建。该文件也支持命令行 -winmd 指定 Windows.winmd 的绝对路径。输出位置固定为上述库资源目录,不受当前工程目录影响。重建后在新的运行会话中重新导入库,避免沿用已加载的程序集。

本库用 $ 内嵌的字节加载 DLL,不锁定文件;但如果当前进程的某个线程按路径加载过旧 DLL,旧文件会被锁定到进程退出。此时 build.aardio 会先把旧文件改名为 wordsSegmenter.dll.*.old(被锁定的文件仍可改名),再生成新文件;这些旧文件在以后重建时自动删除。

修改 C# 接口时,返回给 aardio 的数据只能是一维类型化数组(string[]、int[]);需要返回多个数组时用 object[] 包装,aardio 端读取其 Value 属性一次转换。不要每项返回一个 object[]:嵌套数组在 aardio 中是 .NET 代理,逐元素跨界调用,500 个词约需 0.86 秒;现在的扁平布局约 5 毫秒。布局说明见 WordsSegmenter.cs 中 GetTokenData 的注释。

重建只生成本库的 wordsSegmenter.dll,不依赖也不修改 WinRT 范例目录中的 aardio.WindowsRuntime.dll。

完整控制台示例位于本目录的 example.aardio。

原生 API 参考

微软文档可按下列 API 全名检索:

Markdown 格式