【问题标题】:How do I tokenise a word given tokens that are subsumed incompletely in the word?给定单词中不完全包含的标记,如何标记单词?
【发布时间】:2011-12-15 17:43:40
【问题描述】:

我了解如何通过以下方式在 Perl 中使用正则表达式:

$str =~ s/expression/replacement/g;

我了解,如果表达式的任何部分括在括号中,则可以在替换部分中使用和捕获它,如下所示:

$str =~ s/(a)/($1)dosomething/;

但是有没有办法捕获正则表达式外部上方的($1)

我有一个完整的单词,它是一串辅音,例如bEdmA,它的元音版本baEodamaA(其中ao 是元音),以及由空格分隔的两个标记的拆分形式bEd maA。我只想从完整单词中提取标记的元音形式,如下所示:beEodamaA。我正在尝试在完整的单词表达式中捕获标记,所以我有:

$unvowelizedword = "bEdmA";
$tokens[0] = "bEd", $tokens[1] = "mA";
$vowelizedword = "baEodamA";

foreach $t(@tokens) {
    #find the token within the full word, and capture its vowels
}

我正在尝试做这样的事情:

$vowelizedword = m/($t)/;

这是完全错误的,原因有两个:令牌$t 不完全以其自己的形式存在,例如bEd,但类似m/b.E.d/ 的东西会更相关。另外,如何在正则表达式外部的变量中捕获它?

真正的问题是:给定完整单词beEodamaA 中的标记bEdmA,我如何捕获元音序列baEodamaA


编辑

我从所有答案中意识到我错过了两个重要的细节。

  1. 元音是可选的。因此,如果标记是:“Al”和“ywm”,并且全元音单词是“Alyawmi”,那么输出标记将是“Al”和“yawmi”。
  2. 我只提到了两个元音,但还有更多,包括由两个字符组成的符号,比如'~a'。完整列表(虽然我认为我不需要在这里提及)是:

    @vowels = ('a', 'i', 'u', 'o', '~', '~a', '~i', '~u', 'N', 'F', 'K', '~N', '~K');

【问题讨论】:

  • 这不是英语。这是音译 - 这些符号代表另一种语言的元音。

标签: regex perl token


【解决方案1】:

以下似乎可以满足您的要求:

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

my @tokens = ('bEd', 'mA');
my $vowelizedword = "beEodamaA";

my @regex = map { join('.?', split //) . '.?' } @tokens;

my $regex = join('|', @regex);
$regex = qr/($regex)/;

while (my ($matched) = $vowelizedword =~ $regex) {
    $vowelizedword =~ s{$regex}{};
    print "matched $matched\n";
}

根据您更新的问题进行更新(元音是可选的)。它从字符串的末尾开始工作,因此您必须将标记收集到一个数组中并反向打印它们:

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

my @tokens = ('bEd', 'mA', 'Al', 'ywm');
my $vowelizedword = "beEodamaA Alyawmi"; # Caveat: Without the space it won't work.

my @regex = map { join('.?', split //) . '.?$' } @tokens;

my $regex = join('|', @regex);
$regex = qr/($regex)/;

while (my ($matched) = $vowelizedword =~ $regex) {
        $vowelizedword =~ s{$regex}{};
            print "matched $matched\n";
}

【讨论】:

  • 谢谢,这看起来很整洁,工作正常 - 除了一件事 - 元音是可选的 - 我刚刚编辑了这个问题。
  • 替换 '.?'正则表达式 '(' . join('|', @vowels) . ')?'你可能会更接近你想要的。
【解决方案2】:

在所谓的“列表上下文”中使用m// 运算符,如下所示:

my @tokens = ($input =~ m/capturing_regex_here/modifiershere);

【讨论】:

    【解决方案3】:

    ETA:据我所知,你想说的是你想在标记的每个字符之后匹配一个可选的元音。

    有了这个,您可以调整$vowels 变量以仅包含您要查找的字母。或者,您也可以只使用. 来捕获任何字符。

    use strict;
    use warnings;
    use Data::Dumper;
    
    my @tokens = ("bEd", "mA");
    my $full = "baEodamA";
    
    my $vowels = "[aeiouy]";
    my @matches;
    for my $rx (@tokens) {
        $rx =~ s/.\K/$vowels?/g;
        if ($full =~ /$rx/) {
            push @matches, $full =~ /$rx/g;
        }
    }
    
    print Dumper \@matches;
    

    输出:

    $VAR1 = [
              'baEoda',
              'mA'
            ];
    

    注意

    ... $full =~ /$rx/g;
    

    not require capturing groups 在正则表达式中。

    【讨论】:

    • @briandfoy 这是一个非常方便的功能,比其他选项更令人赏心悦目。
    • 是的,没错!元音是可选的。但是我该如何处理元音可能不止一个字符的事实呢?我刚刚编辑了问题。
    【解决方案4】:

    我怀疑有一种更简单的方法可以完成您想要完成的任何事情。诀窍是不要让正则表达式生成代码变得如此棘手,以至于您忘记了它实际上在做什么。

    我只能开始猜测您的任务,但从您的单个示例来看,您似乎想检查两个子标记是否在较大的标记中,而忽略某些字符。我猜这些子标记必须是有序的,除了那些元音字符之外,它们之间不能有其他任何东西。

    为了匹配标记,我可以在标量上下文中使用 \G 锚点和 /g 全局标志。这会将匹配锚定到相同标量的最后一次匹配结束后的字符。这种方式使我可以为每个子令牌拥有单独的模式。这更容易管理,因为我只需要更改 @subtokens 中的值列表。

    一旦你检查了每一对并找到哪些匹配所有模式,我就可以从这对中提取原始字符串。

    use v5.14;
    
    my $vowels    = '[ao]*';
    my @subtokens = qw(bEd mA);
    
    # prepare the subtoken regular expressions
    my @patterns = map {
        my $s = join "$vowels", map quotemeta, (split( // ), '');
        qr/$s/;
        } @subtokens;
    
    my @tokens = qw( baEodamA mAabaEod baEoda mAbaEoda );
    
    my @grand_matches;
    TOKEN: foreach my $token ( @tokens ) {
        say "-------\nMatching $token..........";
        my @matches;
        PATTERN: foreach my $pattern ( @patterns ) {
            say "Position is ", pos($token) // 0;
    
            # scalar context /g and \G
            next TOKEN unless $token =~ /\G($pattern)/g; 
            push @matches, $1;
            say "Matched with $pattern";
            }
        push @grand_matches, [ $token, \@matches ];
        }
    
    # Now report the original   
    foreach my $tuple ( @grand_matches ) {
        say "$tuple->[0] has both fragments: @{$tuple->[1]}";
        }
    

    现在,这是这个结构的好处。我可能猜错了你的任务。如果有,无需更改设置即可轻松修复。假设子标记不必按顺序排列。这是对我创建的模式的简单更改。我只是摆脱 \G 锚点和/g 标志;

            next TOKEN unless $token =~ /($pattern)/; 
    

    或者,假设标记必须有序,但它们之间可能有其他东西。我可以插入一个.*? 来匹配那些东西,有效地跳过它:

            next TOKEN unless $token =~ /\G.*?($pattern)/g; 
    

    如果我可以从创建模式的map 管理所有这些会更好,但/g 标志不是模式标志。它必须与运营商一起去。

    我发现当我不将所有内容都包装在一个正则表达式中时,管理不断变化的需求要容易得多。

    【讨论】:

      【解决方案5】:

      假设标记需要按顺序出现并且它们之间没有任何东西(除了元音):

      my @tokens = ( "bEd", "mA" );
      my $vowelizedword = "baEodamaA";
      
      my $vowels = '[ao]';
      my (@vowelized_sequences) = $vowelizedword =~ ( '^' . join( '', map "(" . join( $vowels, split( //, $_ ) ) . "(?:$vowels)?)", @tokens ) . '\\z' );
      print for @vowelized_sequences;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-06-06
        • 2017-01-21
        • 1970-01-01
        • 2013-01-01
        • 2017-08-02
        • 1970-01-01
        • 2016-06-16
        相关资源
        最近更新 更多