【问题标题】:Match multiple line string in Perl在 Perl 中匹配多行字符串
【发布时间】:2015-04-13 15:24:25
【问题描述】:

我是 Perl 的新手,我想知道是否有人可以帮助我。

我有这样的输入:

a,b,
c,d,e,f,g,h,
i,j,q // Letras

我正在尝试分别获取// 之前的字母,然后在{} 之间打印它们,并用: 分隔。

我尝试使用这个 RE ([\w,;:\s\t]*)(\n|\/\/)/m,我可以在 $1 中输入每行的所有字母(作为包含分隔符的字符串),但不是我想要的。

我需要在同一个文件中多次匹配该模式,所以我使用了/g

编辑:

这是我的代码块:

while ( <> ) {
  if ( /([\w,;:\s\t]*)(\n|\/\/)/m ) {
    print "$1\n";
  }
} 

【问题讨论】:

  • 我们能看看你到目前为止尝试了什么吗?
  • while(){ if (/([\w,;:\s\t]*)(\n|\/\/)/m){ print "$1\n" ; } } 有了这个,我可以在 //. 之前打印内容

标签: regex perl


【解决方案1】:

/m 用于使用^$ 在多行字符串中逐行匹配。

另一方面,您正在逐行阅读输入。如果您一次只查看一行,则不能期望用一个表达式跨行匹配。

相反,通过将$/ 设置为适当的值来按块读取。如果块总是以确切的字符串"// Letras\n\n" 结尾,那么任务就更简单了。

#!/usr/bin/env perl

use strict;
use warnings;

local $/ = '//';

while (my $chunk = <DATA>) {
    chomp $chunk;
    my @fields = ($chunk =~ /([a-z])[, ]/g);
    next unless @fields;
    printf "{%s}\n", join(':', @fields);
}

__DATA__
a,b,
c,d,e,f,g,h,
i,j,q // Letras

a,b,
c,d,e,f,g,h,
i,j,q // Metras

输出:

{a:b:c:d:e:f:g:h:i:j:q}
{a:b:c:d:e:f:g:h:i:j:q}

你也可以使用File::Stream:

#!/usr/bin/env perl

use strict;
use warnings;

use File::Stream;

my $stream = File::Stream->new(
    \*DATA,
    separator => qr{ (?: \s+ // [^\n]+ ) \n\n }x
);

while (my $chunk = <$stream>) {
    $chunk =~ s{ \s+ // .* \z }{}sx;
    $chunk =~ s{ ,\n? }{:}gx;
    print "{$chunk}\n";
}

__DATA__
a,b,
c,d,e,f,g,h,
i,j,q // Letras

a,b,
c,d,e,f,g,h,
i,j,q // Metras

【讨论】:

  • 这是很多的猜测!
  • 这就是我需要的!在读到“//”之前,我不知道如何读取文件。这段代码是如何工作的:我的 $chunk = ?如果我有一个输入 .txt 文件,我的 DATA 应该是什么?最后,这是如何工作的:next unless @fields; ?再次感谢!
  • 使用通常的open my $fh, '&lt;', $filename 打开文件...然后使用$fh 而不是DATA
【解决方案2】:

认为您的目标是从每行中删除 cmets(用双斜杠表示),并用大括号括起来打印出来,并用冒号 : 分隔符代替逗号

首先,您应该使用chomp从每行中删除尾随换行符

那么您只需删除任何尾随注释即可s|\s*//.*||。这也会删除// 之前的所有空格。我使用管道字符| 作为分隔符,以避免在正则表达式模式中转义斜杠。并且数据一次处理一行,因此不需要 global /g 修饰符

此程序从命令行中指定的文件中读取,我已设置该文件以包含您在问题中显示的数据

use strict;
use warnings;

while ( <DATA> ) {
  chomp;
  s|\s*//.*||;
  print "{$_}\n";
}

输出

{a,b,}
{c,d,e,f,g,h,}
{i,j,q}

更新

感谢Sinan Ünür's solution 我注意到您已要求“打印{} 之间的[字母],以: 分隔”

这是对上面while循环的修改,它查找当前行中不包含逗号的所有子字符串,并使用冒号将它们再次连接在一起:

while ( <> ) {
  chomp;
  s|\s*//.*||;
  my $values = join ':', /[^,]+/g;
  print "{$values}\n";
}

输出

{a:b}
{c:d:e:f:g:h}
{i:j:q}

我确信真正的解决方案要简单得多,但除非您详细说明您的问题,否则我们必须满足所有可能性

【讨论】:

    【解决方案3】:

    您是要将所有 3 行上的字母组合到输出中,还是转换每一行?

    换句话说,是你想要的输出

    {a:b}
    {c:d:e:f:g:h}
    {i:j:q}
    

    {a:b:c:d:e:f:g:h:i:j:q}
    

    ?

    如果你想要前者,鲍罗丁的答案是有效的。 如果你想要后者,那么你应该将内容加载到一个数组中,并使用 join 语句打印它。为此,我修改了鲍罗丁的回答:

    while ( <> ) {    # read each line
      chomp;          # remove \n from line
      s|\s*//.*||;    # remove comment
      push @values, ':', /[^,]+/g;   # store letters in array
    }
    my $values = join ':', @values;  # convert array to string
    print "{$values}\n";             # print the results
    

    【讨论】:

      【解决方案4】:
      my $str = "a,b,
      c,d,e,f,g,h,
      i,j,q // Letras";
      
      $str = join "",map {s/,/:/g ;(split)[0]}  split '\n', $str;
      
      print "{$str}";
      
      
      Sample output
      
      {a:b:c:d:e:f:g:h:i:j:q}
      

      我正在考虑用换行符分隔多行的字符串。

      join "",map {s/,/:/g ;(split)[0]}  split '\n', $str 
      

      这是从右到左评估的。

      1. $str 上使用\n 拆分会生成3 个元素,这些元素是map 的输入。

      2. (split)[0]split 的默认分隔符是空格。所以每个元素都被分割为空白,0th 元素只被认为是丢弃其他元素。 例如(split)[0] for i,j,q // Letras 产生 3 个元素 "i,j,q" "//" "Letras",其中仅考虑元素 0,即 "i,j,q"。

      3. , 替换为:

      4. join 用于组合来自map 的所有结果元素。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-12-30
        • 2014-11-02
        • 2020-10-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-23
        • 1970-01-01
        相关资源
        最近更新 更多