非贪婪的正则表达式只匹配1个字符

时间:2016-11-23 23:00:22

标签: c# regex perl

A有一个文件列表,其中一些名称后缀为.cloud。如何编写一个正则表达式来获取没有.cloud部分的文件名?

这是我尝试的perl脚本示例。

#! /usr/bin/perl -w

my @log_files = ('infolog.txt', 'errorlog.txt.cloud', 'dailyerrorlog.txt.cloud', 'trace.output.cloud', 'debug.log.cloud');

foreach my $file (@log_files)
{
    print $1."\n" if($file =~ /(.+?)(?:\.cloud)?/);
}

这将打印以下内容:

$ perl test.pl 
i
e
d
t
d

如果我摆脱了'?'这使得。+贪婪,它匹配所有东西,包括.cloud。

#! /usr/bin/perl -w

my @log_files = ('infolog.txt', 'errorlog.txt.cloud', 'dailyerrorlog.txt.cloud', 'trace.output.cloud', 'debug.log.cloud');

foreach my $file (@log_files)
{
    print $1."\n" if($file =~ /(.+)(?:\.cloud)?/);
}

这将打印以下内容:

$ perl test.pl 
infolog.txt
errorlog.txt.cloud
dailyerrorlog.txt.cloud
trace.output.cloud
debug.log.cloud

我真正想要的是一个正则表达式,打印出来:

$ perl test.pl 
infolog.txt
errorlog.txt
dailyerrorlog.txt
trace.output
debug.log

我已经将我的真实用例修改为一个非常简单的例子。我需要在这里使用正则表达式来匹配文件名,所以像

这样的答案
$file =~ s/\.cloud$//;
print $file."\n";

对我不起作用。

我也在C#中尝试了类似的事情,结果相似。

    static void Main(string[] args)
    {
        Regex regex = new Regex(@"(?<filename>.+?)(?:\.cloud)?");
        string text = "abcdef.txt.cloud";
        Match match = regex.Match(text);
        if(match.Success)
        {
            Console.WriteLine("Found filename: {0}", match.Groups["filename"].Value);
        }
    }
    // Output
    // Found filename: a

感谢您的帮助。

4 个答案:

答案 0 :(得分:4)

如果指定整个字符串必须匹配,则通常更容易阅读/维护正则表达式。这对^$很容易,它与字符串的开头和结尾相匹配。

匹配字符串中的任意位置:(.+?)(?:\.cloud)?

匹配整个字符串:^(.+?)(?:\.cloud)?$

在第二种情况下,非贪婪组将尽可能少地捕获,但需要捕获多个字符以满足匹配条件。

这并不涵盖所有可能的用例,但它往往会导致正则表达式在六个月后更容易阅读。

答案 1 :(得分:1)

它只匹配一个字符,因为您告诉它匹配尽可能少的字符数,并且.+不允许匹配零个字符。

我将使用$PAT代替.+,因为你说这是一个更复杂的替代品。

尽管您声称s///无法使用,但它似乎仍然是我最简单的解决方案。

my ($match) = map { s/\.cloud\z//r } $file =~ /^($PAT)\z/;  # 5.14+

my ($match) = map { ( my $s = $_ ) =~ s/\.cloud\z//; $s } $file =~ /^($PAT)\z/;

也就是说,也可以使用匹配来实现:

my $match = $file =~ /^(?:($PAT)(?=\.cloud\z)|($PAT))/ ? ($1 // $2) : undef;

顺便说一下,如果$PAT.+,并且我想使用匹配,我会使用以下内容:

my ($match) = $file =~ /^((?:(?!\.cloud\z).)+)/s;

但使用

会更简单
my $match = $file =~ s/\.cloud\z//r;   # 5.14+

(my $match = $file) =~ s/\.cloud\z//;

答案 2 :(得分:0)

您的模式仅匹配单个字符的原因是子模式(?:\.cloud)?是可选的,因此可以完全满足它。这使得(.+?)可以自由匹配+量词所允许的最短字符串,这是一个字符

通过锚定模式的结尾以使其必须匹配整个字符串

,可以轻松解决此问题。

此代码可以正常使用

use strict;
use warnings 'all';

my @log_files = qw/
    infolog.txt
    errorlog.txt.cloud
    dailyerrorlog.txt.cloud
    trace.output.cloud
    debug.log.cloud
/;

for ( @log_files ) {
    print "$1\n" if /(.+?)(?:\.cloud)?$/;
}

输出

infolog.txt
errorlog.txt
dailyerrorlog.txt
trace.output
debug.log

答案 3 :(得分:0)

将文件名分配给临时变量并进行修改。

my @log_files = qw(
    infolog.txt
    errorlog.txt.cloud
    dailyerrorlog.txt.cloud
    trace.output.cloud
    debug.log.cloud
);

foreach my $file (@log_files)
{
    my $tmpry = $file;
    $tmpry =~ s/\.cloud$//;
    printf "%-25s %s\n", $file, $tmpry;
}
相关问题