【问题标题】:Join, split and map using perl for creating new attribs使用 perl 加入、拆分和映射以创建新属性
【发布时间】:2016-12-02 07:23:03
【问题描述】:
my $str = "<SampleElement oldattribs=\"sa1 sa2 sa3\">";

$str =~ s#<SampleElement[^>]*oldattribs="([^"]*)"#
          my $fulcnt=$&;
          my $afids=$1;
          my @affs = ();
          if($afids =~ m/\s+/) {
              @affs = split /\s/, $afids; 
              my $jnafs = join ",", map { $_=~s/[a-z]*//i, } @affs;
              ($fulcnt." newattribs=\"$jnafs\"");
          }
          else {
              ($fulcnt);
          }
         #eg;

我的输出:

<SampleElement oldattribs="sa1 sa2 sa3" newattribs="1,1,1">

预期输出:

<SampleElement oldattribs="sa1 sa2 sa3" newattribs="1,2,3">

有人可以指出我哪里做错了。提前致谢。

【问题讨论】:

标签: regex perl dictionary


【解决方案1】:

您出错的地方比您想象的要早 - 您正在使用正则表达式解析 XML。 XML 是上下文相关的,而正则表达式不是,so it's NEVER going to be better than a dirty hack.

#!/usr/bin/env perl
use strict;
use warnings;

use XML::Twig;
my $twig = XML::Twig -> parse ( \*DATA );

my $sample_elt = $twig -> get_xpath('//SampleElement',0); 
my @old_att = split ( ' ', $sample_elt -> att('oldattribs') );
$sample_elt -> set_att('newattribs', join " ", map { /(\d+)/ } @old_att);

$twig -> set_pretty_print ( 'indented_a' );
$twig -> print;


__DATA__
<XML>
    <SampleElement oldattribs="sa1 sa2 sa3">
    </SampleElement>
</XML>

但是要回答您问题的核心 - 您在这里误用了 map 作为迭代器。

map { $_=~s/[a-z]*//i, } @affs;

因为 那个 正在做的是迭代 @affs 中的所有元素并修改它们......但 map 只是返回表达式的结果 - 这是 1 因为它工作。

如果您想要更改@affs,您会:

s/[a-z]*//i for @affs; 

但如果您不想想要,那么简单的答案是使用r 正则表达式标志:

map { s/[a-z]*//ir } @affs;

或者就像我在示例中所做的那样:

map { /(\d+)/ } @affs; 

哪个正则表达式匹配并捕获字符串的数字部分,但结果是“捕获”的文本是返回的内容。

【讨论】:

    【解决方案2】:

    这是从输入 $str 构建显示输出的简单方法。

    注意:输入是单引号,而不是双引号。那么\" 在正则表达式中不是问题。

    my $str = '<SampleElement oldattribs=\"sa1 sa2 sa3\">';
    
    # Pull 'sa1 sa2 sa3' string out of it
    my ($attrs) = $str =~ /=\\"([^\\]+)/;    # " # (turn off bad syntax highlight)
    
    # Build '1,2,3' string from it
    my $indices = join ',', map { /(\d+)/ } split ' ', $attrs;
    
    # Extract content between < > so to add to it, put it back together
    my ($content) = $str =~ /<(.*)>/;    
    my $outout = '<' . $content . " newattribs=\"$indices\"" . '>';
    

    这给出了所需的输出。

    如果您愿意,其中一些可以组合成单个语句。例如

    my $indices = 
        join ',', map { /(\d+)/ } split ' ', ($str =~ /"([^\\]+)/)[0];   # "
    
    $str =~ s/<(.*)>/<$1 newattribs=\"$indices\">/;
    

    所有这些可以整合到一个正则表达式中,但它变得笨拙且难以维护。


    最重要的是 - 这似乎是XML 或类似的......请不要手动操作,除非实际上只有一两个 sn-p。有优秀的解析器。

    【讨论】:

      【解决方案3】:

      通过搜索地图功能找到了解决方案:

      my $str = "<SampleElement oldattribs=\"sa1 sa2 sa3\">";
      
      $str=~s#<SampleElement[^>]*oldattribs="([^"]*)"#my $fulcnt=$&; my $afids=$1;
      my @affs = ();
      if($afids=~m/\s+/)
      {
          @affs = split /\s/, $afids; 
          my @newas = join ",", map { (my $foo = $_) =~ s/[a-z]*//i; $foo; } @affs ;
          ($fulcnt." newattribs=\"@newas\"");
      }
      else
      {
          ($fulcnt);
      }
      #eg;
      

      我已在我的代码中更新了以下行:

      my @newas = join ",", map { (my $foo = $_) =~ s/[a-z]*//i; $foo; } @affs ;
      

      代替

      my $jnafs = join ",", map { $_=~s/[a-z]*//i, } @affs;
      

      感谢大家的工作。

      【讨论】:

      • 你不需要变量$foo,只需使用map { s/[a-z]*//i ; $_ }
      • 从 perl 5.14 你甚至可以使用map { s/[a-z]*//ir }
      • 这很好,但为什么要去掉字母——你不能匹配数字吗,map { /(\d+)/ }(见我的回答)?此外,这肯定会遗漏一些东西——正则表达式行在语法上是错误的(它不起作用)。你能修复它,发布工作代码吗?
      • IMO 在map 中使用s 正则表达式真的很糟糕(没有r 标志),因为它正在修改源数组,而map 的重点是返回一个新的。这种修改应该通过forIMO 完成 - 它同样高效,并且更清楚你在做什么。
      猜你喜欢
      • 2011-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-05
      • 2017-08-02
      相关资源
      最近更新 更多