[topic] すべて置換

文字列中のマッチする部分をすべて置換する方法について。

Posted feedbacks - Nested

Flatten Hidden

Pythonの場合、正規表現がいらないようなマッチなら文字列のreplaceメソッドを使う。正規表現が必要なら正規表現ライブラリのsubメソッドを使う。

1
2
3
4
5
>>> "2007-06-07".replace("-", "/")
'2007/06/07'
>>> import re
>>> re.sub("\d+", "*", "2007/06/07")
'*/*/*'

Python使いがJavaScriptを使ってつまづいたのは、Pythonと同じように文字列にreplaceメソッドがあるけどもこれは置換するのが1つだけだという点。正規表現にgをつけて使うのが正解。

1
2
3
4
5
6
7
8
>>> "2007-10-30".replace("-", "/") # not replace all
"2007/10-30"
>>> "2007-10-30".replace(/-/g, "/")
"2007/10/30"
>>> "2007-10-30".replace("\d+", "*") # considered as string
"2007-10-30"
>>> "2007-10-30".replace(/\d+/g, "*")
"*-*-*"

正規表現は magic オプションとかでいろいろ変わるので注意が必要です。

:help pattern

1
2
3
4
5
substitute("2007-06-07", "-", "/", "g")
"=> 2007/06/07

substitute("2007-06-07", "[0-9][0-9]*", "*", "g") 
"=> *-*-*

Ruby では String#gsub を使います。(一つだけ置換したいなら sub を使います。正規表現に g オプションはありません)

1
2
3
4
5
6
p "2007-06-07".gsub("-", "/")
#=> "2007/06/07"
p "2007-06-07".gsub(/\d+/, "*")
#=> "*-*-*"
p "2007-06-07".gsub(/\d+/) {|m| m.to_i.to_s(16) }
#=> "7d7-6-7"

4通りの方法で実装してみました。 replace3, 4に関しては置換対象として正規表現を想定しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import java.util.regex.*;
public class ReplaceAll {
    public static void main(String[] args) {
        System.out.println(replace1("2007-06-07", "-", "/"));    // => 2007/06/07
        System.out.println(replace2("2007-06-07", "-", "/"));    // => 2007/06/07
        System.out.println(replace3("2007-06-07", "-", "/"));    // => 2007/06/07
        System.out.println(replace4("2007-06-07", "-", "/"));    // => 2007/06/07
        System.out.println(replace1("2007-06-07", "\\d+", "*"));    // => 2007/06/07
        System.out.println(replace2("2007-06-07", "\\d+", "*"));    // => 2007/06/07
        System.out.println(replace3("2007-06-07", "\\d+", "*"));    // => *-*-*
        System.out.println(replace4("2007-06-07", "\\d+", "*"));    // => *-*-*
    }
    public static String replace1(String str, String target, String replacement) {
        return str.replace(target, replacement);
    }
    public static String replace2(String str, String target, String replacement) {
        StringBuilder sb = new StringBuilder();
        int i, st = 0;
        while ((i = str.indexOf(target, st)) > 0) {
            sb.append(str.substring(st, i));
            sb.append(replacement);
            st = i + replacement.length();
        }
        sb.append(str.substring(st));
        return sb.toString();
    }
    public static String replace3(String str, String target, String replacement) {
        return str.replaceAll(target, replacement);
    }
    public static String replace4(String str, String target, String replacement) {
        Pattern pattern = Pattern.compile(target);
        Matcher matcher = pattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, replacement);
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
}
Squeak Smalltalk では、文字単位の置換には #replaceAll:with: を、文字列の置換には #copyReplaceAll:with: を使います。なお、前者は破壊的です。

後者に似た状況で、しかし一致するトークンのみを置き換えたい場合向けに #copyReplaceTokens:with: が用意されています。

これらで対処できない全置換を行なうには手続き的に書く必要があります。
1
2
3
4
5
6
7
8
'song so fit' replaceAll: $s with: $t
"=> 'tong to fit' "

'How now brown cow?' copyReplaceAll: 'ow' with: 'ello'
"=> 'Hello nello brellon cello?' "

'File asFile Files File''s File' copyReplaceTokens: 'File' with: 'Snick'
"=> 'Snick asFile Files Snick''s Snick' "

Gaucheではregexp-replace-all*を使います。 なお、正規表現-置換文字列の組を複数指定することもできます。

(スター無しのregexp-replace-allというのもあるんですが、正規表現-置換文字列の組がひとつしか指定できないのと、歴史的経緯から引数の順序が違います。一組の場合もregexp-replace-all*でカバーできることからこちらを推奨)

1
2
3
4
gosh> (regexp-replace-all* "2007-10-30" #/\d+/ "*")
"*-*-*"
gosh> (regexp-replace-all* "2007-10-30" #/\d+/ "*" #/-/ "=")
"*=*=*"

上:正規表現使用しない版 下:正規表現使用する版

1
2
Console.WriteLine("hello, world!".Replace("l", "あ"));
Console.WriteLine(Regex.Replace("hello, world!", "l", "あ"));

Perlではいくつかやり方がありますが、単純に1文字単位の置換では、tr/~/~/ を使えます。 また、文字列の一部を置き換えるのに substrを左辺値に使う方法もあります。 もちろん正規表現で、s/~/~/g で全て置換を行えます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# tr
$date = "2007-10-30";
$date =~ tr#-#/#;
print $date, "\n";

# substr
sub replace {
    my ($str, $target, $replace) = @_;
    for (my $i = index($str, $target); $i >= 0; $i = index($str, $target, $i + 1)) {
        substr($str, $i, length($target)) = $replace;
    }
    $str;
}

$date = "2007-10-30";
print replace($date, "-", "/"), "\n";

# s///g
$date = "2007-10-30";
$date =~ s/\d+/*/g;
print $date, "\n";
なんかもっとシンプルに書けそうな気もするのですが。。。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
--
--  文字列の置き換え
--
  
module Main where
  
import Data.List
  
replace :: String -> String -> String -> String
replace _ _ "" = ""
replace "" _ all = all
replace inp out all@(s:ss)
    | isPrefixOf inp all    = out ++ replace inp out (drop (length inp) all)
    | otherwise             = s : replace inp out ss
  
main = do
    putStrLn $ show $ replace "abc" "ABC" "abcdefgabcdefg"

Index

Feed

Other

Link

Pathtraq

loading...