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

正则替换 ,不替换 a 标签内的字符

  •  
  •   dante3imin · 2017-09-04 22:46:55 +08:00 · 5591 次点击
    这是一个创建于 2638 天前的主题,其中的信息可能已经有所发展或是发生改变。

    $str = "<a name='房间' href='...'>房间</a>深刻的房间"; 现在需要替换除 a 标签以外的指定字符。如‘房间’两个字(只替换第三个)

    29 条回复    2017-09-11 15:16:29 +08:00
    blahgeek
        1
    blahgeek  
       2017-09-04 23:06:40 +08:00
    正则的表达能力应该无法完全表达 HTML 语法 总能找到 failure case
    dante3imin
        2
    dante3imin  
    OP
       2017-09-04 23:16:44 +08:00
    @blahgeek 额~有什么其他方案么
    shiny
        3
    shiny  
       2017-09-04 23:19:50 +08:00
    phpQuery
    bombless
        4
    bombless  
       2017-09-05 02:38:52 +08:00 via Android
    拟个 bnf 写个简单的递归下降吧哈哈哈
    blanu
        5
    blanu  
       2017-09-05 04:15:07 +08:00 via iPhone
    我只知道 npm 上这种轮子很多…
    Perry
        6
    Perry  
       2017-09-05 04:33:04 +08:00   ❤️ 1
    XPath
    2ME
        7
    2ME  
       2017-09-05 08:47:36 +08:00
    notolddriver
        8
    notolddriver  
       2017-09-05 09:02:05 +08:00 via iPhone
    使用向后匹配 ?<=匹配 a 标签的全部内容,后面再跟你要替换的字符,不知可否
    zgx030030
        9
    zgx030030  
       2017-09-05 09:16:11 +08:00
    @notolddriver 不行,你这样也可能匹配上在 a 标签内的字符。
    airycanon
        10
    airycanon  
       2017-09-05 09:29:51 +08:00   ❤️ 1
    先把带房间的 a 标签临时换成其他的唯一字符,然后全文替换房间,然后再把 a 标签还原?
    wlchn
        11
    wlchn  
       2017-09-05 10:24:06 +08:00   ❤️ 1
    单就你给的测试用例,写一个正则表达式可能并不难,复杂的是需不需要更多的 case。
    针对你的测试用例,使用该正则就可以,附上测试连接( Ruby )
    a>[^<>]*?(?<name>房间)
    测试连接: http://rubular.com/r/YFnQGTwzk2
    dante3imin
        12
    dante3imin  
    OP
       2017-09-05 10:43:50 +08:00
    @shiny 这个抓取倒是用过,不过我这个需求好像不太实用
    dante3imin
        13
    dante3imin  
    OP
       2017-09-05 10:44:52 +08:00
    @airycanon 这个方案也想过,还是觉得不太可行
    notolddriver
        14
    notolddriver  
       2017-09-05 11:40:50 +08:00 via iPhone
    可不可以多给点样本……
    VgV
        15
    VgV  
       2017-09-05 12:28:38 +08:00
    我好奇的是,已经知道具体字符串了,为何还要用正则??
    str_replace 不行吗?
    sola97
        16
    sola97  
       2017-09-05 13:16:05 +08:00
    感觉 10 楼的可行
    zgx030030
        17
    zgx030030  
       2017-09-05 14:12:13 +08:00
    http://rubular.com/r/1dsQ7BEuzo

    (?<!['"])房间(?!<\/a>)
    这样虽然上面的例子是可以匹配到。。。感觉还是不够灵活啊
    lieh222
        18
    lieh222  
       2017-09-05 14:42:39 +08:00
    楼主这个为啥要用正则,写个递归,先查找<a 的索引,如果没有直接替换所有,如果有替换开始到这的内容,然后找到</a>索引,取出这部分来,剩下的继续递归
    gnaggnoyil
        19
    gnaggnoyil  
       2017-09-05 14:53:49 +08:00
    @lieh222 $str="<a name='</a>' href='./page.html'>foo_bar</a>"
    请开始你的表演.

    这种需求 LZ 如果不想找现成的轮子用的话就做好手撸一个完整的 HTML parser 的准备吧……
    UnknownR
        20
    UnknownR  
       2017-09-05 15:15:52 +08:00
    以前写过类似的脚本,如果楼主是想替换标签间的房间的话,可以匹配>和<字符间的任意非数字与字母,这样可以替换第二个,想替换第三个的话可以匹配"</a>"这一关键字符串三个字符长度后的任意字符
    lieh222
        21
    lieh222  
       2017-09-05 15:20:08 +08:00
    @gnaggnoyil 打脸打的好
    $str="<a name='</a>aaa 房间' href='./page.html'>foo_bar</a>nnn 房间"
    11、17、18 全不过
    autoxbc
        22
    autoxbc  
       2017-09-05 15:38:16 +08:00
    我一直觉得用正则处理 html 是有问题的

    html 是结构化的数据,要想代码写的简洁有语义而健壮
    先解析成结构数据,再用 XPath 和 CSS Selectors 最好

    就像上面说的,强行用正则处理结构化的数据
    必然不自觉的在正则里自己实现了 html parser
    lzjV2EX
        23
    lzjV2EX  
       2017-09-05 16:52:35 +08:00
    1、先使用正则替换 a 标签里面的 指定字符 成某个 标识符比如 ###。
    lzjV2EX
        24
    lzjV2EX  
       2017-09-05 16:54:21 +08:00
    @lzjV2EX 2、再把字符串里面的指定字符替换掉。
    3、再把 标识符 替换成指定字符。
    这样行不行?
    eecjimmy
        25
    eecjimmy  
       2017-09-05 18:00:52 +08:00 via iPhone
    @lzjV2EX 思路没毛病
    ZxBing0066
        26
    ZxBing0066  
       2017-09-05 18:13:24 +08:00
    cheerio 解析 html 然后使用 jquery 查找 a 标签临近的文本节点
    WheatField
        27
    WheatField  
       2017-09-06 20:58:15 +08:00 via iPhone
    @lieh222 但是也不会有这样的 name 属性值吧,这样的$str = "<a name='</a>aaa 房间' href='./page.html'>foo_bar</a>nnn 房间" ,浏览器也不能解析吧?
    WheatField
        28
    WheatField  
       2017-09-06 21:33:33 +08:00
    @airycanon 你的想法,我觉得很赞。
    支持 10 楼的方法
    abccccabc
        29
    abccccabc  
       2017-09-11 15:16:29 +08:00
    楼主,试下:
    (?<!['"])房间(?!(<\/a>|'|"))

    将 17 楼的改动下就行了,但你给的例子和发贴的例子标签不一样,所以导致 17 楼的规则不生效。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   898 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 20:49 · PVG 04:49 · LAX 12:49 · JFK 15:49
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.