公司有个项目,是做文本语义化分析工具,采用 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 元素上嵌入自定义数据属性的能力,并可以通过脚本在 HTML 与 DOM 表现之间进行专有数据的交换。
使用 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 属性以及词语,再与后端交互,达到手动校正的效果。
评论