【问题标题】:Matching a perl regular expression over multiple lines在多行上匹配 perl 正则表达式
【发布时间】:2014-04-10 11:22:13
【问题描述】:

好的,所以我有另一个问题需要帮助,我需要使用 perl 在 txt 文件中收集地址,我有一个地方可以从文件中的每张票中获取地址。我的问题是地址延伸的多行。我可以让它抓住第一行,但无论我做什么,它都不会抓住下一行。

示例文本文件

NAME     Sprinkle, Jonathan U  ADDRESS     16887 36 St NW    
                                           Calgary, AB T8O 0B0

带有公寓的示例文本文件

NAME     Nguyen, Michael S     ADDRESS     100A  
                                           8447 149 Ave NW    
                                           Sherwood Park, AB T6J    0Z0

我需要能够处理带有公寓号码的地址和没有公寓号码的房子

到目前为止我的代码(这个只能抓取第一行):

if (/ADDRESS/){
    my @arr = /ADDRESS\s*\S*\s\S*\s\S*\s\S*\s*\n\s*\S*/g or next;
    print "$_\n" for @arr;
}

这给出的输出是: ADDRESS 16887 36 St NW 然后它在这里打印一个换行符,没有其余信息

【问题讨论】:

  • 考虑激活/s/m 标签
  • 我尝试添加 /m 但是当我这样做时,它打印出来的唯一内容是“1”@sshashank124
  • @Mitchk 请尝试使用格式化工具使您的帖子更具吸引力和整洁
  • @HamZa 在我注意到它看起来有多丑之后,我一直试图弄清楚如何使用它们。

标签: regex perl


【解决方案1】:

OP 注意:如果您提供的不仅仅是一条数据记录,这将有助于解决此类问题。

但是,当我们结合这两个数据示例时,很明显 NAME 和 ADDRESS 字段是垂直对齐的。这提供了一种相当简单的解析方法,因为我们基本上只需要匹配一个精确的正则表达式:

NAME     Sprinkle, Jonathan U  ADDRESS     16887 36 St NW    
                                           Calgary, AB T8O 0B0
NAME     Nguyen, Michael S     ADDRESS     100A  
                                           8447 149 Ave NW    
                                           Sherwood Park, AB T6J    0Z0

使用它作为基线,以下脚本可用于解析四个记录:

use warnings;
use strict;

my @records;

while (<DATA>) {
    if (/^NAME     (.{22})ADDRESS     (.*)/) {
        push @records, {
            name => $1,
            address => $2,
        };

    } elsif (/^\s{43}(.*)/) {
        $records[-1]{address} .= "\n$1";

    } else {
        warn "Unknown format on $.: $_";
    }
}

# Strip extra spacing from all fields
for (@records) {
    for (values %$_) {
        s/\s+$//mg;
    }
}

# Output records for debugging
use Data::Dump;
dd \@records;

__DATA__
NAME     Sprinkle, Jonathan U  ADDRESS     16887 36 St NW    
                                           Calgary, AB T8O 0B0
NAME     Nguyen, Michael S     ADDRESS     100A  
                                           8447 149 Ave NW    
                                           Sherwood Park, AB T6J    0Z0
NAME     Sprinkle, Jonathan U  ADDRESS     16887 36 St NW    
                                           Calgary, AB T8O 0B0
NAME     Nguyen, Michael S     ADDRESS     100A  
                                           8447 149 Ave NW    
                                           Sherwood Park, AB T6J    0Z0

输出:

[
  {
    address => "16887 36 St NW\nCalgary, AB T8O 0B0",
    name => "Sprinkle, Jonathan U",
  },
  {
    address => "100A\n8447 149 Ave NW\nSherwood Park, AB T6J    0Z0",
    name => "Nguyen, Michael S",
  },
  {
    address => "16887 36 St NW\nCalgary, AB T8O 0B0",
    name => "Sprinkle, Jonathan U",
  },
  {
    address => "100A\n8447 149 Ave NW\nSherwood Park, AB T6J    0Z0",
    name => "Nguyen, Michael S",
  },
]

【讨论】:

    【解决方案2】:

    由于my @arr = /ADDRESS\s*\S*\s\S*\s\S*\s\S*\s*\n\s*\S*/g or next; 每次迭代您将数组设置为等于您的最后一个模式匹配,您只会得到一行。您需要使用push 附加到该行,如下所示:

    数据

    NAME     Sprinkle, Jonathan U  ADDRESS     16887 36 St NW    Calgary, AB T8O 0B0
    NAME     Nguyen, Michael S     ADDRESS     100A  8447 149 Ave NW    Sherwood Park, AB T6J    0Z0
    

    前:

    use strict;
    use warnings;
    
    my @addresses;
    while ( $test =~ /ADDRESS\s*([A-Za-z0-9,[:blank:]]+)/gxm ) {
        push @addresses, $1 ;
    }
    

    【讨论】:

      【解决方案3】:

      首先,您的两个示例都没有显示多行。因此,根据您的示例,我马上看不出如何为您提供帮助。

      虽然这主要是默认输入记录分隔符的问题。这意味着当您处理文件时,Perl 的默认行为是一次给您一行。除非你对此有所作为,否则你将永远得不到你想要的。

      控制它的变量是$/,因此假设FILE 是您打开的文件句柄,您需要执行以下操作:

      local $/;
      my $contents = <FILE>;
      

      现在$contents 将文件的全部内容作为单个字符串包含所有"\n"s 嵌入。这样你就可以真正地尝试你的比赛了。

      【讨论】:

      • 很抱歉,他们起初确实在编辑时显示了多行,但它被更改了。我修复了它以显示文件如何显示信息
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-02
      • 2012-04-13
      • 1970-01-01
      相关资源
      最近更新 更多