Найти и заменить шаблоны в строке - PullRequest
0 голосов
/ 21 мая 2009

Моя строка

(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 значений для замены в строке.

Это мой РЕГЭКС

$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 ]

1 голос
/ 21 мая 2009

спринт на помощь:

#!/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";
1 голос
/ 21 мая 2009

Попробуйте это для размера:

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/;

Это должно соответствовать числам с плавающей запятой.

0 голосов
/ 21 мая 2009

Я понял, что ваша структура была слишком нерегулярной или странной, чтобы подходить для регулярного выражения, вложенные выражения редко бывают.

Итак, я отправился на охоту за парсером. Не найдя подходящего и не разбираясь ни в одной из формальных грамматик разбора, я написал свой собственный токенистер / конечный автомат.

Превращает ваш код в дерево данных, которое затем можно извлечь с помощью простых циклических конструкций.

Осторожно , код предназначен только для работы с вашим небольшим набором данных при условии, что пока несбалансированные скобки вызовут головные боли синтаксического анализатора и создадут бесполезное дерево.

Переместитесь вниз, чтобы увидеть, как использовать этот 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'
  ]
}
0 голосов
/ 21 мая 2009

Вы, кажется, делаете это противоположным образом, чем я. то есть я бы искал числа и заменял их, а не то, что вам нужно, т. е. сопоставлял материал, окружающий числа, и подставлял их в строку.

ВСЕГДА будет 8 значений? Будут ли они всегда следовать одним и тем же словам? если так:

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

или могут дебютировать и плавать в любом месте, и всякий раз, когда они захотят, вы можете заменить их следующим образом:

debut x y -> debut $ moyLargRectNom, $ moyHautRectNom, fin x y -> fin $ moyLargRectNom, $ moyHautRectNom, (дебют 144825 25345) (плавник 244102 40647)

если это правда, просто сделайте это с помощью двух простых регулярных выражений:

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

и заменить группы словами ..

но я не могу вспомнить, в какой переменной хранится количество созданных групп, извините.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...