《快递地址文本识别》二、textProcessing文本处理使用指南

HarmonyOS textProcessing文本处理完全指南:分词与实体抽取实战

适用版本 :HarmonyOS NEXT(API 23+)

开发工具 :DevEco Studio 6.1+

核心技术:Natural Language Kit、textProcessing、EntityType


效果

一、Natural Language Kit 概述

HarmonyOS SDK 内置的 Natural Language Kit(自然语言理解服务) 提供了两项核心文本分析能力:

能力 说明 典型场景
分词(WordSegment) 将文本切分为独立词语单元 搜索引擎关键词提取、文本分析
实体抽取(Entity) 从文本中识别具有特定意义的实体 地址解析、信息提取、智能表单

1.1 约束与限制

维度 限制
支持语言 简体中文、英文、繁体中文
文本长度 不超过1000字符
并发限制 同一用户不支持同一特性并发调用
模拟器 不支持,需真机调试

二、模块导入

typescript 复制代码
// 分词功能
import { textProcessing } from '@kit.NaturalLanguageKit';

// 实体抽取功能(需额外导入EntityType)
import { textProcessing, EntityType } from '@kit.NaturalLanguageKit';

三、分词功能详解

3.1 API接口

typescript 复制代码
textProcessing.getWordSegment(text: string): Promise<textProcessing.WordSegment[]>

返回值 WordSegment 结构:

属性 类型 说明
word string 分词结果文本
wordTag string 词性标签

3.2 基础示例

typescript 复制代码
import { textProcessing } from '@kit.NaturalLanguageKit';

@Entry
@Component
struct WordSegmentDemo {
  private inputText: string = '';
  @State outputText: string = '';

  build() {
    Column() {
      TextInput({ placeholder: '请输入要分词的文本' })
        .height(40)
        .fontSize(16)
        .width('90%')
        .margin(10)
        .onChange((value: string) => {
          this.inputText = value;
        })

      Button('获取分词结果')
        .type(ButtonType.Capsule)
        .fontColor(Color.White)
        .margin(10)
        .onClick(async () => {
          try {
            let result = await textProcessing.getWordSegment(this.inputText);
            this.outputText = this.formatResult(result);
          } catch (err) {
            this.outputText = `错误: ${(err as Error).message}`;
          }
        })

      Scroll() {
        Text(this.outputText)
          .fontSize(14)
          .width('90%')
      }
      .height('50%')
      .margin(10)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

  private formatResult(segments: textProcessing.WordSegment[]): string {
    if (!segments || !segments.length) {
      return '无分词结果';
    }
    let output = `共 ${segments.length} 个词:\n\n`;
    for (let i = 0; i < segments.length; i++) {
      output += `[${i + 1}] ${segments[i].word}  (${segments[i].wordTag})\n`;
    }
    return output;
  }
}

3.3 分词结果示例

输入文本:"华为技术有限公司成立于深圳"

复制代码
共 6 个词:

[1] 华为技术有限公司  (机构名)
[2] 成立  (动词)
[3] 于  (介词)
[4] 深圳  (地名)

四、实体抽取功能详解(重点)

4.1 API接口

typescript 复制代码
textProcessing.getEntity(
  text: string,
  options: { entityTypes: EntityType[] }
): Promise<textProcessing.Entity[]>

4.2 支持的实体类型(EntityType)

EntityType枚举值 含义 示例
EntityType.NAME 人名 张三、李明
EntityType.PHONE_NO 手机号 13800138000
EntityType.LOCATION 地址/地点 北京市朝阳区XX路1号
EntityType.DATETIME 日期时间 2025年6月18日
EntityType.EMAIL 邮箱 user@example.com
EntityType.EXPRESS_NO 快递单号 SF1234567890
EntityType.FLIGHT_NO 航班号 CA1234
EntityType.URL 网址 https://example.com
EntityType.VERIFICATION_CODE 验证码 582916
EntityType.ID_NO 身份证号 110101199001011234

4.3 返回值 Entity 结构

属性 类型 说明
text string 识别出的实体文本
charOffset number 实体在原文中的字符偏移量
type EntityType 实体类型
jsonObject object 实体附加信息(JSON格式)

4.4 Entity.jsonObject 地址附加信息

对于LOCATION类型的实体,jsonObject包含丰富的地址结构信息:

字段 说明
coreLocation 核心地址标识
province 省份信息
city 城市信息
district 区县信息
county 县级信息
adornLocation 修饰性地址(如"XX小区门口")

这些字段在地址解析场景中至关重要,可帮助区分"省市区"和"详细地址"。

4.5 基础示例:人名和手机号识别

typescript 复制代码
import { textProcessing, EntityType } from '@kit.NaturalLanguageKit';

@Entry
@Component
struct EntityDemo {
  private inputText: string = '';
  @State outputText: string = '';

  build() {
    Column() {
      TextInput({ placeholder: '输入包含姓名、电话的文本' })
        .height(40)
        .fontSize(16)
        .width('90%')
        .margin(10)
        .onChange((value: string) => {
          this.inputText = value;
        })

      Button('识别实体')
        .type(ButtonType.Capsule)
        .fontColor(Color.White)
        .margin(10)
        .onClick(async () => {
          try {
            let result = await textProcessing.getEntity(this.inputText, {
              entityTypes: [EntityType.NAME, EntityType.PHONE_NO]
            });
            this.outputText = this.formatEntities(result);
          } catch (err) {
            this.outputText = `错误: ${(err as Error).message}`;
          }
        })

      Scroll() {
        Text(this.outputText)
          .fontSize(14)
          .width('90%')
      }
      .height('50%')
      .margin(10)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

  private formatEntities(entities: textProcessing.Entity[]): string {
    if (!entities || !entities.length) {
      return '未识别到实体';
    }
    let output = `识别到 ${entities.length} 个实体:\n\n`;
    for (let i = 0; i < entities.length; i++) {
      let entity = entities[i];
      output += `实体[${i + 1}]\n`;
      output += `  文本: ${entity.text}\n`;
      output += `  类型: ${entity.type}\n`;
      output += `  偏移: ${entity.charOffset}\n\n`;
    }
    return output;
  }
}

五、实战:快递地址智能解析

5.1 场景说明

用户粘贴一段快递地址文本,例如:

"张伟 13912345678 广东省深圳市南山区科技园南路88号创新大厦A座"

系统自动将其拆分为:

字段 解析结果
姓名 张伟
电话 13912345678
省市区 广东省深圳市南山区
详细地址 科技园南路88号创新大厦A座

5.2 核心解析逻辑

typescript 复制代码
import { textProcessing, EntityType } from '@kit.NaturalLanguageKit';

private async parseAddressText(text: string): Promise<void> {
  try {
    let entities = await textProcessing.getEntity(text, {
      entityTypes: [EntityType.NAME, EntityType.PHONE_NO, EntityType.LOCATION]
    });

    if (!entities || !entities.length) {
      return;
    }

    for (let i = 0; i < entities.length; i++) {
      let entity = entities[i];

      if (entity.type === EntityType.NAME) {
        // 提取姓名
        this.recipientName = entity.text;

      } else if (entity.type === EntityType.PHONE_NO) {
        // 提取手机号
        this.phoneNumber = entity.text;

      } else if (entity.type === EntityType.LOCATION) {
        // 地址实体需要特殊处理
        let jsonObj = entity.jsonObject.toString();

        // 判断是否为核心地址(含省、市、区/县)
        let isCoreLocation =
          jsonObj.includes('coreLocation') &&
          jsonObj.includes('province') &&
          jsonObj.includes('city') &&
          (jsonObj.includes('district') || jsonObj.includes('county'));

        // 判断是否为修饰性地址(如"XX号门口")
        let isAdornLocation = jsonObj.includes('adornLocation');

        if (isCoreLocation && !isAdornLocation &&
            (i + 1) < entities.length &&
            entities[i + 1].type === EntityType.LOCATION) {
          // 当前实体为省市区
          this.regionAddress = entity.text;
        } else {
          // 尝试按"区"或"县"拆分省市区和详细地址
          if (entity.text.includes('区')) {
            let parts = entity.text.split('区');
            this.regionAddress = parts[0] + '区';
            this.detailAddress = parts[1];
          } else if (entity.text.includes('县')) {
            let parts = entity.text.split('县');
            this.regionAddress = parts[0] + '县';
            this.detailAddress = parts[1];
          } else {
            this.detailAddress = entity.text;
          }
        }
      }
    }
  } catch (err) {
    console.error(`解析失败: ${(err as Error).message}`);
  }
}

5.3 地址解析要点

  1. 请求3种实体类型NAME(姓名)、PHONE_NO(电话)、LOCATION(地址)
  2. 地址实体可能返回多个:第一个通常为省市区,第二个为详细地址
  3. 利用jsonObject判断地址性质coreLocation标识核心地址,adornLocation标识修饰地址
  4. 按"区"或"县"拆分:当省市区和详细地址合并在一个实体中时,可用分隔字符拆分

六、错误处理最佳实践

typescript 复制代码
try {
  let result = await textProcessing.getEntity(text, {
    entityTypes: [EntityType.NAME, EntityType.PHONE_NO, EntityType.LOCATION]
  });
  // 处理结果
} catch (err) {
  let error = err as Error;
  // 常见错误码处理
  if (error.code === '10210001') {
    // 输入文本为空
    console.warn('输入文本不能为空');
  } else if (error.code === '10210002') {
    // 文本超长
    console.warn('文本长度不能超过1000字符');
  } else {
    console.error(`未知错误: ${error.code} - ${error.message}`);
  }
}

七、性能优化建议

  1. 避免重复调用 :同一段文本只调用一次getEntity,缓存结果
  2. 按需指定entityTypes:只请求需要的实体类型,减少处理时间
  3. 控制文本长度:超过1000字符的文本需要分段处理
  4. 避免并发调用:同一特性不支持同一用户并发调用

八、与PasteButton配合使用

将剪贴板粘贴与文本解析结合,形成完整的"粘贴即解析"体验:

typescript 复制代码
PasteButton()
  .onClick(async () => {
    // 第一步:通过安全控件读取剪贴板
    await systemPasteboard.getData().then(async (data: pasteboard.PasteData) => {
      const text = data.getPrimaryText();
      if (text) {
        // 第二步:调用实体抽取解析文本
        try {
          let entities = await textProcessing.getEntity(text, {
            entityTypes: [EntityType.NAME, EntityType.PHONE_NO, EntityType.LOCATION]
          });
          this.processEntities(entities);
        } catch (err) {
          console.error('解析失败');
        }
      }
    });
  })

九、总结

textProcessing是HarmonyOS Natural Language Kit的核心API,提供了分词实体抽取 两大文本分析能力。在快递地址解析场景中,通过getEntity()配合EntityType.NAMEPHONE_NOLOCATION三种实体类型,可以将非结构化的地址文本自动拆分为姓名、电话、省市区和详细地址,大幅提升用户填写效率。

核心要点回顾

  • 导入@kit.NaturalLanguageKit模块
  • 使用EntityType枚举指定需要的实体类型
  • 通过Entity.jsonObject中的地址结构信息区分省市区和详细地址
  • PasteButton配合实现"粘贴即解析"的流畅体验