Files
ToolGood.Words/javascript/ToolGood.Words.WordsSearch.js
T
2020-05-17 09:57:09 +08:00

324 lines
9.6 KiB
JavaScript

// ToolGood.Words.WordsSearch.js
// 2020, Lin Zhijun, https://github.com/toolgood/ToolGood.Words
// Licensed under the Apache License 2.0
// 更新日志
// 2020.05.17 修改,支持大于0xffff的字符
function WordsSearch() {
function TrieNode() {
this.Index = 0;
this.Layer = 0;
this.End = false;
this.Char = '';
this.Results = [];
this.m_values = {};
this.Failure = null;
this.Parent = null;
this.Add = function (c) {
if (this.m_values[c] != null) {
return this.m_values[c];
}
var node = new TrieNode();
node.Parent = this;
node.Char = c;
this.m_values[c] = node;
return node;
}
this.SetResults = function (index) {
if (this.End == false) {
this.End = true;
}
this.Results.push(index)
}
}
function TrieNode2() {
this.End = false;
this.Results = [];
this.m_values = {};
this.minflag = 0xffff;
this.maxflag = 0;
this.Add = function (c, node3) {
if (typeof c !== 'number') {
c = parseInt(c);
}
if (this.minflag > c) { this.minflag = c; }
if (this.maxflag < c) { this.maxflag = c; }
this.m_values[c] = node3;
}
this.SetResults = function (index) {
if (this.End == false) {
this.End = true;
}
if (this.Results.indexOf(index) == -1) {
this.Results.push(index);
}
}
this.HasKey = function (c) {
return this.m_values[c] != undefined;
}
this.TryGetValue = function (c) {
if (this.minflag <= c && this.maxflag >= c) {
return this.m_values[c];
}
return null;
}
}
var _first = {};
var _keywords = [];
var _indexs = [];
/**
* 设置关键字
* @param {any} keywords
*/
this.SetKeywords = function (keywords) {
_keywords = keywords;
for (var i = 0; i < keywords.length; i++) {
_indexs.push(i);
}
SetKeywords2();
}
function SetKeywords2() {
var root = new TrieNode();
var allNodeLayer = {};
for (var i = 0; i < _keywords.length; i++) {
var p = _keywords[i];
var nd = root;
for (var j = 0; j < p.length; j++) {
nd = nd.Add(p.charCodeAt(j));
if (nd.Layer == 0) {
nd.Layer = j + 1;
if (allNodeLayer[nd.Layer]) {
allNodeLayer[nd.Layer].push(nd)
} else {
allNodeLayer[nd.Layer] = [];
allNodeLayer[nd.Layer].push(nd)
}
}
}
nd.SetResults(i);
}
var allNode = [];
allNode.push(root);
for (var key in allNodeLayer) {
var nds = allNodeLayer[key];
for (var i = 0; i < nds.length; i++) {
allNode.push(nds[i]);
}
}
allNodeLayer = null;
for (var i = 1; i < allNode.length; i++) {
var nd = allNode[i];
nd.Index = i;
var r = nd.Parent.Failure;
var c = nd.Char;
while (r != null && !r.m_values[c])
r = r.Failure;
if (r == null)
nd.Failure = root;
else {
nd.Failure = r.m_values[c];
for (var key2 in nd.Failure.Results) {
if (nd.Failure.Results.hasOwnProperty(key2) == false) { continue; }
var result = nd.Failure.Results[key2];
nd.SetResults(result);
}
}
}
root.Failure = root;
var allNode2 = [];
for (var i = 0; i < allNode.length; i++) {
allNode2.push(new TrieNode2());
}
for (var i = 0; i < allNode2.length; i++) {
var oldNode = allNode[i];
var newNode = allNode2[i];
for (var key in oldNode.m_values) {
if (oldNode.m_values.hasOwnProperty(key) == false) { continue; }
var index = oldNode.m_values[key].Index;
newNode.Add(key, allNode2[index]);
}
for (let index = 0; index < oldNode.Results.length; index++) {
var item = oldNode.Results[index];
newNode.SetResults(item);
}
oldNode = oldNode.Failure;
while (oldNode != root) {
for (var key in oldNode.m_values) {
if (oldNode.m_values.hasOwnProperty(key) == false) { continue; }
if (newNode.HasKey(key) == false) {
var index = oldNode.m_values[key].Index;
newNode.Add(key, allNode2[index]);
}
}
for (let index = 0; index < oldNode.Results.length; index++) {
var item = oldNode.Results[index];
newNode.SetResults(item);
}
oldNode = oldNode.Failure;
}
}
allNode = null;
root = null;
// var first = [];
// for (let index = 0; index < 0xffff; index++) {
// first.push(null);
// }
// for (var key in allNode2[0].m_values) {
// if (allNode2[0].m_values.hasOwnProperty(key) == false) { continue; }
// first[key] = allNode2[0].m_values[key];
// }
_first = allNode2[0];
}
/**
* 查找第一个匹配 字符串,带位置信息和索引编号
* @param {any} text
*/
this.FindFirst = function (text) {
var ptr = null;
for (let i = 0; i < text.length; i++) {
var t = text.charCodeAt(i);
var tn = null;
if (ptr == null) {
tn = _first.TryGetValue(t);
} else {
tn = ptr.TryGetValue(t);
if (!tn) {
tn = _first.TryGetValue(t);
}
}
if (tn != null) {
if (tn.End) {
var item = tn.Results[0];
var keyword = _keywords[item];
return {
Keyword: keyword,
Success: true,
End: i,
Start: i + 1 - _keywords[item].length,
Index: _indexs[item],
}
}
}
ptr = tn;
}
return null;
}
/**
* 查找所有匹配 字符串,带位置信息和索引编号
* @param {any} text
*/
this.FindAll = function (text) {
var ptr = null;
var list = [];
for (let i = 0; i < text.length; i++) {
var t = text.charCodeAt(i);
var tn = null;
if (ptr == null) {
tn = _first.TryGetValue(t);
} else {
tn = ptr.TryGetValue(t);
if (!tn) {
tn = _first.TryGetValue(t);
}
}
if (tn != null) {
if (tn.End) {
for (let j = 0; j < tn.Results.length; j++) {
var item = tn.Results[j];
var keyword = _keywords[item];
list.push({
Keyword: keyword,
Success: true,
End: i,
Start: i + 1 - _keywords[item].length,
Index: _indexs[item],
});
}
}
}
ptr = tn;
}
return list;
}
/**
* 检查是否包含
* @param {any} text
*/
this.ContainsAny = function (text) {
var ptr = null;
for (let index = 0; index < text.length; index++) {
var t = text.charCodeAt(index);
var tn = null;
if (ptr == null) {
tn = _first.TryGetValue(t);
} else {
tn = ptr.TryGetValue(t);
if (!tn) {
tn = _first.TryGetValue(t);
}
}
if (tn != null) {
if (tn.End) {
return true;
}
}
ptr = tn;
}
return false;
}
/**
* 查找所有匹配全部替换
* @param {any} text
* @param {any} replaceChar
*/
this.Replace = function (text, replaceChar) {
if (replaceChar == undefined) {
replaceChar = '*'
}
var result = text.split('');
var ptr = null;
for (var i = 0; i < text.length; i++) {
var t = text.charCodeAt(i);
var tn = null;
if (ptr == null) {
tn = _first.TryGetValue(t);
} else {
tn = ptr.TryGetValue(t);
if (!tn) {
tn = _first.TryGetValue(t);
}
}
if (tn != null) {
if (tn.End) {
var maxLength = _keywords[tn.Results[0]].length;
var start = i + 1 - maxLength;
for (var j = start; j <= i; j++) {
result[j] = replaceChar;
}
}
}
ptr = tn;
}
return result.join("");
}
}