Удалить слово из конца строки, которая была обрезана до различной длины - PullRequest
0 голосов
/ 24 декабря 2018

Итак, допустим, у меня есть список строк, которые иногда заканчиваются фразой, которая была обрезана до разной длины.В этом примере фраза «hello».

my @strings =
(
    "Test 1 hello",
    "Something else",
    "Test 2 hell",
    "And also he",
    "Test 4 hel"
);

Вот как я могу удалить фрагменты «hello» прямо сейчас:

foreach my $string (@strings)
{
    if ($string =~ m/(.*?)\s*(h(e(l(lo?)?)?)?)?$/)
    {
        print "'", $string, "' -> '", $1, "'\n";
    }
}

Это работает:

'Test 1 hello' -> 'Test 1'
'Something else' -> 'Something else'
'Test 2 hell' -> 'Test 2'
'And also he' -> 'And also'
'Test 4 hel' -> 'Test 4'

Однако я нахожу, что регулярное выражение совпадает со всеми фрагментами «привет», длинным, запутанным и трудно поддающимся изменению для будущих случаев использования.Есть ли более короткий способ написать что-то эквивалентное (h(e(l(lo?)?)?)?)?$?

Ответы [ 5 ]

0 голосов
/ 24 декабря 2018

Вы можете использовать обратную логику: вместо поиска частичных hello s захватите последнее слово и найдите его в hello.

Возможно, этого не произойдетбыть точно короче, но это может быть чище.Захватить последнее слово достаточно просто с помощью /(\w+)$/, и для проверки, содержится ли оно в hello, регулярное выражение не требуется.Будет делать простой вызов index.

foreach (@strings) {
    (my $original = $_) =~ /(\w+)$/;
    s/\s*\w+$// unless index('hello', $1);
    say "'$original' -> '$_'";
}

Чтобы было ясно, index возвращает индекс подстроки $1 в 'hello'.Мы заботимся только о случае, когда он возвращает 0, что означает, что он существует и находится в начале (будет -1, если он не существует или больше 0 в другой позиции).Вот почему мы удаляем последнее слово, только когда эта операция равна 0 с помощью unless.

0 голосов
/ 24 декабря 2018

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

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

my $target = shift || 'hello';

my @strings = (
    "Test 1 hello",
    "Something else",
    "Test 2 hell",
    "And also he",
    "Test 4 hel"
);

my $re_versions = build_regex($target);

foreach my $string (@strings)
{
    if ($string =~ /($re_versions)$/)
    {
        say "'$string' --> $1";
    }
};

sub build_regex {
    my ($s) = @_;
    my @versions;
    while ($s) {
        push @versions, quotemeta $s;
        chop $s;
    }
    return join '|', @versions;
}

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

Если есть причина, по которой вам нужно вернуть скомпилированное регулярное выражение, измените функцию return на

my $re_str = join '|', @versions;
return qr/$re_str/;

, где теперь можно также добавлять флагиэто может подойти.

0 голосов
/ 24 декабря 2018

Вы ищете регулярное выражение для соответствия следующим выражениям в конце строки : hello, hell, hel, he, h.Мы можем ожидать, что перед выражением будет хотя бы один пробел.

Вы можете просто написать:

s/\s+(hello$)|(hell$)|(hel$)|(he$)|(h$)// for @strings;

Это изменит все элементы в массиве так, как вы ожидаете.

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

my $word  = "hello";
my @parts = map { substr $word, 0, $_ } (1..(length $word));
my $match = join "|", map { "(" . $_ . "\$)" } @words;
s/\s+$match// for @strings;
0 голосов
/ 24 декабря 2018

Ответ Доуга упрощает регулярное выражение, но он не работает для вариантов использования, которые являются более сложными, чем мой пример.Ответ GMB работает в любом случае, но также приводит к длинным (но, по общему признанию, более понятным) регулярным выражениям.Мое личное решение - использовать функцию для динамического построения правильного регулярного выражения из любой необходимой строки:

#!/usr/bin/perl

use strict;
use warnings;

my @strings =
    (
        "Test 1 hello",
        "Something else",
        "Test 2 hell",
        "And also he",
        "Test 4 hel"
    );

my $regex = cutOffStringRegex('hello');

foreach my $string (@strings)
{
    if ($string =~ m/(.*?)\s*$regex$/x)
    {
        print "'", $string, "' -> '", $1, "'\n";
    }
}

sub cutOffStringRegex
{
    my ($string) = @_;
    my $resultString = "";
    if (length($string) == 1)
    {
        $resultString = quotemeta $string;
    }
    else
    {
        my $firstChar = quotemeta(substr $string, 0, 1);
        my $rest = substr $string, 1;
        $resultString = $firstChar . cutOffStringRegex($rest);
    }
    return '(' . $resultString . ')?';
}

cutOffStringRegex('hello') приводит к "(h(e(l(l(o)?)?)?)?)?".Так как мой вопрос был «как написать это короче», я не буду отмечать этот ответ как правильный, потому что он определенно не короче.

0 голосов
/ 24 декабря 2018

Если вы хотите удалить фрагменты, начинающиеся с he, причем этот фрагмент является необязательным:

#!/usr/bin/perl

use 5.020;
use strict;
use warnings;

my @strings =
(
    "Test 1 hello",
    "Something else",
    "Test 2 hell",
    "And also he",
    "Test 4 hel"
);

for (@strings){
    s/\hhe[lo]*$//;
    say;
}

Отпечатки:

Test 1
Something else
Test 2
And also
Test 4

Или вы можете сопоставить и сохранитьчто вы хотите:

for (@strings){
    say $1 if /^(.*?)(?:\hhe[lo]*)?$/;
}
# same output

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

for (@strings){
    say if /^(.*?)( he[lo]*)?$/ && (!$2 || ' hello' =~ /^$2/);
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...