Regex 如何在此特定文本上使用正则表达式捕获多个单词?

Regex 如何在此特定文本上使用正则表达式捕获多个单词?,regex,perl,Regex,Perl,我试图从以下示例文本中提取薪酬最高的职务: 数据科学家 #在收入最高的工作中排名第一 5100个预计就业岗位250000美元工资中位数0.5%失业率 程序员 #2.从事报酬最高的工作 4000个预计就业岗位24万美元工资中位数1.0%失业率 SAP模块顾问 #3.薪水最高的工作 3000个预计工作22万美元工资中位数0.2%失业率 通过使用以下正则表达式和Perl代码 使用File::Glob; 本地$/=undef; 我的$file=@ARGV[0]; 打开输入,“为什么不逐行处理,简单易行

我试图从以下示例文本中提取薪酬最高的职务:

数据科学家 #在收入最高的工作中排名第一 5100个预计就业岗位250000美元工资中位数0.5%失业率 程序员 #2.从事报酬最高的工作 4000个预计就业岗位24万美元工资中位数1.0%失业率 SAP模块顾问 #3.薪水最高的工作 3000个预计工作22万美元工资中位数0.2%失业率 通过使用以下正则表达式和Perl代码

使用File::Glob;
本地$/=undef;
我的$file=@ARGV[0];

打开输入,“为什么不逐行处理,简单易行

use warnings;
use strict;
use feature 'say';

my $file = shift || die "Usage: $0 file\n";

open my $fh, '<', $file  or die "Can't open $file: $!";

my (@jobs, $prev_line);

while (my $line = <$fh>) { 
    chomp $line;
    next if not $line =~ /\S/;

    if ($line =~ /^\s*#[0-9]/) {
        push @jobs, $prev_line;
    }   

    $prev_line = $line;
}

say for @jobs;
问题并不是说是否也需要排名,但regex中有一个提示,表明它们可能需要排名。然后,假设文件中的顺序是“正确”的,您可以迭代数组索引,并使用它们的索引(秩)打印元素(标题)

或者,可以肯定地说,在正则表达式中捕获它们,
/^\s*#([0-9]+)/
。然后您可以直接打印标题及其排名,或者将它们存储在具有键值对的哈希中
rank=>title


对于正则表达式,需要进行一些更正。要在匹配之前合成正则表达式,最好使用运算符。要处理多行字符串,需要使用
/m
修饰符。(请参阅。)正则表达式本身需要修复。例如

my $regex  = qr/^(.+)?(?:\n\s*)+\n\s*#\s*[0-9]/m;
my @titles = $content =~ /$regex/g
捕获一行之后至少有一个空行,然后在另一行上捕获
#N

如果还需要标题的排名,那么也将其捕获,并存储在散列中

my $regex = qr/^(.+)?(?:\n\s*)+\n\s*#\s*([0-9]+)/m;
my %jobs  = reverse  $content =~ /$regex/g;
或者最好不要用
反向推送它,而是迭代匹配列表

my %jobs;
while ($content =~ /$regex/g) {
    $jobs{$2} = $1;
}
因为有了它,我们可以在每次迭代中检查我们的“捕获”,进行其他处理,等等。然后您可以按顺序对要打印的键进行排序

say "#$_ $jobs{$_}" for sort { $a <=> $b } keys %jobs;
对排序{$a$b}键%jobs说“#$$jobs{$}”;
一般来说,只要根据需要按职级选择工作就行了


我认为可以公平地说,这里的正则表达式比第一个程序复杂得多。

您没有考虑空格(如
Data Scientist
):

^\w+.*$\R+#(\d+)
看。

\R
等于
(?>\R\n |\n |\R |\f |\x0b |\x85)
(匹配Unicode换行符序列)。

回答您的问题:

  • 您只捕获了第二个单词,并且不允许在它们之间留有空格。这就是它不匹配的原因,例如,
    Data Scientist

  • >P>使用<代码> QR///<代码>操作符编译动态内容的正则表达式。该错误源于正则表达式中的“代码> $/CODE”,Perl正则表达式编译器假定您错了,因为<代码> $>代码>应该只出现在正则表达式的结尾。

    以下代码应达到您的要求。请注意两步方法:

  • 查找匹配的文本

    • 行首(
      ^
    • 一个或多个由空格分隔的单词(
      \w+(?:\s+\w+*
      ,无需捕获匹配项)
    • 两行结束(
      \n\n
    • #
      后面跟一个数字(
      \d+
    • 多次应用正则表达式(
      /g
      )并将字符串视为多行(
      /m
      ,即
      ^
      将匹配输入文本中某行的任何开头)
  • 在行尾拆分匹配(
    \n
    )并提取第一个和第三个字段

    • 我们知道,
      $match
      将包含三行,这种方法比编写另一个正则表达式容易得多
  • 对您在问题中提供的示例文本进行测试

    $perl dummy.pl dummy.txt
    匹配“数据科学家”“1”
    匹配“程序员”“2”
    匹配“SAP模块顾问”“3”
    

    UNICODE更新:根据@Jan的回答,代码可以改进如下:

    my$regex=qr/^(\w+(?:\s+\w+)*\R\R\d+)/m;
    ...
    我的($title,undf,$rank)=拆分(/\R/,$match);
    

    这可能是更通用的方法,因为
    UTF-8
    File::Slurper::read_text()
    的默认值…

    Q2:使用
    qr/
    操作符(请参阅)编译正则表达式。即
    my$regex=qr/^\w+(\w+)*$\n\n\n#(\d+)/;
    。您也可以用这种方式组合子正则表达式,即
    my$regex=qr/${subre1}${subre2}${subre3}/
    Q1有点不清楚。F.ex。第一个示例:是否要将
    数据科学家
    #1
    匹配?谢谢你的回答。这是一个很好的正则表达式。但它不包含如何将正则表达式的结果集带入Perl数组,因此我不能用勾号选择它。@Romario:不用担心,很乐意帮助。谢谢你的帮助答案。我听说过
    qr//
    ,但已经完全忘记了。循环部分很好。以前从未见过或使用过
    使用功能qw(比如);
    使用文件::Slurper qw(read_text);
    ,但它们非常实用和精细。表达式内部
    (?:\s+\w+)*
    我尝试过在没有它的情况下重新登录,但它没有改变输出。这可能是打字错误吗?它做什么?请参阅:“
    (?:pattern)
    这是用于群集,而不是捕获;它将子表达式分组,如
    ()
    ,但不会像
    ()
    那样进行反向引用,也就是非捕获组(NCG)。当您需要对内容进行分组,但不捕获内容时,这对于稍微提高性能更为可取。我曾单独考虑过
    ,但结果是组合
    ?:
    ,现在这很有意义。谢谢。谢谢您的回答。我想将整个文本作为单个字符串处理,因为这不是一个很长的文本,我想看看我是否可以用一个正则表达式来解决它。@Romario当然,这就是我们学习的方式:)我只是想提供一个更简单的方法(原则上在生产中至关重要,并且有困难的问题:使用合适的计算方法和算法来分解它
    ^\w+.*$\R+#(\d+)
    
    #!/usr/bin/perl
    use strict;
    use warnings;
    
    use feature qw(say);
    use File::Slurper qw(read_text);
    
    my $input = read_text($ARGV[0])
        or die "slurp: $!\n";
    
    my $regex = qr/^(\w+(?:\s+\w+)*\n\n#\d+)/m;
    
    foreach my $match ($input =~ /$regex/g) {
        #say $match;
        my($title, undef, $rank) = split("\n", $match);
        $rank =~ s/^#//;
        say "MATCH '${title}' '${rank}'";
    }
    
    exit 0;