V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
viskem
V2EX  ›  PHP

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

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

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

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

    事实上我并没做过类似的功能,仅仅是猜测。
    Lucups
        7
    Lucups  
       2014-12-13 21:02:39 +08:00   ❤️ 1
    @lincanbin Good job! 已 fork 你的项目~ : )
    jox
        8
    jox  
       2014-12-13 21:09:15 +08:00   ❤️ 1
    @dong3580 应该就是每个用户有个数据结构,类似队列那样的数据结构,被@的用户浏览某个或者某些特定的网页会消耗这个数据结构里的item,比如v2ex只有点开提醒那个页面才会消灭这些提醒,如果用户永远不点开,那么这个数据结构里的item就永远都不会消耗,伴随着用户直到他死去。每个item有自己的数据结构,里面可以保存url之类的数据,至于前端怎么展示,那就是前端的问题了,后端的逻辑应该就是这样的。
    jox
        9
    jox  
       2014-12-13 21:13:51 +08:00   ❤️ 1
    @dong3580 正则匹配文本的效率不会是问题,从头到尾挨个字符遍历一遍就行了,这个过程可以非常快,疯狂的快,快到让人受不了!现在大点儿的互联网公司,这种操作对他们的服务器来说简直就是可以忽略到使劲掐手指来形容“小”都无法描述出有多么微不足道的那种程度。
    kslr
        10
    kslr  
       2014-12-13 21:34:51 +08:00 via Android   ❤️ 1
    @dong3580 我觉得匹配很适合B树,效率不会太差
    akira
        11
    akira  
       2014-12-13 21:38:10 +08:00   ❤️ 1
    也只能这么做了吧。。
    rainday
        12
    rainday  
       2014-12-13 22:05:54 +08:00   ❤️ 1
    在牛客网 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
        13
    RelativeLayout  
       2014-12-13 22:47:40 +08:00   ❤️ 1
    一个正则就搞定了,拿去用吧

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

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

    希望高人来谈一谈,这样做是不是合理。
    zzutmebwd
        16
    zzutmebwd  
       2014-12-14 08:47:50 +08:00 via Android   ❤️ 1
    @jox 那么对于QQ.空间里那种用户昵称可重复/可备注时 如何确定唯一性呢
    jox
        17
    jox  
       2014-12-14 10:22:56 +08:00 via iPhone   ❤️ 1
    @zzutmebwd 这个就看开发者怎么取舍了吧 看你把某个昵称的作用范围定为多大和你想拿@昵称用来干啥了 如果你说你要像微博那样拿一个昵称去@某用户 这个昵称既和发起@的用户没有任何联系同时还存在好多个用户拥有相同昵称 这是不能确定唯一的 同时这种功能也是没有意义的 但如果限定@昵称的作用范围就好办了 比如好友或者同一页面内的评论用户 如果作用域内存在重复 就捕捉用户的输入事件 提供个包含不同个体的菜单 用户必须在菜单内选择某选项@才会生效 否则就视为普通字符串
    zzutmebwd
        18
    zzutmebwd  
       2014-12-14 10:25:58 +08:00   ❤️ 1
    @jox TKS 得解。
    ultraqs
        19
    ultraqs  
       2014-12-14 11:20:33 +08:00   ❤️ 1
    @lincanbin 你是怎样防止注入的
    lincanbin
        20
    lincanbin  
       2014-12-14 12:11:45 +08:00   ❤️ 1
    @ultraqs 使用带参数绑定的数据库类,例如PDO或者MySQLi,这样命令和参数分离,也就无法在参数中构造命令。
    直接搞引号过滤之类的是再愚蠢不过了。
    这是我封装的PDO类: https://github.com/lincanbin/PHP-PDO-MySQL-Class
    c4pt0r
        21
    c4pt0r  
       2014-12-14 14:23:16 +08:00   ❤️ 1
    发帖的时候正则,提取出 @ 然后把帖子的 id 丢到一个提醒的队列里(这里的队列可以是数据库的一个表,Redis Queue,whatever)。
    然后通知一下在线的客户端(or web)的长连接,让客户端过来拉取这是 push 的方式,长连接不想做的话换成 pull 的方式也行 。
    iugo
        22
    iugo  
       2014-12-16 14:36:43 +08:00   ❤️ 1
    我觉得用 PHP 实现得比较好的是 esoTalk 这个东西.
    https://github.com/esotalk/esoTalk
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   995 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 21:37 · PVG 05:37 · LAX 13:37 · JFK 16:37
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.