查找并替换字符串中的模式

时间:2009-05-20 20:12:56

标签: regex perl

我的字符串

(champs1 (champs6 donnee_o donnee_f) [(champs2 [] (champs3 _YOJNJeyyyyyyB (champs4 donnee_x)) (debut 144825 25345) (fin 244102 40647)), (champs2 [] (champs3 _FuGNJeyyyyyyB (champs4 donnee_z)) (debut 796443 190570) (fin 145247 42663))] [] []).

(注释为可读性):

(champs1 
     (champs6 donnee_o donnee_f) 
     [(champs2 [] 
          (champs3 _YOJNJeyyyyyyB (champs4 donnee_x)) 
          (debut 144825 25345)
          (fin 244102 40647)
       ), 
      (champs2 [] 
          (champs3 _FuGNJeyyyyyyB (champs4 donnee_z)) 
          (debut 796443 190570) 
          (fin 145247 42663)
     )] 
     [] 
     []
).

在上面的字符串中,我想分别用这些值替换整数值:

$moyLargRectNom, $moyHautRectNom, $moyLargRectNom, 
$moyHautRectNom, $moyLargRectMat, $moyHautRectMat, 
$moyLargRectMat, $moyHautRectMat

我要在字符串中替换8个值。

这是我的REGEX

$ligne =~ s{
    (.*debut) \s\d+ \s\d+
    (.*fin)   \s\d+ \s\d+
    (.*debut) \s\d+ \s\d+
    (.*fin)   \s\d+ \s\d+
    (.*)
}{
    $1 . $moyLargRectNom . 
    $2 . $moyHautRectNom . 
    $3 . $moyLargRectNom . 
    $4 . $moyHautRectNom . 
    $5 . $moyLargRectMat . 
    $6 . $moyHautRectMat . 
    $7 . $moyLargRectMat . 
    $8 . $moyHautRectMat . 
    $9
}xe;

它根本不替换值;有人可以帮我吗?谢谢。

4 个答案:

答案 0 :(得分:1)

尝试这个尺寸:

my @numbers = ($moyLargRectNom, $moyHautRectNom, $moyLargRectNom, $moyHautRectNom, $moyLargRectMat, $moyHautRectMat, $moyLargRectMat, $moyHautRectMat);
my @temp = split / /, $ligne;
for(@temp) {
  if(/^\W*\d\W*$/) {
    my $num = shift @numbers;
    s/\d+/$num/;
  }
}
$ligne = join " ", @temp;

根据@temp中的“字词”(大约),制作一个列表$ligne。它会生成另一个列表@numbers,它是您要在列表中替换的数字列表,按您希望它们替换的顺序排列。然后它一个接一个地经过@temp,如果一个给定的元素是一个数字(即匹配正则表达式/^\W*\d\W*$/,这意味着它没有单词字符(所以它不是“champs4”)并且至少有一个数字 - 除了“25346”之外,这将匹配“25346”),然后用@numbers中的第一个值替换数字部分。现在我已经测试过,我可以向你保证这确实有效!

我相信使用map可以实现更短的实施,但这对你来说效果会很好。

这种方法对您的方法的优势:

首先,这个解决方案是可扩展的。要用您的解决方案替换八个以上的数字,您需要编写一个新的正则表达式。要使用我的解决方案替换八个以上的数字,只需向@numbers添加一些条目即可。此代码可以放入一个子程序,该子程序需要更改字符串以及要更改的数字列表,您不必担心它们是否传递了正确数量的数字,或者它们是否具有正确的格式。

其次,粗略一瞥就更容易理解了。只要您使用的正则表达式很难在视觉上解析。即使它有效,总有一天有人可能需要改变你的代码来做一些不同的事情。如果你使用一个巨大的正则表达式,重写器(也许你)将只是摇头,突出你的代码,然后按删除,然后编写新代码来执行它。有了这个,他们可以很容易地看到你的代码中发生了什么,如果他们需要对它进行修改,他们就可以。

第三,如果您想要在指定数量的替换品中进行硬编码,您也可以这样做:

my @numbers = ($moyLargRectNom, $moyHautRectNom, $moyLargRectNom, $moyHautRectNom, $moyLargRectMat, $moyHautRectMat, $moyLargRectMat, $moyHautRectMat);
my @temp = split / /, $ligne;
my $max_replacements = 8;
for(@temp) {
  if(/^\W*\d\W*$/) {
    my $num = shift @numbers;
    s/\d+/$num/;
    last unless --$max_replacements;
  }
}
$ligne = join " ", @temp;

作为旁注(之前已应用,但仍然适用),这将失败浮点数 - /^\W*\d\W*$/将匹配浮点数,但s/\d+/$num/不会替换浮点数,只有整数部分。如果您发现需要浮点数,请更改以下行:

s/\d+/$num/;

对此:

s/\d+|(?:\d+)?\.\d+/$num/;

那应该匹配浮点数。

答案 1 :(得分:1)

sprintf救援:

#!/usr/bin/perl

use strict;
use warnings;

my $s = <<EO_TXT;
(champs1 (champs6 donnee_o donnee_f) [(champs2 [] 
(champs3 _YOJNJeyyyyyyB (champs4 donnee_x)) (debut 144825 25345) 
(fin 244102 40647)), (champs2 [] (champs3 _FuGNJeyyyyyyB 
(champs4 donnee_z)) (debut 796443 190570) (fin 145247 42663))] [] []).
EO_TXT

my ( 
    $moyLargRectNom, $moyHautRectNom, 
    $moyLargRectMat, $moyHautRectMat, 
) = map { "val$_" } qw( 1 2 3 4 );

my @replacements = (
    $moyLargRectNom, $moyHautRectNom,
    $moyLargRectNom, $moyHautRectNom,
    $moyLargRectMat, $moyHautRectMat,
    $moyLargRectMat, $moyHautRectMat,
);

$s =~ s/\b[0-9]+\b/%s/g; # replace %s with the appropriate specifier
$s = sprintf $s, @replacements;

print $s, "\n";

答案 2 :(得分:0)

你似乎以与我相反的方式这样做。即我会查找这些数字并替换它们,而不是替换它们,即匹配数字周围的东西并将它们代入字符串。

总是会有8个值吗?他们会一直跟着同样的话吗?如果是的话:

.+?debut\s([\d]+)\s([\d]+).+?fin\s([\d]+)\s([\d]+).+?debut\s([\d]+)\s([\d]+).+?fin\s([\d]+)\s([\d]+)

或者可以首次亮相&amp; fin出现在任何地方,无论什么时候你都想要替换它们:

首次亮相x y - &gt;首次亮相$ moyLargRectNom,$ moyHautRectNom, fin x y - &gt; fin $ moyLargRectNom,$ moyHautRectNom, (首次亮相144825 25345)(fin 244102 40647)

如果这是真的,只需使用两个简单的正则表达式:

debut\s([\d]+)\s([\d]+)
fin\s([\d]+)\s([\d]+)

并用单词..

替换组

但我不记得哪个变量存储了创建的组数,抱歉。

答案 3 :(得分:0)

我认为你的结构太不规则或奇怪,不适合正则表达式,嵌套表达式很少。

所以我去寻找一张解析树。找不到适合的,不理解任何正式的解析语法,我写了自己的令牌/状态机。

它将您的代码转换为数据树,然后您可以使用简单的循环结构提取它。

谨防,代码仅设计用于您目前提供的小型数据集,不平衡的括号会给解析器带来麻烦并产生无用的树。

浏览到底部以了解如何使用此blob

#!/usr/bin/perl 

use strict;
use warnings;
use version;
use Data::Dumper;
our $VERSION = qv('0.1');

my @stack;

my $data = <<'EOF';
(champs1 
     (champs6 donnee_o donnee_f) 
     [(champs2 [] 
          (champs3 _YOJNJeyyyyyyB (champs4 donnee_x)) 
          (debut 144825 25345)
          (fin 244102 40647)
       ), 
      (champs2 [] 
          (champs3 _FuGNJeyyyyyyB (champs4 donnee_z)) 
          (debut 796443 190570) 
          (fin 145247 42663)
     )] 
     [] 
     []
)
EOF

push @stack,
  {
    tokens  => [],
    context => 'void',
  };

my $state;

my $eaten;
my $str = $data;

sub eat
{
    my $n = shift;
    substr( $str, 0, $n, '' );
}

while ( @stack && $str )
{
    $state = $stack[-1];
    my @tokens  = @{ $stack[-1]->{tokens} };
    my $context = $stack[-1]->{context};

    if ( $str =~ m{(^[\s,]+)} )
    {
        eat length($1);
        next;
    }
    if ( $str =~ m{(^\w+)} )
    {
        eat length($1);
        push @{ $stack[-1]->{tokens} }, $1;
        next;
    }
    if (    $str =~ m{^\[}
        and $context eq 'nest'
        || $context  eq 'nestgroup'
        || $context  eq 'array' )
    {
        eat 1;
        print "\e[33m[\e[0m";
        push @stack,
          {
            tokens  => [],
            context => 'array',
          };

        next;
    }

    if ( $str =~ m{^\]} and $context eq 'array' )
    {
        eat 1;
        print "\e[33m]\e[0m";
        pop @stack;
        push @{ $stack[-1]->{tokens} }, \@tokens;
        next;
    }

    if (
        $str =~ m{^\((champs(\d)|debut|fin)\s}
        and (  $context eq 'nest'
            || $context eq 'array'
            || $context eq 'nestgroup'
            || $context eq 'void' )
      )
    {
        eat length($1) + 1;
        $stack[-1]->{nodename} = $1;
        print "\e[32m($1\e[0m";
        push @stack,
          {
            tokens  => [],
            context => 'nestgroup',
          };
        next;
    }
    if ( $str =~ m{^\)} and $context eq 'nestgroup' )
    {
        eat 1;
        print "\e[32m)\e[0m";
        pop @stack;
        my $nodename = $stack[-1]->{nodename};
        push @{ $stack[-1]->{tokens} }, { $nodename, \@tokens };
        next;
    }
    if ( $str =~ m{^\(} )
    {
        eat 1;
        print "\e[31m(\e[0m";
        push @stack,
          {
            tokens  => [],
            context => 'nest',
          };
        next;
    }
    if ( $str =~ m{^\)} and $context eq 'nest' )
    {
        eat 1;
        print "\e[31m)\e[0m";
        pop @stack;
        push @{ $stack[-1]->{tokens} }, \@tokens;
        next;
    }

    print substr( $str, 0, 1 ), "\e[34m$context\e[0m";
    eat 1;
}

$Data::Dumper::Indent = 1;
$Data::Dumper::Terse  = 1;

print "Tree:\n";
print Dumper( $state->{tokens}->[0]->{champs1}->[1] );

print "--------";
for ( @{ $state->{tokens}->[0]->{champs1}->[1] } )
{
    my @data = @{ $_->{champs2} };
    print ">", Dumper( $data[2], $data[3] );
}

输出:

(champs1(champs6)[(champs2[](champs3(champs4))(debut)(fin))(champs2[](champs3(champs4))(debut)(fin))][][])
Tree:
[
  {
    'champs2' => [
      [],
      {
        'champs3' => [
          '_YOJNJeyyyyyyB',
          {
            'champs4' => [
              'donnee_x'
            ]
          }
        ]
      },
      {
        'debut' => [
          '144825',
          '25345'
        ]
      },
      {
        'fin' => [
          '244102',
          '40647'
        ]
      }
    ]
  },
  {
    'champs2' => [
      [],
      {
        'champs3' => [
          '_FuGNJeyyyyyyB',
          {
            'champs4' => [
              'donnee_z'
            ]
          }
        ]
      },
      {
        'debut' => [
          '796443',
          '190570'
        ]
      },
      {
        'fin' => [
          '145247',
          '42663'
        ]
      }
    ]
  }
]
--------
>{
  'debut' => [
    '144825',
    '25345'
  ]
}
{
  'fin' => [
    '244102',
    '40647'
  ]
}
>{
  'debut' => [
    '796443',
    '190570'
  ]
}
{
  'fin' => [
    '145247',
    '42663'
  ]
}