我直接在查詢中清理一些文本,而不是使用嵌套替換函式,我發現這段代碼使用 perl 一次執行多個替換:multi-replace with perl
CREATE FUNCTION pg_temp.multi_replace(string text, orig text[], repl text[])
RETURNS text
AS $BODY$
my ($string, $orig, $repl) = @_;
my %subs;
if (@$orig != @$repl) {
elog(ERROR, "array sizes mismatch");
}
if (ref @$orig[0] eq 'ARRAY' || ref @$repl[0] eq 'ARRAY') {
elog(ERROR, "array dimensions mismatch");
}
@subs{@$orig} = @$repl;
my $re = join "|",
sort { (length($b) <=> length($a)) } keys %subs;
$re = qr/($re)/;
$string =~ s/$re/$subs{$1}/g;
return $string;
$BODY$ language plperl strict immutable;
示例查詢:
select
name as original_name,
multi_replace(name, '{-,&,LLC$}', '{_,and,business}') as cleaned_name
from some_table
該函式在名稱字串的末尾找到模式 LLC,但將其洗掉,而不是用“business”替換它。
我怎樣才能使這項作業按預期進行?
uj5u.com熱心網友回復:
當字串@$orig要按字面匹配時,我實際上會使用它:
my ($string, $orig, $repl) = @_;
# Argument checks here.
my %subs; @subs{ @$orig } = @$repl;
my $pat =
join "|",
map quotemeta,
sort { length($b) <=> length($a) }
@$orig;
return $string =~ s/$re/$subs{$&}/gr;
特別map quotemeta,是失蹤了。
(順便說一句,如果您確保在要替換和之前出現sort,則不需要該行。)xyx@$origx(?!y)xy
但是您希望將其中的字串@$orig視為正則運算式模式。為此,您可以使用以下內容:
# IMPORTANT! Only provide strings from trusted sources in
# `@$orig` as it allows execution of arbitrary Perl code.
my ($string, $orig, $repl) = @_;
# Argument checks here.
my $re =
join "|",
map "(?:$orig->[$_])(?{ $_ })",
0..$#$orig;
{
use re qw( eval );
$re = qr/$re/;
}
return $string =~ s/$re/$repl->[$^R]/gr;
use re qw( eval );但是,在您的環境中,我對and的可用性有疑問(?{ }),因此上述方法對您來說可能是不可行的解決方案。
my ($string, $orig, $repl) = @_;
# Argument checks here.
my $re =
join "|",
map "(?<_$_>$orig->[$_])",
0..$#$orig;
$re = qr/$re/;
return
$string =~ s{$re}{
my ( $n ) =
map substr( $_, 1 ),
grep { $-{$_} && defined( $-{$_}[0] ) }
grep { /^_\d \z/aa }
keys( %- );
$repl->[$n]
}egr;
uj5u.com熱心網友回復:
雖然正則運算式測驗LLC$具有特殊含義的 ,但$捕獲$1的只是字串LLC,因此它找不到要替換的查找值。
如果您唯一關心的是$,那么您可以通過將地圖構建線更改為:
@subs{map {my $t=$_; $t=~s/\$$//; $t} @$orig} = @$repl;
my $re = join "|",
sort { (length($b) <=> length($a)) } @$orig;
但是很難讓它更普遍地適用于正則運算式的每一個可能的特性。
uj5u.com熱心網友回復:
plperl鏈接的博客文章中此函式的目的是查找/替換字串,而不是正則運算式。在原始代碼中不會作為搜索詞LLC被發現,因為搜索詞在被包含之前會經過(也建議在ikegami 的答案中)LLC$quotemeta$re
洗掉quotemeta轉換的效果是LLC在字串的末尾匹配,但是由于在其中找不到它作為鍵$subs(因為鍵存在LLC$),所以它被一個空字串替換。
orig那么如何使用引數中的正則運算式使其作業呢?
@ikegami 提出的解決方案似乎無法使用plperl,因為它抱怨此錯誤:無法將 re.pm 加載到 plperl中。
我想到了一個沒有該功能的替代實作(?{ code }):可以在orig使用/ge. 在第一次匹配時,選擇相應的字串 inrepl作為替換。代碼:
CREATE or replace FUNCTION pg_temp.multi_replace(string text, orig text[], repl text[])
RETURNS text AS
$BODY$
my ($string, $orig, $repl) = @_;
my %subs;
if (@$orig != @$repl) {
elog(ERROR, "array sizes mismatch");
}
if (ref @$orig[0] eq 'ARRAY' || ref @$repl[0] eq 'ARRAY') {
elog(ERROR, "array dimensions mismatch");
}
@subs{@$orig} = @$repl;
my $re = join "|", keys %subs;
$re = qr/($re)/;
# on each match, recheck the match individually against each regexp
# to find the corresponding replacement string
$string =~ s/$re/{ my $r; foreach (@$orig) { if ($1 =~ $_) {$r=$subs{$_}; last;} } $r;}/ge;
return $string;
$BODY$ language plperl strict immutable;
測驗
=> select pg_temp.multi_replace(
'bar foo - bar & LLC',
'{^bar,-,&,LLC$}',
'{baz,_,and,business}'
);
multi_replace
----------------------------
baz foo _ bar and business
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/475689.html
標籤:正则表达式 PostgreSQL perl
