"@xxx"提及用户,这种功能的实现原理是怎样的?

2014-12-13 20:40:50 +08:00
 viskem
我自己想的当然就是匹配处文本中所有以@开头~空格结束中间的部分。
然后依次存进一个提醒表里(什么时候谁在哪篇评论或话题中@了你)
是这样做的吗?
6264 次点击
所在节点    PHP
22 条回复
abelyao
2014-12-13 20:47:37 +08:00
我也是这样理解的,来 @Livid 老大出来指点指点?
jox
2014-12-13 20:48:55 +08:00
如果需要验证并且需要严肃处理的话,需要分析字符串,然后用语法规则匹配,匹配得到结果之后验证是否是有效的账户,是的话选择高亮并进入提醒逻辑,不是的话就不管。

如果用户不是很多,也不用考虑验证的问题,直接简单粗暴地用正则表达式去匹配,得到结果就进入提醒逻辑,否则就不管
dong3580
2014-12-13 20:50:26 +08:00
@jox
如果用户很多怎么办?
例如微博,甚至企鹅。
lincanbin
2014-12-13 20:50:33 +08:00
https://github.com/lincanbin/Carbon-Forum/blob/master/common.php#L54
我自己的实现就是这样的,不这样做要怎么做?
jox
2014-12-13 20:56:32 +08:00
@dong3580 他们大概会做严肃的分析吧,也可能不会。我看新浪微博如果是 @@用户名 这样的是不会触发提醒的,也可能他们只是做简单的正则匹配,反正这个东西无所谓的吧,因为如果匹配失败的话发出去的微博就是普通的字符串,用户看见了就知道失败了,如果需要重新@的话就让用户重新弄一遍,我觉得没什么,正则匹配应该就够用了,性能方面也不是问题。腾讯微博我不知道,我因为最近开始使用v2ex才去申请了个新浪微博作为图床。
dong3580
2014-12-13 21:01:34 +08:00
@jox
猜测,@是不是只是一个被动的标志,普通的数据库(缓存)保存。用户只有刷新页面,才会收到@自己的消息或者留言(主动推送另当别论),这样,就像普通的查询数据库(缓存)一样。

至于上面童鞋说的正则匹配文本,似乎觉得效率会有问题。

事实上我并没做过类似的功能,仅仅是猜测。
Lucups
2014-12-13 21:02:39 +08:00
@lincanbin Good job! 已 fork 你的项目~ : )
jox
2014-12-13 21:09:15 +08:00
@dong3580 应该就是每个用户有个数据结构,类似队列那样的数据结构,被@的用户浏览某个或者某些特定的网页会消耗这个数据结构里的item,比如v2ex只有点开提醒那个页面才会消灭这些提醒,如果用户永远不点开,那么这个数据结构里的item就永远都不会消耗,伴随着用户直到他死去。每个item有自己的数据结构,里面可以保存url之类的数据,至于前端怎么展示,那就是前端的问题了,后端的逻辑应该就是这样的。
jox
2014-12-13 21:13:51 +08:00
@dong3580 正则匹配文本的效率不会是问题,从头到尾挨个字符遍历一遍就行了,这个过程可以非常快,疯狂的快,快到让人受不了!现在大点儿的互联网公司,这种操作对他们的服务器来说简直就是可以忽略到使劲掐手指来形容“小”都无法描述出有多么微不足道的那种程度。
kslr
2014-12-13 21:34:51 +08:00
@dong3580 我觉得匹配很适合B树,效率不会太差
akira
2014-12-13 21:38:10 +08:00
也只能这么做了吧。。
rainday
2014-12-13 22:05:54 +08:00
在牛客网 http://www.nowcoder.com 用户在我们网站提交的评论,我们会进行敏感词过滤,其算法是基于有限状态机DFA过滤的。 我觉得可以用敏感词过滤的方法来查找@的用户ID。 (用户ID就是好多敏感词), 具体的代码如下:
<code>
/**
* 过滤敏感词
*
* @param text
* @return
*/
public String filter(String text) {
if (StringUtils.isBlank(text)) {
return text;
}
String replacement = DEFAULT_REPLACEMENT;
StringBuilder result = new StringBuilder();

DFATreeNode tempNode = rootNode;
int begin = 0; // 回滚数
int position = 0; // 当前比较的位置

while (position < text.length()) {
char c = text.charAt(position);
// 空格直接跳过
if (isSymbol(c)) {
++position;
continue;
}

tempNode = tempNode.getSubNode(c);

// 当前位置的匹配结束
if (tempNode == null) {
// 以begin开始的字符串不存在敏感词
result.append(text.charAt(begin));
// 跳到下一个字符开始测试
position = begin + 1;
begin = position;
// 回到树初始节点
tempNode = rootNode;
} else if (tempNode.isKeywordEnd()) {
// 发现敏感词, 从begin到position的位置用replacement替换掉
result.append(replacement);
position = position + 1;
begin = position;
tempNode = rootNode;
} else {
++position;
}
}

result.append(text.substring(begin));

return result.toString();
}
</code>
RelativeLayout
2014-12-13 22:47:40 +08:00
一个正则就搞定了,拿去用吧

/@([^\s@]+)/gi
LukeXuan
2014-12-13 22:50:20 +08:00
不是一个基于trie的DFA就搞定了么……当然可以路径压缩一下速度更优……
imxz
2014-12-13 23:03:49 +08:00
正在继续写的BBS程序 http://letsbbs.com/ 实现逻辑基本如二楼所说,就是还没有加验证,直接往提醒系统里去了,这要谢谢 @jox 提醒,回头加上验证。

相关代码:
https://github.com/imxz/LetsBBS/blob/master/application/controllers/comment.php

希望高人来谈一谈,这样做是不是合理。
zzutmebwd
2014-12-14 08:47:50 +08:00
@jox 那么对于QQ.空间里那种用户昵称可重复/可备注时 如何确定唯一性呢
jox
2014-12-14 10:22:56 +08:00
@zzutmebwd 这个就看开发者怎么取舍了吧 看你把某个昵称的作用范围定为多大和你想拿@昵称用来干啥了 如果你说你要像微博那样拿一个昵称去@某用户 这个昵称既和发起@的用户没有任何联系同时还存在好多个用户拥有相同昵称 这是不能确定唯一的 同时这种功能也是没有意义的 但如果限定@昵称的作用范围就好办了 比如好友或者同一页面内的评论用户 如果作用域内存在重复 就捕捉用户的输入事件 提供个包含不同个体的菜单 用户必须在菜单内选择某选项@才会生效 否则就视为普通字符串
zzutmebwd
2014-12-14 10:25:58 +08:00
@jox TKS 得解。
ultraqs
2014-12-14 11:20:33 +08:00
@lincanbin 你是怎样防止注入的
lincanbin
2014-12-14 12:11:45 +08:00
@ultraqs 使用带参数绑定的数据库类,例如PDO或者MySQLi,这样命令和参数分离,也就无法在参数中构造命令。
直接搞引号过滤之类的是再愚蠢不过了。
这是我封装的PDO类: https://github.com/lincanbin/PHP-PDO-MySQL-Class

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/153711

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX