サブストリングをそのサブストリングで索引付けされた辞書から取得した値に置き換える方法

サブストリングをそのサブストリングで索引付けされた辞書から取得した値に置き換える方法

正規表現に一致する可能な文字列の一部である可能な部分文字列を、インデックスが関連する部分文字列の配列から取得された値に置き換えて、大容量ファイルを解析する必要があります。

ファイルはプレーンテキストファイルです。つまり、改行で区切られた行であり、各行には、ASCII 32からASCII 126までのすべての文字、デフォルトではCロケールの制御文字を除くすべての印刷可能文字を含めることができます。

関心のある文字列と正確に一致する拡張正規表現はであり\<prefix-[[:alnum:]]{2,}\>、問​​題の部分文字列はダッシュの後のすべての文字列です。

サンプル(合成)入力を使用してください。たとえば、次のようになります。

# arbitrary number of comment lines of any length
:prefix-foo ; arbitrary strings
# arbitrary number of comment lines of any length foo -prefix-foo-
-bar -foo-xx arbitrary string -yet-more strings prefix-foo-bar MORE strings
YET more --STRINGS prefix-bar -prefix-foo-STRingS--
even MORE strings ; prefix -foo -yy--more-and-prefix-bar-and-more

次のサンプル辞書があります。

dictionary["foo"] = 2
dictionary["bar"] = 15

希望の出力は次のとおりです。

# arbitrary number of comment lines of any length
:prefix-2 ; arbitrary strings
# arbitrary number of comment lines of any length foo -prefix-2-
-bar -foo-xx arbitrary string -yet-more strings prefix-2-bar MORE strings
YET more --STRINGS prefix-15 -prefix-2-STRingS--
even MORE strings ; prefix -foo -yy--more-and-prefix-15-and-more

私はこれが最高のツールだと思います。特に、awk単一のフィールドを置き換えてレコード全体を書き換える機能が本質的にあるためです。だから私は次のスクリプトを思いついた。$0$1...$n

#!/usr/bin/gawk -f

BEGIN {
    # first fill in dictionary
    while ("cmd-providing-dictionary" | getline) {
            dictionary[$1] = $2
    }
    close("cmd-providing-dictionary")
    # pattern that matches interesting fields
    field_regex = "\\<prefix-[[:alnum:]]{2,}\\>"
    # I don't care default splitting of line
    FS = OFS = ""
}
{
    # split line in fields as per regex
    if (patsplit($0, fields, field_regex, seps)) {
        FS = OFS = "-"
        # for each field, split it on dash character,
        # modify its substring as per dictionary,
        # and finally rebuild it
        for (fn in fields) {
            $0 = fields[fn]
            if ($2 in dictionary) {
                    $2 = dictionary[$2]
                    fields[fn] = $0
            }
        }
        FS = OFS = ""
        # clear whole record and rebuild it with
        # fields computed above + original separators
        $0 = ""
        for (fn in fields)
            $fn = seps[fn - 1] fields[fn]
        $(fn+1) = seps[fn]
    }
    print
}

たとえ私がawkをうまく扱っていないとしても、上記のコードは十分に速く正しい仕事をするように見えますが、少し不快に見え、awk不自然な方法で何かが起こるように強制するように感じます.同じ結果を得るより良い方法があるかどうか疑問に思います。またはより良いツールもあります。

gsub()私の最初の考えは、orを使用して単純な正規表現置換を実行することでしたが、正規表現のサブ式gensub()(この場合\<prefix-([[:alnum:]]{2,})\>)をクエリ配列として使用し、それを代替文字列で使用する(きれいな)方法が見つかりませんでした。値。一方、すべての辞書キーを繰り返してallgsubを常に適用することは、実際には実現できません。辞書が非常に大きく、したがって非常に非効率的であるからです。

ベストアンサー1

比較のために代替項目から関数を呼び出すことができ、多くの利点を得ることができる非専門家バージョンのPerlを紹介します。まるであなたが言えるように

gsub(regexp, call_function(matched_part), variable_to_change)

この関数は代替文字列を返します。

#!/usr/bin/perl
use strict;
my %d;
sub fix{
  my ($prefix,$str) = @_;
  $str = $d{$str} if defined $d{$str};
  return "$prefix$str";
}
open(D,"dictionary") or die;
while(<D>){
  $d{$1} = $2 if $_ =~ m/^([^ ]+) ([^ \n]+)/;
}
close(D);
while(<>){
  $_ =~ s/\b(prefix-)([[:alnum:]]{2,})\b/fix($1,$2)/ge;
  print;
}

ここで置換コマンドは、$_ =~ s/regex/fix($1,$2)/ge現在の行(g)をグローバルに変更し、(e)正規表現キャプチャグループ(内部)で、およびを含む$_代替文字列を実行します。fix()$1$2()

おすすめ記事