Non greedy正则表达式匹配怎么弄?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (10)
  • 关注 (0)
  • 查看 (129)

我图使用sed来清理URL行

因此:

http://www.suepearson.co.uk/product/174/71/3816/

我要:

http://www.suePearson.co.uk/

我试过:

 sed 's|\(http:\/\/.*?\/\).*|\1|'

sed 's|\(http:\/\/.*\?\/\).*|\1|'

但是我似乎不能让Non greedy的量词工作,所以它总是匹配整个字符串。

提问于
用户回答回答于

无论是基本的还是扩展的POSIX/GNU正则表达式都不会识别Non greedy的量词;你需要稍后的正则表达式。幸运的是,用于此上下文的Perl regex非常容易获得:

perl -pe 's|(http://.*?/).*|\1|'
用户回答回答于

sed当然有它的地位,但这不是他们中的一个!

正如Dee所指出的:只要使用cut在这种情况下,它要简单得多,也要安全得多。下面是一个使用Bash语法从URL中提取各种组件的示例:

url="http://www.suepearson.co.uk/product/174/71/3816/"

protocol=$(echo "$url" | cut -d':' -f1)
host=$(echo "$url" | cut -d'/' -f3)
urlhost=$(echo "$url" | cut -d'/' -f1-3)
urlpath=$(echo "$url" | cut -d'/' -f4-)

给你:

protocol = "http"
host = "www.suepearson.co.uk"
urlhost = "http://www.suepearson.co.uk"
urlpath = "product/174/71/3816/"

正如你所看到的,这是一个更灵活的方法。

用户回答回答于

SED-由Christoph Sieghart非匹配

在sed中获得non greedy匹配的诀窍是匹配所有字符,不包括终止匹配的字符。我知道,这是一个不费脑筋的人,但我浪费了宝贵的时间在上面,毕竟shell脚本应该是快速而简单的。所以万一有人需要它:

greedy匹配

% echo "<b>foo</b>bar" | sed 's/<.*>//g'
bar

non greedy匹配

% echo "<b>foo</b>bar" | sed 's/<[^>]*>//g'
foobar
用户回答回答于

另一种不使用regex的方法是使用字段/分隔符方法

string="http://www.suepearson.co.uk/product/174/71/3816/"
echo $string | awk -F"/" '{print $1,$2,$3}' OFS="/"
用户回答回答于

模拟sed 中的 lazy (un-greedy)量词。

还有其他的regex口味!

  1. 找到表达式的第一次出现:
    • POSIX e(使用-r(备选方案)

Regex:

(EXPRESSION).*.

SED:

SED-r“s/(EXPRESSION)*./\1/g“#Globalg修饰符应该打开

示例:

$ sed -r "s/([0-9]+).*|./\1/g" <<< "foo 12 bar 34"

其工作原理如何呢??

这个正则表达式从交替中受益。|在每一个位置,引擎都会寻找第一面的交替(我们的目标),如果它不匹配,第二面的交替,其中有一个点。.匹配下一个即时字符。

由于设置了全局标志,引擎将继续逐字符匹配,直到输入字符串或目标结束为止。一旦第一次也是唯一一次捕获左侧交替组的匹配(EXPRESSION)其余部分也会立即消耗.*我们现在在第一个捕获组中保留我们的值。

POSIX BRE

Regex:

\(\(\(EXPRESSION\).*\)*.\)*

Sed:

sed "s/\(\(\(EXPRESSION\).*\)*.\)*/\3/"

Example (finding first sequence of digits):

$ sed "s/\(\(\([0-9]\{1,\}\).*\)*.\)*/\3/" <<< "foo 12 bar 34"

这个版本类似于Ere版本,但没有任何修改。仅此而已。在每个位置,引擎都试图匹配一个数字

foo 12 bar 34

如果找到了,其他的数字将被消耗和捕获,其余的行将立即匹配,因为*手段多或零它跳过第二个捕获组。\(\([0-9]\{1,\}\).*\)*到达一个点.若要匹配单个字符,则此过程将继续。二次第一次发现定界表达:

此方法将与第一次出现的字符串进行匹配,该字符串是分隔的。我们可以称它为字符串块。

sed "s/\(END-DELIMITER-EXPRESSION\).*/\1/; \ s/\(\(START-DELIMITER-EXPRESSION.*\)*.\)*/\1/g"

输入字符串:

foobar start block #1 end barfoo start block #2 end

-EDE:end

-SDE:start

$ sed "s/\(end\).*/\1/; s/\(\(start.*\)*.\)*/\1/g"

输出:

start block #1 end

第一判据\(end\).*匹配和捕获第一个结束分隔符end并替换所有替换与最近捕获的字符,这是结束分隔符。在现阶段,我们的产出是:foobar start block #1 end...

然后将结果传递给第二正则表达式。\(\(start.*\)*.\)*这与上述POSIX BRE版本相同。如果开始分隔符,则匹配单个字符。start不匹配,否则它将匹配和捕获开始分隔符,并匹配其余字符。

直接回答你的问题

使用方法2(分隔表达式),你应该选择两个适当的表达式:

  • Ede:[^:/]\/
  • SDE:http:

用法:

$ sed "s/\([^:/]\/\).*/\1/g; s/\(\(http:.*\)*.\)*/\1/" <<< "http://www.suepearson.co.uk/product/174/71/3816/"

输出:

http://www.suepearson.co.uk/
用户回答回答于

这可以使用裁剪来完成:

echo "http://www.suepearson.co.uk/product/174/71/3816/" | cut -d'/' -f1-3
用户回答回答于

多个字符的Non greedy解决方案

这条线确实很旧,但我想人们仍然需要它。假设你想要杀死一切直到第一次发生HELLO你不能说[^HELLO]...

因此,一个很好的解决方案包括两个步骤,假设您可以在输入中保留一个你不期望的惟一字符,例如`(倒钩)

在这种情况下,我们可以:

s_HELLO_`_     #will only replace the very first occurrence
s_.*`__        #kill everything till end of the first HELLO
用户回答回答于

SED不支持“Non greedy”运算符。

你必须用“。[]“运算符将”/“从匹配中排除”。

sed 's,\(http://[^/]*\)/.*,\1,'

没有必要用反斜杠“/”。

用户回答回答于

使用sed,我通常通过搜索除分隔符以外的任何内容来实现Non greedy搜索,直到分隔符:

echo "http://www.suon.co.uk/product/1/7/3/" | sed -n 's;\(http://[^/]*\)/.*;\1;p'

输出:

http://www.suon.co.uk

这是:

  • 不输出-n
  • 搜索,匹配模式,替换和打印s/<pattern>/<replace>/p
  • 使用;搜索命令分隔符而不是命令分隔符/以便更容易地键入s;<pattern>;<replace>;p
  • 记住括号之间的匹配\(...\),稍后可通过\1,,,\2...
  • 匹配http://
  • 后面跟着括号里的任何东西[],,,[ab/]意味着ab/
  • 第一^[]手段not,所以后面跟着的除了[]
  • 所以[^/]意味着除了/
  • *是重复以前的组,所以[^/]*意思是除了字符/...
  • 到目前为止sed -n 's;\(http://[^/]*\)意思是搜索和记忆http://后面跟着任何字符,除非/记住你发现了什么
  • 我们想要搜索到域的末尾,所以在下一个域上停止搜索。/所以再加一个/最后:sed -n 's;\(http://[^/]*\)/'但是我们想要在域后面匹配行的其余部分,所以请添加.*
  • 现在第一组的比赛被记住了(\1)是域,所以用组中保存的内容替换匹配行\1和印刷品:sed -n 's;\(http://[^/]*\)/.*;\1;p'

如果你也想在域后面包括反斜杠,那么在组中再添加一个反斜杠以记住:

echo "http://www.suon.co.uk/product/1/7/3/" | sed -n 's;\(http://[^/]*/\).*;\1;p'

输出:

http://www.suon.co.uk/
用户回答回答于

试一试[^/]*而不是.*?:

sed 's|\(http://[^/]*/\).*|\1|g'

扫码关注云+社区

领取腾讯云代金券