Как я могу сопоставить текст и заменить его вычисленным значением, основанным на совпадении в Perl? - PullRequest
4 голосов
/ 04 января 2010

У меня есть функция, которая называется f, которая принимает строку и возвращает строку.

У меня есть файл со строками, которые выглядят так:

stuff:morestuff:stuff*:otherstuff:otherstuff*\n

Двоеточия появляются только в качестве разделителей, а * появляется только в конце каждого слова. Я хочу перебрать файл и заменить все экземпляры вещи * на f (вещи). Предыдущая строка будет идти к

stuff:morestuff:f(stuff):otherstuff:f(otherstuff)\n

Я могу сделать это в несколько строк, но должен быть способ сделать это в одной.

Редактировать Для ясности, под f (материал) я имею в виду f, называемый «материал», а не строка «f (материал)».

Ответы [ 4 ]

9 голосов
/ 04 января 2010

Если вы используете опцию e для s//, то выражение правой руки оценивается как код. Так что это так просто, как:

$line =~ s/([^:]+)\*/f($1)/ge;

Разбивка матча:

  • ( начинает отмечать часть рисунка
  • [^:] означает что угодно, кроме :
  • + означает один или несколько из них, то есть один или несколько символов, не являющихся двоеточием
  • ) заканчивается маркировкой части шаблона как $1
  • \* буквально означает *

Этот шаблон основан на том факте, что * появляется только в конце каждого слова. Если он может появиться в середине поля, вам нужно немного подправить шаблон.

Или, поместив шаблон в целый скрипт:

sub f {
    my $word = shift;
    $word =~ tr/a-z/A-Z/;
    return $word;
}

while (<>) {
    s/([^:]+)\*/f($1)/ge;
    print;
}
4 голосов
/ 04 января 2010

Я бы сделал это так:

#! /usr/bin/perl

use warnings;
use strict;

sub f { uc reverse $_[0] }

while (<DATA>) {
  chomp;

  my $out = join ":" =>
            map s/(stuff)\*$/$1/ ? f($_) : $_,
            split /:/;

  print $out, "\n";
}

__DATA__
stuff:morestuff:stuff*:otherstuff:otherstuff*
otherstuff
stuff
stuff*
stuff*:otherstuff*

Выход:

stuff:morestuff:FFUTS:otherstuff:FFUTSREHTO
otherstuff
stuff
FFUTS
FFUTS:FFUTSREHTO

Но если у вас аллинерегексит, переходите на

while (<DATA>) {
  chomp;

  s/  (?:^|(?<=:))     # BOL or just after colon
      ([^:]*stuff)\*   # ending with 'stuff*'
      (?=:|$)          # at EOL or just before colon
  / f $1 /gex;

  print $_, "\n";
}

Это работает из-за переключателя /e :

A /e приведет к тому, что заменяющая часть будет рассматриваться как полноценное выражение Perl и будет оцениваться тут же.

2 голосов
/ 04 января 2010
 $string =~ s{ (^|:) ([^:*]+) \* }{$1 . f($2)}gxe;

должно быть достаточно.

0 голосов
/ 04 января 2010
$a=~s/(^|:)([^*:]*)\*(?=(:|$))/\1f\(\2\)/g;

-EDIT-

Если f () - функция, я не вижу особой причины делать это в одной строке. split - process - join

def f(x):
    return x.upper()

a='stuff*:morestuff:stuff*:otherstuff:otherstuff*\n';

print ':'.join([f(x[:-1]) if x[-1]=='*' else x for x in a.strip().split(':')])

Звучит так же просто, как и задача. Я люблю питона;)

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