dante3imin
V2EX  ›  PHP

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

By dante3imin at 2017 年 9 月 4 日 · 6811 次点击

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

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

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

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

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

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

将 17 楼的改动下就行了,但你给的例子和发贴的例子标签不一样,所以导致 17 楼的规则不生效。
© 2026 V2EX · 73ms · 3.9.8.5