如何替换字符串并保留其大写/小写

时间:2010-06-25 08:02:10

标签: regex perl

我想在Perl中用另一个字符串替换一个字符串;两者的长度相同。我想替换所有出现的字符串(不区分大小写),但我希望保留字母的大小写。因此,如果第一个字母是大写字母,则替换后的第一个字母也将是大写字母。

例如,如果我想用“bar”替换“foo”,那么我想要那个

foo ==> bar
Foo ==> Bar
FOO ==> BAR

在Perl中有一种简单的方法吗?

10 个答案:

答案 0 :(得分:13)

这可能就是你所追求的:

How do I substitute case insensitively on the LHS while preserving case on the RHS?

这几乎是直接从以上链接复制的:

sub preserve_case($$) {
    my ($old, $new) = @_;
    my $mask = uc $old ^ $old;
    uc $new | $mask .
    substr($mask, -1) x (length($new) - length($old))
}

my $string;

$string = "this is a Foo case";
$string =~ s/(Foo)/preserve_case($1, "bar")/egi;
print "$string\n";

# this is a Bar case

$string = "this is a foo case";
$string =~ s/(Foo)/preserve_case($1, "bar")/egi;
print "$string\n";

# this is a bar case

$string = "this is a FOO case";
$string =~ s/(Foo)/preserve_case($1, "bar")/egi;
print "$string\n";

# this is a BAR case

答案 1 :(得分:13)

perldoc perlfaq6提供了一些见解:

  

如何在保留RHS案例的同时对LHS不区分大小写?

     

这是Larry Rosler的一个可爱的Perlish解决方案。它利用了   ASCII字符串上按位xor的属性。

$_= "this is a TEsT case";
$old = 'test';
$new = 'success';
s{(\Q$old\E)}
    { uc $new | (uc $1 ^ $1) .
            (uc(substr $1, -1) ^ substr $1, -1) x
            (length($new) - length $1)
    }egi;
print;    # 'this is a SUcCESS case'
     

在这里它是一个子程序,模仿上述:

sub preserve_case {
        my ($old, $new) = @_;
        my $mask = uc $old ^ $old;
        uc $new | $mask .
            substr($mask, -1) x (length($new) - length($old))
    }

$string = "this is a TEsT case";
$string =~ s/(test)/preserve_case($1, "success")/egi;
print "$string\n";
     

打印:

this is a SUcCESS case

所以你可以像这样使用preserve_case()子程序。只是不要指望Unicode奇迹:)

s[\b(abc)\b][preserve_case($1,'xyz')]ei ;

答案 2 :(得分:6)

$text =~ s/\b(?:(Abc)|abc)\b/ $1 ? 'Xyz' : 'xyz' /eg;

如果实际列表较长,则可以使用查找表。

my %translations = (
   'Abc' => 'Xyz',  'abc' => 'xyz',
   'Def' => 'Ghi',  'def' => 'ghi',
   'Jkl' => 'Mno',  'jkl' => 'mno',
);

my $alt_pat = join '|', map quotemeta, keys(%translations);

$text =~ s/\b($alt_pat)\b/$translations{$1}/g;

但是这仍然会留下一些可以通过派生小写版本来删除的重复。

my %translations = (
   'Abc' => 'Xyz',
   'Def' => 'Ghi',
   'Jkl' => 'Mno',
);

%translations = ( ( map lc, %translations ), %translations );

my $alt_pat = join '|', map quotemeta, keys(%translations);

$text =~ s/\b($alt_pat)\b/$translations{$1}/g;

答案 3 :(得分:5)

这是一个解决方案,将“将一个字符串更改为与另一个字符串的大小写相匹配”的概念转化为函数,并调用该函数来构建替换。

sub matchcap
{
  my ($s,$r) = @_;
  return $s eq ucfirst($s) ? ucfirst($r) : lcfirst($r);
}

s/\b(Abc|abc)\b/matchcap($1,'xyz')/ge;

答案 4 :(得分:3)

有点黑客,使用实验代码扩展正则表达式:

$text =~ s/\b([Aa])(?{ $n=chr(ord($^N)+23) })bc/${n}yz/

首先,将字母A与([Aa])匹配。以下(?{...})包含任意代码,其中$^N包含最近捕获的子组的文本。 23是A和X之间ASCII码的差异(大写和小写),因此$n包含字母X,其大小写与相应的A相同。

(这不应被视为编写此类代码的认可,而是作为此实验性正则表达式的一个有趣示例。)

答案 5 :(得分:3)

这是一个“半perlish”解决方案,适用于任意regexps Unicode数据:

sub adjust_case {
    my ($text, $case) = @_;
    $case .= substr($case, -1) x (length($text) - length($case));
    $_ = [ split // ] for $text, $case;
    return join "", map {
        $case->[$_] =~ /\p{Upper}/ ? uc $text->[$_] :
        $case->[$_] =~ /\p{Lower}/ ? lc $text->[$_] : $text->[$_]
    } 0 .. $#$text;
}

my $regexp  = qr/\b(abc\w*)\b/i;
my $replace = "Xyzzy";

s/$regexp/adjust_case $replace, ${^MATCH}/egp;

答案 6 :(得分:2)

你可以这样做:

my %trans = (
    'Abc' => Xyz, 
    'abc' => xyz,
);
$text =~s/\b(Abc|abc)\b/$trans{$1}/ge;

答案 7 :(得分:1)

你知道每个字符串的长度是相同的,所以基本上你可以:

index = Pos(string, oldString)
for i = index to index + strlen(oldString)
  if (oldString[i] >= 'a') && (oldString[i] <= 'z'')
    string[i] = ToLower(newString[i])
  else
    string[i] = ToUpper(newString[i])0x20

答案 8 :(得分:0)

逐个字符检查。如果字符的ASCII值以大写ASCII值表示,请替换为大写,否则为小写。

答案 9 :(得分:0)

这是一个巧妙的技巧,在替换结果中使用非破坏性音译(在Perl 5.14中可用)。

use 5.014;
$string =~ s/\b(f)(o)(o)\b/ ($1 =~ tr{fF}{bB}r) . ($2 =~ tr{oO}{aA}r) . ($3 =~ tr{oO}{rR}r) /egi;

如果连续的字母组具有相同的替换,您甚至可以缩短它,例如

# foo ==> see, FoO ==> SeE, etc.
$string =~ s/\b(foo)\b/ $1 =~ tr{fFoO}{sSeE}r /egi;