解析带emoji和链接的聊天系统消息

4次阅读

共计 6094 个字符,预计需要花费 16 分钟才能阅读完成。

在写聊天系统的时候,不可避免地要对聊天系统中的消息做一些解析常见的比如一句话中带有 emoji、link 等信息的时候,要把 emoji 解析成图片、把 link 转成可以点击的(项目中没有做对图片做行内处理,而是把图片像微信一样作为单独消息发送)我们知道 react 的标签都是 jsx 的,所以在解析消息的时候,就必须在得到消息内容的时候,就先把消息内容分段截取比如这样一则消息
今天吃饭了吗?[emoji] 我还没吃呢 [emoji], 给你个链接看看吧!http://www.google.com/
emoji 要解析成图片,http://www.google.com/ 要解析成可以点击的链接,之间的文字要解析成文本
jquery 时代,只需要使用正则匹配 emoji,替换成图片,用正则匹配链接,替换成 a 标签即可但是在 react 里,这三者对应的是不同的 jsx 标签。所以必须把文本解析成分段式的
思路:上面这句话,可以解析成 6 部分
part1: 今天吃饭了吗?
part2: [emoji]
part3: 我还没吃呢
part4: [emoji]
part5: , 给你个链接看看吧!
part6: http://www.google.com/

每部分对应使用不同的 jsx 标签
第一步,我们先使用正则匹配 emoji 和链接分别的正则如下 (匹配链接应该有更优秀的正则)
var emojiregex = new RegExp(/\ud83c[\udf00-\udfff]|\ud83d[\udc00-\ude4f]|\ud83d[\ude80-\udeff]/g, ‘g’); // 匹配 emoji 字符
var matchUrlRegex = new RegExp(/(https?:)\/\/([^\/]+)(\/[^\?]*)?(\?[^#]*)?(#.*)?/g,’g’); // 匹配 url 的正则

var emojiRegArray = text.match(emojiregex); // 匹配了所有的 emoji 的词
var urlRegArray = text.match(matchUrlRegex);
得到两个数组,分别是匹配到的 emoji 和匹配到的 url
第二步,使用 index() 方法,获取每个 emoji、url 的位置、长度,并记录
var indexEmojiArray = []; // 记录表情的位置、内容的数组
var indexUrlArray = []; // 记录链接的位置、内容的数组

var pos1 = -1, pos2 = -1;// 头
if(emojiRegArray){
for (let i = 0; i < emojiRegArray.length; i++) {
pos1 = text.indexOf(emojiRegArray[i], pos1 + 1);
indexEmojiArray.push({
type: 1, // type 为 1 表示是表情
pos: pos1,
length: emojiRegArray[i].length,
res: emojiRegArray[i],
});
}
}
if(urlRegArray){
for (let i = 0; i < urlRegArray.length; i++) {
pos2 = text.indexOf(urlRegArray[i], pos2 + 1);
indexUrlArray.push({
type: 3, // type 为 1 表示是 url
pos: pos2,
length: urlRegArray[i].length,
res: urlRegArray[i],
});
}
}

第三步,按照这些元素在消息中的位置,两个数组合并成一个数组
// 合并两个数组
var indexArray = []; // 以上两个数组按照 pos 顺序合并的数组
if(emojiRegArray && urlRegArray){
let point1 = 0,point2 = 0;
while(point1 < indexEmojiArray.length || point2 < indexUrlArray.length){
if(!indexEmojiArray[point1]){// emoji 加完了
indexArray.push(indexUrlArray[point2]);
point2++;
}else if(!indexUrlArray[point2]){// url 加完了
indexArray.push(indexEmojiArray[point1]);
point1++;
}else{// 两个都没加完
if(indexEmojiArray[point1].pos < indexUrlArray[point2].pos){
indexArray.push(indexEmojiArray[point1]);
point1++;
}else{
indexArray.push(indexUrlArray[point2]);
point2++;
}
}
}
}else if(emojiRegArray && !urlRegArray){// 有 emoji 没有 url
indexArray = indexEmojiArray;
}else if(!emojiRegArray && urlRegArray){// 有 url 没有 emoji
indexArray = indexUrlArray;
}
第四步现在,我们得到了一个 indexArray,存储了 emoji 和 url 的位置和长度的数组现在我们要把文本也加进去,并且,emoji 替换成图片
// 这里开始把 indexArray 加工成 contentArray
let contentArray = [];
let point = 0; // 记录当前指针位置
for (let i = 0; i < indexArray.length; i++) {
// 把这一项和上一项之间的内容 push 成文本
console.log(point);
let textContent = text.substr(point, indexArray[i].pos-point);
console.log(textContent);
contentArray.push({type: 0, content: textContent});
// point += textContent.length;
if(indexArray[i].type === 1){// 如果这一项是 emoji
// contentArray.push({type: 1, “resources”: EMOJI_MAP[indexArray[i].res] || []});
contentArray.push({type: 1, resources: indexArray[i].res || []});
point = indexArray[i].pos + indexArray[i].length;
}else if(indexArray[i].type === 3){// 如果这一项是 url
contentArray.push({type: 3, url: indexArray[i].res});
point = indexArray[i].pos + indexArray[i].length;
}
}
// 加入末尾项。如果 indexArray 为空,那么末尾项就是唯一的文本项
let lastPrevItemIndex = (indexArray[indexArray.length-1] ? indexArray[indexArray.length-1].pos+indexArray[indexArray.length-1].length : 0);
contentArray.push({type: 0, content: text.substr(lastPrevItemIndex, text.length)});
最后得到的 contentArray 我们 return 出去。
比较难的部分在第四步,思路是,我们使用一个指针,对消息做解析,一开始指针的位置为 0 开头不管如何都 push 一个文本对象进入 contentArray 中直到遇到 emoji 或者 url 位置为止比如遇到的是 emoji,我们把 emoji 解析成对象 push 到 contentArray 中,然后指针加上 emoji 的长度最后加上末尾。如果末尾不为文本,也添加一个空的文本对象。
完毕。
下面附上所有代码

let stringToContentArray = function (text) {

var emojiregex = new RegExp(/\ud83c[\udf00-\udfff]|\ud83d[\udc00-\ude4f]|\ud83d[\ude80-\udeff]/g, ‘g’);
var matchUrlRegex = new RegExp(/(https?:)\/\/([^\/]+)(\/[^\?]*)?(\?[^#]*)?(#.*)?/g,’g’); // 匹配 url 的正则
var contentArray = [];
if (!text) {// 没有内容
contentArray.push({type: 0, “content”: ‘[ 无内容消息]’ });
return contentArray;
}
var emojiRegArray = text.match(emojiregex); // 匹配了所有的 emoji 的词
var urlRegArray = text.match(matchUrlRegex);
// console.log(text);
console.log(’emojiRegArray:’,emojiRegArray);
console.log(‘urlRegArray:’,urlRegArray);
if (emojiRegArray === null && urlRegArray === null) {// 没有 emoji 表情, 也没有链接
contentArray.push({type: 0, “content”: text});
return contentArray;
}

var indexEmojiArray = []; // 记录表情的位置、内容的数组
var indexUrlArray = []; // 记录链接的位置、内容的数组
var indexArray = []; // 以上两个数组按照 pos 顺序合并的数组
var pos1 = -1, pos2 = -1;// 头
if(emojiRegArray){
for (let i = 0; i < emojiRegArray.length; i++) {
pos1 = text.indexOf(emojiRegArray[i], pos1 + 1);
indexEmojiArray.push({
type: 1, // type 为 1 表示是表情
pos: pos1,
length: emojiRegArray[i].length,
res: emojiRegArray[i],
});
}
}
if(urlRegArray){
for (let i = 0; i < urlRegArray.length; i++) {
pos2 = text.indexOf(urlRegArray[i], pos2 + 1);
indexUrlArray.push({
type: 3, // type 为 1 表示是 url
pos: pos2,
length: urlRegArray[i].length,
res: urlRegArray[i],
});
}
}
if(emojiRegArray && urlRegArray){
let point1 = 0,point2 = 0;
while(point1 < indexEmojiArray.length || point2 < indexUrlArray.length){
if(!indexEmojiArray[point1]){// emoji 加完了
indexArray.push(indexUrlArray[point2]);
point2++;
}else if(!indexUrlArray[point2]){// url 加完了
indexArray.push(indexEmojiArray[point1]);
point1++;
}else{// 两个都没加完
if(indexEmojiArray[point1].pos < indexUrlArray[point2].pos){
indexArray.push(indexEmojiArray[point1]);
point1++;
}else{
indexArray.push(indexUrlArray[point2]);
point2++;
}
}
}
}else if(emojiRegArray && !urlRegArray){// 有 emoji 没有 url
indexArray = indexEmojiArray;
}else if(!emojiRegArray && urlRegArray){// 有 url 没有 emoji
indexArray = indexUrlArray;
}

console.log(“indexArray: “, indexArray);

// 这里开始把 indexArray 加工成 contentArray
let point = 0; // 记录当前指针位置
for (let i = 0; i < indexArray.length; i++) {
// 把这一项和上一项之间的内容 push 成文本
console.log(point);
let textContent = text.substr(point, indexArray[i].pos-point);
console.log(textContent);
contentArray.push({type: 0, content: textContent});
// point += textContent.length;
if(indexArray[i].type === 1){// 如果这一项是 emoji
// contentArray.push({type: 1, “resources”: EMOJI_MAP[indexArray[i].res] || []});
contentArray.push({type: 1, resources: indexArray[i].res || []});
point = indexArray[i].pos + indexArray[i].length;
}else if(indexArray[i].type === 3){// 如果这一项是 url
contentArray.push({type: 3, url: indexArray[i].res});
point = indexArray[i].pos + indexArray[i].length;
}
}
// 加入末尾项。如果 indexArray 为空,那么末尾项就是唯一的文本项
let lastPrevItemIndex = (indexArray[indexArray.length-1] ? indexArray[indexArray.length-1].pos+indexArray[indexArray.length-1].length : 0);
contentArray.push({type: 0, content: text.substr(lastPrevItemIndex, text.length)});

return contentArray;
}

正文完
 0