【问题标题】:Handling Nested Delimiters in perl在 perl 中处理嵌套分隔符
【发布时间】:2016-09-26 16:33:07
【问题描述】:
use strict;   
use warnings; 

my %result_hash = (); 
my %final_hash  = (); 
Compare_results(); 

foreach my $key (sort keys %result_hash ){ 
  print "$key \n"; 
  print "$result_hash{$key} \n"; 
} 

sub Compare_results 
{ 

  while ( <DATA> ) 
  { 
   my($instance,$values) = split /\:/, $_; 
   $result_hash{$instance} = $values; 

   } 
} 
__DATA__ 
1:7802315095\d\d,7802315098\d\d;7802025001\d\d,7802025002\d\d,7802025003\d\ d,7802025004\d\d,7802025005\d\d,7802025006\d\d,7802025007\d\d
2:7802315095\d\d,7802025002\d\d,7802025003\d\d,7802025004\d\d,7802025005\d\d,7802025006\d\d,7802025007\d\d

输出

1 
7802315095\d\d,7802315098\d\d;7802025001\d\d,7802025002\d\d,7802025003\d\d,7802025004\d\d,7802025005\d\d,7802025006\d\d,7802025007\d\d 

2 
7802315095\d\d,7802025002\d\d,7802025003\d\d,7802025004\d\d,7802025005\d\d,7802025006\d\d,7802025007\d\d

我试图获取每个键的值并再次尝试从结果哈希中拆分逗号分隔值,如果我在任何值中找到分号,我希望将左右值存储在单独的哈希键中。

如下图

1.#split the value of result_hash{$key}  again by , and see whether any chunk is seperated by ;
2. #every chunk without ; and value on left  with ; should be stored in   
 @{$final_hash{"eto"}} =   ['7802315095\d\d','7802315098\d\d','7802025002\d\d','7802025003\d\d','7802025004\d\d','7802025005\d\d','7802025006\d\d','7802025007\d\d']  ;
3.#Anything found on the right side of ; has to be stored in  
 @{$final_hash{"pro"}} = ['7802025001\d\d'] ;   

有没有一种方法可以处理子程序中的所有事情?我可以让代码更简单吗

更新:

我尝试一次拆分字符串,但它只是用分号选择值并忽略所有内容

foreach my $key (sort keys %result_hash ){
#   print "$key \n";
#   print "$result_hash{$key} \n";
my ($o,$t) = split(/,|;/, $result_hash{$key});
   print "Left : $o \n";
   print "Left : $t \n";
   #push @{$final_hash{"eto"}}, $o;
   #push @{$final_hash{"pro"}} ,$t;
 }

}

帮助后我更新的代码

sub Compare_results
{   
  open my $fh, '<', 'Data_File.txt' or die $!;
  # split by colon and further split by , and ; if any (done in insert_array)
  my %result_hash = map { chomp; split ':', $_ } <$fh> ; 
  foreach  ( sort { $a <=> $b }  (keys %result_hash) )
  { 
     ($_ < 21) 
        ? insert_array($result_hash{$_}, "west")
        : insert_array($result_hash{$_}, "east");
  } 
}


 sub insert_array()
 {
   my ($val,$key) = @_;
   foreach my $field (split ',', $val)
   {   
     $field =~ s/^\s+|\s+$//g;    # /  turn off editor coloring
     if ($field !~ /;/) {
        push @{ $file_data{"pto"}{$key} }, $field ;
     }
     else {
       my ($left, $right) = split ';', $field;
       push  @{$file_data{"pto"}{$key}}, $left if($left ne '') ;
       push @{$file_data{"ero"}{$key}}, $right if($right ne '')  ;
    }
   }  
  }

谢谢

【问题讨论】:

  • 不错的想法,但是如果你在/[,;]/ 上分裂,你如何判断哪些是“正确”的(; 的右边)?它返回所有字段的列表,在,; 之间,无论它们发生的顺序如何。 (您编写的内容将列表分配给两 (2) 个标量,因此您只能在拆分后获得前两个元素。)
  • 我编辑了您的添加 - 将长评论移至其自己的行,因此不必滚动阅读它,并通过正则表达式添加了评论 # / ...,该评论关闭了编辑器错误的红色着色. (它经常这样做——在一个正则表达式之后,一切都变红了。)如果你不喜欢这个,请恢复更改——点击我的用户名上方的“已编辑(时间)”,你会看到修订,每个都有按钮。其中一个说“回滚”,按您要回滚到的修订点击它。
  • @zdim :您能告诉我如何拆分具有两个分隔符的数据,例如:1,2,3,4,5,6,7;8,9,10,11,12我想在之后存储所有内容;在一个数组中,然后在另一个数组中。请帮助我使用一次性拆分功能。 O/P array1 = [1,2,3,4,5,6,7] 和 array2 =[8,9,10,11,12]
  • 目前,我的答案中最后一个示例的两次变体。第一个流行音乐 -- @left = split ",", (/([^;]+)/)[0]@right = split ",", (/;([^;]+)/)[0];。这是如果要处理的字符串在$_ 中——如果它在变量中,则使用($str =~ /.../)[0]。加say for @left查看。 (...)[0] 是必需的,因为匹配返回一个列表,如果你将它传递给split,它在标量上下文中计算为1(长度)。真正的一次性更难,因为您会得到一份清单,但需要将其分成两份。如果我想出更好的,我会发表另一条评论。
  • 但它是否会尝试将相同的数据拆分两次,这值得提出一个新问题吗?

标签: perl delimiter


【解决方案1】:

更新  在末尾添加了两遍正则表达式


只是系统地进行,逐步分析字符串。您需要连续拆分和特定的分离规则这一事实使得一次性完成变得笨拙。最好有一个明确的方法,而不是一个怪物声明。

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

my (%result_hash, %final_hash); 

Compare_results(); 

say "$_ => $result_hash{$_}" for sort keys %result_hash;
say '---';
say "$_ => [ @{$final_hash{$_}} ]" for sort keys %final_hash;

sub Compare_results 
{   
    %result_hash = map { chomp; split ':', $_ } <DATA>;

    my (@eto, @pro);
    foreach my $val (values %result_hash)
    {   
        foreach my $field (split ',', $val)
        {   
            if ($field !~ /;/) { push @eto, $field }
            else { 
                my ($left, $right) = split ';', $field;
                push @eto, $left;
                push @pro, $right;
            }
        }    
    }        
    $final_hash{eto} = \@eto;
    $final_hash{pro} = \@pro;
    return 1;                  # but add checks above
}

这里有一些效率低下,并且没有错误检查,但是方法很简单。如果您的输入不是很小的,请将上面的内容更改为逐行处理,您清楚地知道该怎么做。它打印

1 => ...(你的问题) --- eto => [ 7802315095\d\d 7802315098\d\d 7802025002\d\d 7802025003\d\d ... 亲 => [ 7802025001\d\d ]

请注意,您的数据确实有一个松散的\d\ d


我们不需要为此构建整个哈希%result_hash,而只需要选择: 之后的行部分。我留下了散列,因为它被声明为全局的,所以你可能想要它。如果它实际上不需要它自己,这会简化

sub Compare_results {
    my (@eto, @pro);
    while (<DATA>) {
        my ($val) = /:(.*)/;
        foreach my $field (split ',', $val)
        # ... same
    }
    # assign to %final_hash, return from sub
}

感谢ikegami cmets。


出于好奇,这里使用正则表达式分两遍

sub compare_rx {
    my @data = map { (split ':', $_)[1] } <DATA>;
    $final_hash{eto} = [ map { /([^,;]+)/g  } @data ];
    $final_hash{pro} = [ map { /;([^,;]+)/g } @data ];
    return 1;
}

这会选择所有不是,; 的字符,使用否定字符类[^,;]。所以这取决于他们中的第一个,从左到右。它在全局范围内执行此操作,/g,因此它不断遍历字符串,收集“左侧”,; 的所有字段。然后它有点作弊,选择所有在; 右边的[^,;]map 用于对所有数据行执行此操作。

如果需要 %result_hash 而不是 @data 构建它,然后使用 my @values = values %hash_result 从中提取值并使用 @values 提供地图。

或者,逐行打断(同样,您可以构建%result_hash,而不是直接使用$data

my (@eto, @pro);
while (<DATA>) {
    my ($data) = /:(.*)/;
    push @eto, $data =~ /([^,;]+)/g; 
    push @pro, $data =~ /;([^,;]+)/g;
}

【讨论】:

  • @LearningCpp 我更新了答案——将代码完成为一个完整的程序,用你的数据进行测试,添加了一些 cmets。
  • 谢谢@zdim,我已经使用了你的最后一段代码,但是它在\d(即\\d)后附加了一个额外的\,你能否解释一下map { chomp;(split': ', $_)[1] };我添加了 chomp 来消除空虚
  • @LearningCpp 这很奇怪——它没有在我的测试中添加它(我发布的输出是从我的终端复制的),因为它不应该。正则表达式的输出相同。该行如下。它chomps 换行。然后它被: 拆分,产生两个字段。 map 使得它对每一行都执行此操作,并发送整个列表。所以这些是对的,您可以将其直接分配给哈希。这是合法的:my %h = (1, 'one', 2, 'two'); 将它们配对,key =&gt; value, ... 分配给哈希的非常好的方法。
  • @LearningCpp 这里已经很晚了,我现在就走了(对于那些 zzzz)。告诉我,我会在几个小时后回来。如果这些 cmets 不够好,我可以添加到帖子中。
  • 这是我的输出,我使用您发布的地图代码 ['7802261004\\d\\d', '7802261046\\d\\d', '7802261064\\d\\d' , '7802261186\\d\\d', '7802293001\\d\\d' ];调查出了什么问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-12
  • 2014-02-26
  • 2019-04-22
  • 2010-10-07
相关资源
最近更新 更多