Perl多行正则表达式

5

我有一个包含JSON对象的文件需要解析,类似于以下内容:

{
"_id" : ObjectId("523a58c1e4b09611f4c58a66"),
"_items" : [
    {
        "adGroupId" : NumberLong(1230610621),
        "keywordId" : NumberLong("5458816773")
    },
    {
        "adGroupId" : NumberLong(1230613681),
        "keywordId" : NumberLong("3204196588")
    },
    {
        "adGroupId" : NumberLong(1230613681),
        "keywordId" : NumberLong("4340421772")
    },
    {
        "adGroupId" : NumberLong(1230615571),
        "keywordId" : NumberLong("10525630645")
    },
    {
        "adGroupId" : NumberLong(1230617641),
        "keywordId" : NumberLong("4178290208")
    }
]}
我希望你能够取出NumberLong()中间的数字。一开始我只需要得到keywordId,我通过以下代码实现了它:
cat listado.txt |& perl -ne 'print "$1," if /\"keywordId\" : NumberLong\(\"?(\d*)\"?\)/' keywordIds.txt

这生成了一个以逗号分隔的数字文件。现在我也需要广告组id,所以我正在尝试以下匹配正则表达式,但没有成功:

cat ./work/listado.txt |& perl -ne 'print "$1-$2," if /\"adGroupId\" : NumberLong\(\"?(\d*)\"?\),\s*\"keywordId\" : NumberLong\(\"?(\d*)\"?\)/m'
正则表达式匹配了,但我认为perl没有进行多行匹配,即使我使用了/m。你有什么想法吗?

1
你为什么要用正则表达式而不是像JSON这样的适当工具来完成它呢? - w.k
好的,这个文件有100MB大小,这只是一个更大脚本中的一步,其中涉及到Python、sed、sort和bash。我想用这种方式解决它,但如果太复杂了,我会将其添加到我相信的Python代码中... - Nicolas Rodríguez Seara
1
问题在于 perl -n 一次只读取一行。如果您只匹配一行,则使用多行正则表达式也无济于事。 - Barmar
那么,我如何进行多行匹配? - Nicolas Rodríguez Seara
要匹配包含多行的字符串,您必须首先拥有一个包含多行的字符串。我在我的答案中提到了如何做到这一点。 - ikegami
5个回答

6
/m影响^$的匹配。你没有使用它们,所以/m没有任何影响。 你每次只读取一行,所以只会在单独的一行上进行匹配。/m不可能导致正则表达式匹配尚未从某个文件句柄中读取的数据,因为它对这些文件句柄一无所知。 你可以通过使用-0777将整个文件加载到内存中,并循环处理所有匹配项,而不仅仅是获取第一个匹配项。

返回第一组的结果是ok,输出:"1230610621-5458816773"。我该如何让它继续执行下去?哦,文件大小为100MB,如果可以避免将其全部上传到内存,那就更好了。 - Nicolas Rodríguez Seara
在 /.../g 中,打印 "$1-$2,"。或者不带额外的逗号,将 "$1-$2" 推入 @matches 数组中,在 END 块中打印 join ',' @matches。 - ikegami

1

只需要使用 grepsed 就可以轻松实现:

grep adGroupId listado.txt | sed -E  "s/[^0-9]+//g"
  1. 匹配包含adGroupId的行
  2. 删除非数字内容

他声称想要这些数字,这有什么不同吗?(除了缺少逗号) - Hunter McMillen
你只捕获了广告组ID号码,我需要广告组ID和关键词ID,像这样的文件:group1-keyword1,group2-keywd2,... - Nicolas Rodríguez Seara
1
1-2、3-4、5-6和1\n3\n5之间有很大的区别。 - ikegami
1
@Nicolas 让我惊讶的是,竟然还没有人发布sed的变体。sed -nr 's/.*adGroupId.*\(([0-9]+)\).*/\1/; Te; N; s/\n.*keywordId.*\("([0-9]+)"\).*$/-\1/; H; :e ${g;s/^\n//;s/\n/,/g;p};' <file - tijagi

1
根据您的数据结构,您可以利用行号进行操作:
while (<>) {
  if ( /NumberLong\("?(?<nr>\d+)/ ) {
    $.%2 ? print "$+{nr}-" : print "$+{nr}\n";
  }
}

或者使用标志:

my $flag = 0;

while (<>) {
  if ( /NumberLong\("?(?<nr>\d+)/ ) {
    !$flag 
      ? (print "$+{nr}-" and $flag++)
      : (print "$+{nr}\n" and $flag--);
  }
}

或者使用 slurping:

use 5.010;
my $file;

{
  local $/;
  $file = <>;
}

while ($file =~ /adGroupId" : NumberLong\("?(?<first>\d+).+?keywordId" : NumberLong\("?(?<second>\d+)/gs ) {
  say "$+{first}-$+{second}";
}

0
perl -ne "print $1.'-' if /adGroupId.+?(\d+)/;print $1.',' if /keywordId.+?(\d+)/" listado.txt

0

请查看File::MultilineGrep

摘自其描述: 被视为具有重复结构的文本文件。这些结构具有重复的起始分隔符、可选的停止分隔符和可变内容。也就是说,这些结构的某些或所有字段都是可选的。任务是选择包含指定模式的所有完整结构。这可以使用多行正则表达式来完成。但存在性能问题:使用正则表达式的处理时间与结构数量不成比例,因此增加结构数量可能导致正则表达式永远无法完成。所提出的函数的处理时间与结构数量成正比。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,