写点什么

HLP 分词后的文本如何在 web 端高亮显示

作者:lo
  • 2022 年 4 月 18 日
  • 本文字数:1835 字

    阅读完需:约 6 分钟

HLP分词后的文本如何在web端高亮显示

公司有个项目,是做文本语义化分析工具,采用 NLP 分词工具进行模型训练,前端这边的交互主要是有两点: 文本高亮显示和手动训练

先说说文本高亮显示

文本高亮显示

首先文本是段落结构,存在多个段落,

其次是每段文本中,存在多句文本;

例如:‘武汉大学坐落于武汉市珞珈山’

所以后端返回的数据格式形式

const data = [{
  "paraId": 0,
  "paraText": "武汉大学坐落于武汉市珞珈山",
  "paraEntity": [{
     "category": "名词",
     "labelText": "武汉大学",
     "startIndex": 0,
     "endIndex": 4,
     "color": 'rgba(240,215,12,.5)'
   }, {
     "category": "地名",
     "labelText": "武汉",
     "startIndex": 7,
     "endIndex": 9,
     "color": '#00baff'
  }]
}];
复制代码

前端如何在文本中高亮显示呢

最开始的方法:

硬核 replace

将后端返回的数据格式提取形成一个数组,里面的元素以对象的形式存储,然后循环遍历,将文本中需要高亮的词替换成<em style="background: '颜色'"></em>

存在的问题很明显,无法针对相同的词汇给出不同的词性

比如: 

武汉大学坐落于武汉市珞珈山


这里识别前面武汉大学是一个名词,武汉是一个地名,通过上述方法,会将所有武汉标记为地名或者其它词性;

那么该如何改进呢?我想到的是拆分法

拆分法

将文本中每一个字遍历,替换成 em 标签;

拆分形成的 em 标签文本,我们可以按每个字去显示不同的颜色,也就是词性;

<em>武</em><em>汉</em><em>大</em><em>学</em><em>坐</em><em>落</em><em>于</em><em>武</em><em>汉</em><em>市</em><em>珞</em><em>珈</em><em>山</em>

那么,如何定位呢,

这里我们用到了 html 中的 data 属性

data 属性

data-* 全局属性 是一类被称为自定义数据属性的属性,它赋予我们在所有 HTML 元素上嵌入自定义数据属性的能力,并可以通过脚本在 HTMLDOM 表现之间进行专有数据的交换。

使用 js 中的 getAttribute 方法这样我们可以通过后台给定的起始位置和长度,定位到相应的文本

/* 切割和拼成em */
// paranum是段落数 index是这句话中第几个字
createElement(textStr, tagName = 'em', k) {
  let NewTextStr = '';
  for (let i = 0; i < textStr.length; i += 1) {
    NewTextStr += `<${tagName} data-paranum="${k}" data-index="${i}">${textStr[i]}</${tagName}>`;
  }
  return NewTextStr;
},
复制代码

注意,由于文本中会存在特殊文号,比如书名号‘《》’, 括号‘()’,所以我们需要对此进行转义

/* 转义 */
escapeStr(str) {
  let regStr = '';
  const specialArry = ['(', ')', '[', ']', '\\', '+', '*', '?', '.', '|'];
  for (let k = 0; k < str.length; k += 1) {
    if (specialArry.indexOf(str[k]) > -1) {
      regStr += `\\${str[k]}`;
    } else {
      regStr += str[k];
    }
  }
  return regStr;
},
复制代码

效果


总结思路

1、先将段落文本每一个字切割合并成 em 标签,并加上 data 属性(段落以及字位置);

2、循环遍历数据,最小单位为每个字,所以这里会遍历 3 次;

data.forEach((e) => {
  for (let m = 0; m < e.paraEntity.length; m += 1) {
   for (let i = 0; i < e.paraEntity[m].labelText.length; i += 1) {
   }
  }
}
复制代码

3、在循环中拼接非高亮的字符串以及高亮的字符串

注意: notHightStr 和 notHightStr 是在 forEach 中声明的变量

notHightStr = `<em data-paranum="${k}" data-index="${i + e.paraEntity[m].startIndex}">${e.paraEntity[m].labelText[i]}</em>`;
hightStr = `<em data-paranum="${k}" data-index="${i + e.paraEntity[m].startIndex}" style="background: ${ e.paraEntity[m].color};">${e.paraEntity[m].labelText[i]}</em>`;
复制代码

4、替换

注意: emStr 是在循环外

let emStr = createElement(fileText, 'em', k);
emStr = emStr.replace(notHightStr, hightStr);
复制代码

5、生成拼接后的高亮 html

markTxt += `<p>${emStr}</p>`;
复制代码

完整代码

https://github.com/642134542/HLP

最后

在项目中,因为开发者不同,返回的数据会有细微差别,除了字段名称外,数据结构以及每个字的位置都会有所偏差,所以需要根据实际进行调整,总的来说,拆分的思想便于定位

但是,相应的存在弊端,增加 html 标签、循环嵌套过多影响性能等。

除了用语义模型进行识别外,还需要人工进行调整,一般的交互是通过右键点击,获取到 data 属性以及词语,再与后端交互,达到手动校正的效果。

用户头像

lo

关注

汝等ここに入るもの, 一切の望みを捨てる 2020.02.06 加入

学习,摸鱼两不误

评论

发布
暂无评论
HLP分词后的文本如何在web端高亮显示_前端_lo_InfoQ写作平台