如何修复我的正则表达式,使其不与贪婪量词匹配太多?

发布于 2024-07-08 10:02:26 字数 325 浏览 10 评论 0原文

我有以下行:

"14:48 say;0ed673079715c343281355c2a1fde843;2;laka;hello ;)"

我通过使用简单的正则表达式来解析它:

if($line =~ /(\d+:\d+)\ssay;(.*);(.*);(.*);(.*)/) {
    my($ts, $hash, $pid, $handle, $quote) = ($1, $2, $3, $4, $5);
}

但是 ; 最后把事情搞砸了,我不知道为什么。 贪心运算符不应该处理“一切”吗?

I have the following line:

"14:48 say;0ed673079715c343281355c2a1fde843;2;laka;hello ;)"

I parse this by using a simple regexp:

if($line =~ /(\d+:\d+)\ssay;(.*);(.*);(.*);(.*)/) {
    my($ts, $hash, $pid, $handle, $quote) = ($1, $2, $3, $4, $5);
}

But the ; at the end messes things up and I don't know why. Shouldn't the greedy operator handle "everything"?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

贪婪运算符试图获取尽可能多的东西,并且仍然与字符串匹配。 发生的情况是第一个(在“say”之后)获取“0ed673079715c343281355c2a1fde843;2”,第二个获取“laka”,第三个找到“hello”,第四个匹配括号。

你需要做的是让除了最后一个之外的所有都变得非贪婪,这样它们就会尽可能少地抓取并仍然匹配字符串:

(\d+:\d+)\ssay;(.*?);(.*?);(.*?);(.*)

The greedy operator tries to grab as much stuff as it can and still match the string. What's happening is the first one (after "say") grabs "0ed673079715c343281355c2a1fde843;2", the second one takes "laka", the third finds "hello " and the fourth matches the parenthesis.

What you need to do is make all but the last one non-greedy, so they grab as little as possible and still match the string:

(\d+:\d+)\ssay;(.*?);(.*?);(.*?);(.*)
苏别ゝ 2024-07-15 10:02:26
(\d+:\d+)\ssay;([^;]*);([^;]*);([^;]*);(.*)

应该工作得更好

(\d+:\d+)\ssay;([^;]*);([^;]*);([^;]*);(.*)

should work better

旧瑾黎汐 2024-07-15 10:02:26

尽管正则表达式可以轻松做到这一点,但我不确定这是最直接的方法。 它可能是最短的,但这实际上并不意味着它是最可维护的。

相反,我建议这样:

$x="14:48 say;0ed673079715c343281355c2a1fde843;2;laka;hello ;)";

if (($ts,$rest) = $x =~ /(\d+:\d+)\s+(.*)/)
{
    my($command,$hash,$pid,$handle,$quote) = split /;/, $rest, 5;
    print join ",", map { "[$_]" } $ts,$command,$hash,$pid,$handle,$quote
}

这会导致:

[14:48],[say],[0ed673079715c343281355c2a1fde843],[2],[laka],[hello ;)]

我认为这更具可读性。 不仅如此,我认为它也更容易调试和维护,因为这更接近于人类用笔和纸尝试同样的事情时的做法。 将字符串分解为多个块,以便您可以更轻松地解析 - 让计算机完全按照您的操作进行操作。 当需要进行修改时,我认为这个会表现得更好。 YMMV。

Although a regex can easily do this, I'm not sure it's the most straight-forward approach. It's probably the shortest, but that doesn't actually make it the most maintainable.

Instead, I'd suggest something like this:

$x="14:48 say;0ed673079715c343281355c2a1fde843;2;laka;hello ;)";

if (($ts,$rest) = $x =~ /(\d+:\d+)\s+(.*)/)
{
    my($command,$hash,$pid,$handle,$quote) = split /;/, $rest, 5;
    print join ",", map { "[$_]" } $ts,$command,$hash,$pid,$handle,$quote
}

This results in:

[14:48],[say],[0ed673079715c343281355c2a1fde843],[2],[laka],[hello ;)]

I think this is just a bit more readable. Not only that, I think it's also easier to debug and maintain, because this is closer to how you would do it if a human were to attempt the same thing with pen and paper. Break the string down into chunks that you can then parse easier - have the computer do exactly what you would do. When it comes time to make modifications, I think this one will fare better. YMMV.

断念 2024-07-15 10:02:26

尝试使前 3 个 (.*) 变得不贪婪 (.*?)

Try making the first 3 (.*) ungreedy (.*?)

时间你老了 2024-07-15 10:02:26

如果分号分隔列表中的值本身不能包含任何分号,则只需将其拼写出来即可获得最有效、最简单的正则表达式。 如果某些值只能是十六进制字符的字符串,请将其拼写出来。 当正则表达式与主题字符串不匹配时,使用惰性点或贪婪点的解决方案总是会导致大量无用的回溯。

(\d+:\d+)\ssay;([a-f0-9]+);(\d+);(\w+);([^;\r\n]+)

If the values in your semicolon-delimited list cannot include any semicolons themselves, you'll get the most efficient and straightforward regular expression simply by spelling that out. If certain values can only be, say, a string of hex characters, spell that out. Solutions using a lazy or greedy dot will always lead to a lot of useless backtracking when the regex does not match the subject string.

(\d+:\d+)\ssay;([a-f0-9]+);(\d+);(\w+);([^;\r\n]+)
划一舟意中人 2024-07-15 10:02:26

您可以通过附加问号来使 * 非贪婪:

$line =~ /(\d+:\d+)\ssay;(.*?);(.*?);(.*?);(.*)/

或者您可以匹配除最后一个部分之外的每个部分中除分号之外的所有内容:

$line =~ /(\d+:\d+)\ssay;([^;]*);([^;]*);([^;]*);(.*)/

You could make * non-greedy by appending a question mark:

$line =~ /(\d+:\d+)\ssay;(.*?);(.*?);(.*?);(.*)/

or you can match everything except a semicolon in each part except the last:

$line =~ /(\d+:\d+)\ssay;([^;]*);([^;]*);([^;]*);(.*)/
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文