正则表达式非贪婪地匹配多行直到以特定字符串开头的行
Regex to non-greedily match across multiple lines up to a line that starts with a specific string
我要自己回答这个问题,但这让我整天都不舒服,虽然在其他地方有解释,但我想我会 post 用我的解决方案来解决这个问题。
我遇到过需要替换一些跨越多行的文本的情况。不难找到有关如何跨多行匹配的主题。我的情况有点困难,因为我需要通配符匹配多行中的任何字符,直到停在第一个非缩进的结束括号处。
出于演示目的,我制作了一个示例文件,其中包含让我难以理解的功能:
起始文件:
cat << EOF > test.txt
server {
abcdefg blablablabla
pizza
#blablablabla
blablablabla {
zazazazazaza
}
turtles
#}
ninjas
blablablabla
} #comments that might or might not be here
server {
blablablabla
blablablabla
blablablabla
blablablabla
}
zabzazab
EOF
这是我想要的输出。请注意,我要匹配的括号既不是右括号的第一个也不是最后一个。它唯一的显着特征是在我的比赛开始后成为一行开头的第一个 }
:
server {
wxyz
server {
blablablabla
blablablabla
blablablabla
blablablabla
}
zabzazab
我希望能奏效。但是用 0777
slupring 去除了一行开头和结尾的标记,所以它不起作用:
~# perl -0777 -pe 's/(abcdefg(.*?)(^}.*$))/wxyz/gs' test.txt
server {
abcdefg blablablabla
pizza
#blablablabla
blablablabla {
zazazazazaza
}
turtles
#}
ninjas
blablablabla
} #comments that might or might not be here
server {
blablablabla
blablablabla
blablablabla
blablablabla
}
zabzazab
匹配线 start/end 同时 slupring 是症结所在:
~# perl -0777 -pe 's/(abcdefg(.*?)(}))/wxyz/gs' test.txt
server {
wxyz
turtles
#}
ninjas
blablablabla
} #comments that might or might not be here
server {
blablablabla
blablablabla
blablablabla
blablablabla
}
zabzazab
那么有没有办法让正则表达式匹配字符串和出现在行首的 {
的第一个实例?我也愿意使用 sed,但我认为我搜索的非贪婪性质会使 perl 成为更好的选择。
看来我需要 s 和 m 标志以及 slurping:
~# perl -0777 -pe 's/(abcdefg(.+?)(\n}))/wxyz/sm' test.txt
server {
wxyz #comments that might or might not be here
server {
blablablabla
blablablabla
blablablabla
blablablabla
}
我仍然不太明白为什么我需要 m
修饰符和 slurping。所以如果有人有更好的答案,我会标记那个而不是我自己的。
据我了解,您希望匹配字符串的一部分
server {
abcdefg blablablabla
pizza
#blablablabla
blablablabla {
zazazazazaza
}
turtles
#}
ninjas
blablablabla
} #comments that might or might not be here
server {
... blablablabla
}
...
从 "abcdefg"
开始并在行尾 "} #comments that might or might not be here"
结束,前提是 "abcdefg"
在缩进后开始一行并且该行前面是行 "server {"
.然后,您将用另一个字符串替换匹配的文本。
您可以使用以下正则表达式匹配要替换的文本:
/^server +\{\s+(abcdefg.+?\n\}.*?$)/sm
标志 s
允许 .*
匹配换行符。标志 m
指示解析器将锚点 ^
和 $
分别视为一行的开头和结尾(大概与字符串的开头和结尾相反)。
我们可以在自由间距模式中编写正则表达式,使其自记录。
/
^server +\{\s+ # match 'server {` followed by 1+
# whitespace chars
( # begin capture group 1
abcdefg # match literal
.+? # match 1+ chars, lazily
\n # match a newline
\} # match '}'
.*? # match 1+ chars, lazily
$ # match end of line
) # end capture group 1
/smx # single-line, multiline and free-
# spacing regex definition modes
也许以下任何命令都可以做到
perl -0777 -pe 's/abcdefg.*?(\nserver.*?)/wxyz\n/s' test.txt
perl -0777 -pe 's/abcdefg.*?server/wxyz\n\nserver/s' text.txt
perl -0777 -pe 's/abcdefg.*?}.*?}.*?}.*?\n/wxyz\n/s' test.txt
perl -0777 -pe 's/abcdefg(.*?}){3}.*?\n/wxyz\n/s' test.txt
perl -0777 -pe 's/abcdefg.*?\n}.*?\n/wxyz\n/s' test.txt
输出
server {
wxyz
server {
blablablabla
blablablabla
blablablabla
blablablabla
}
zabzazab
我要自己回答这个问题,但这让我整天都不舒服,虽然在其他地方有解释,但我想我会 post 用我的解决方案来解决这个问题。
我遇到过需要替换一些跨越多行的文本的情况。不难找到有关如何跨多行匹配的主题。我的情况有点困难,因为我需要通配符匹配多行中的任何字符,直到停在第一个非缩进的结束括号处。
出于演示目的,我制作了一个示例文件,其中包含让我难以理解的功能:
起始文件:
cat << EOF > test.txt
server {
abcdefg blablablabla
pizza
#blablablabla
blablablabla {
zazazazazaza
}
turtles
#}
ninjas
blablablabla
} #comments that might or might not be here
server {
blablablabla
blablablabla
blablablabla
blablablabla
}
zabzazab
EOF
这是我想要的输出。请注意,我要匹配的括号既不是右括号的第一个也不是最后一个。它唯一的显着特征是在我的比赛开始后成为一行开头的第一个 }
:
server {
wxyz
server {
blablablabla
blablablabla
blablablabla
blablablabla
}
zabzazab
我希望能奏效。但是用 0777
slupring 去除了一行开头和结尾的标记,所以它不起作用:
~# perl -0777 -pe 's/(abcdefg(.*?)(^}.*$))/wxyz/gs' test.txt
server {
abcdefg blablablabla
pizza
#blablablabla
blablablabla {
zazazazazaza
}
turtles
#}
ninjas
blablablabla
} #comments that might or might not be here
server {
blablablabla
blablablabla
blablablabla
blablablabla
}
zabzazab
匹配线 start/end 同时 slupring 是症结所在:
~# perl -0777 -pe 's/(abcdefg(.*?)(}))/wxyz/gs' test.txt
server {
wxyz
turtles
#}
ninjas
blablablabla
} #comments that might or might not be here
server {
blablablabla
blablablabla
blablablabla
blablablabla
}
zabzazab
那么有没有办法让正则表达式匹配字符串和出现在行首的 {
的第一个实例?我也愿意使用 sed,但我认为我搜索的非贪婪性质会使 perl 成为更好的选择。
看来我需要 s 和 m 标志以及 slurping:
~# perl -0777 -pe 's/(abcdefg(.+?)(\n}))/wxyz/sm' test.txt
server {
wxyz #comments that might or might not be here
server {
blablablabla
blablablabla
blablablabla
blablablabla
}
我仍然不太明白为什么我需要 m
修饰符和 slurping。所以如果有人有更好的答案,我会标记那个而不是我自己的。
据我了解,您希望匹配字符串的一部分
server {
abcdefg blablablabla
pizza
#blablablabla
blablablabla {
zazazazazaza
}
turtles
#}
ninjas
blablablabla
} #comments that might or might not be here
server {
... blablablabla
}
...
从 "abcdefg"
开始并在行尾 "} #comments that might or might not be here"
结束,前提是 "abcdefg"
在缩进后开始一行并且该行前面是行 "server {"
.然后,您将用另一个字符串替换匹配的文本。
您可以使用以下正则表达式匹配要替换的文本:
/^server +\{\s+(abcdefg.+?\n\}.*?$)/sm
标志 s
允许 .*
匹配换行符。标志 m
指示解析器将锚点 ^
和 $
分别视为一行的开头和结尾(大概与字符串的开头和结尾相反)。
我们可以在自由间距模式中编写正则表达式,使其自记录。
/
^server +\{\s+ # match 'server {` followed by 1+
# whitespace chars
( # begin capture group 1
abcdefg # match literal
.+? # match 1+ chars, lazily
\n # match a newline
\} # match '}'
.*? # match 1+ chars, lazily
$ # match end of line
) # end capture group 1
/smx # single-line, multiline and free-
# spacing regex definition modes
也许以下任何命令都可以做到
perl -0777 -pe 's/abcdefg.*?(\nserver.*?)/wxyz\n/s' test.txt
perl -0777 -pe 's/abcdefg.*?server/wxyz\n\nserver/s' text.txt
perl -0777 -pe 's/abcdefg.*?}.*?}.*?}.*?\n/wxyz\n/s' test.txt
perl -0777 -pe 's/abcdefg(.*?}){3}.*?\n/wxyz\n/s' test.txt
perl -0777 -pe 's/abcdefg.*?\n}.*?\n/wxyz\n/s' test.txt
输出
server {
wxyz
server {
blablablabla
blablablabla
blablablabla
blablablabla
}
zabzazab