大きなテキストファイルに複数回表示される「キー」を対応する代替「値」に置き換えます。

大きなテキストファイルに複数回表示される「キー」を対応する代替「値」に置き換えます。

大きなテキストファイルでは、複数の単語(「キー」と呼ばれる)を別の代替テキスト(「値」と呼ばれる)に置き換える必要があります。現在私はsedこの目的のために次のようなものを使います。

sed -i -e 's/\bkey\b/value/' file

ファイルが大きく、プロセスに数分かかります。 1,000を超えるキーと値のペアがあり、現在sed各キーと値のペアに対してプロセスを繰り返しています。明らかにこれは長い時間がかかります。

一度に(またはより速く)交換を実行できるように、「Key-Value」(パターン交換)ペアセットをsed/または同様のユーティリティに入力する方法があるかどうか疑問に思います。awkキーと値のペアは任意の形式で構成できます。

たとえば、名前を略語(TSV形式など)に変更します。

Key                                               Value
United Nations                                    UN
United States Environmental Protection Agency     EPA
International Atomic Energy Agency                IAEA
World Health Organization                         WHO

入力テキストは次のとおりです。

これは国連と世界保健機関(WHO)が報告した内容です。これがIAEAの主な分野です。米国環境保護局は、この問題を監督する連邦機関です。

答え1

ここでは-i両方とも\bいくつかのsed実装ですperl。まず、以下を使用することをお勧めしますperl

perl -i -pe '
  BEGIN {
    %map = (
      "key1"  => "value1",
      "key 2" => "value2"
    );
    $re = join "|", map {qr{\Q$_\E}} keys %map;
  }
  s/\b(?:$re)\b/$map{$&}/g' your-file

キー => 値マッピングは、次のように表現することもできます。

%map = qw(
   key1 value1
   key2 value2
);

または、対応するperlモジュール(Text::CSV、)を使用して、JSONCSVまたは他の構造化形式から読み込みますperl。テキスト操作に適した汎用プログラミング言語なので、ここでは明確な選択であり、実行できる操作に制限はありません。 。

単純なTSVの場合は、次のようになります。

<map.tsv perl -i -pe '
  BEGIN {
    <STDIN>; # skip header
    while (<STDIN>) {
      chomp;
      my ($k, $v) = split /\t/;
      $map{$k} = $v;
    }

    $re = join "|", map {qr{\Q$_\E}} keys %map;
  }
  s/\b(?:$re)\b/$map{$&}/g' your-file

次の作業を行う場合は注意してください。

sed -i -e 's/\bK1\b/V1/g' file
sed -i -e 's/\bK2\b/V2/g' file

次のように単純化できます。

sed -i '
  s/\bK1\b/V1/g
  s/\bK2\b/V2/g' file

またはTSVの場合:

<map.tsv awk -F'\t' '
   NR > 1 {
     # escape regexp operators in keys to emulate perl \Q \E:
     gsub(/[][\/\\*.^$]/, "\\\\&", $1)
     # escape /, \ and & in replacement:
     gsub(/[\\/&]/, "\\\\&", $2)
     print "s/\\b"$1"\\b/"$2"/g"
   }' | sed -i -f - your-file

ファイルを一度だけ読み書きします。

ただし、いずれの場合も一部の場合価値その中でも。たとえば、s/\bA\b/B/gヒールを使用すると、sの代わりにsがs/\bB\b/C/g表示されます。上記の方法はubtitute演算子のみを実行するため、問題はありません。ACBperls

また、perl正規表現では左から右へのシフトを処理するため、入力s/\b(?:foo|foo bar)\b/$map{$&}/gに、がある場合は代わりにfoo barそれを置き換えます。foofoo bar

連想配列はランダムな順序で探索されることを覚えておいてください。

sed-E-r(BREを使用するか、BREで拡張正規表現をサポートする実装の場合\|)代わりに、最長の一致を見つけようとします。

perlを組み合わせる前に、キーを長さで並べ替えることで、同じ動作を得ることができます(例:で|置き換えます)。keys %mapsort {length$b <=> length$a} keys %map

最後の注意:デフォルトでperlは、入力はバイト単位で処理され、単語文字(\b単語と単語以外の文字の境界と一致する)はASCII文字、数字、および下線に制限され、実装では通常ロケールsedの文字セットに従ってそれをデコードしますします。入力またはキー/値にASCII以外の文字が含まれている場合は、追加してロケールの文字セットに従ってデコードできます-Mopen=locale。または、UTF-8(現在最も一般的なロケールエンコーディング)の場合は、その-Cオプションを追加できます。

答え2

提供された例のように、Sunnyマッピングのみを処理したいとします(例:正規表現または逆参照メタ文字なし、大文字と小文字の変更なし、部分文字列なし、ループマッピングなしなど)、awkを使用してください。

$ awk -F'\t+' '
    NR==FNR { if (NR>1) map[$1]=$2; next }
    { for (key in map) gsub(key,map[key]); print }
' map_file input_file
This has been covered by both the UN and WHO. This is the main domain of the IAEA. EPA is a federal agency supervising this matter.

これが必要なものでない場合は、質問を編集してより代表的な入力/出力の例を提供してください。

答え3

使用幸せ(以前のPerl_6)

~$ raku -pe 'BEGIN my %h = ("United Nations" => "UN",  \
             "United States Environmental Protection Agency" => "EPA",  \
             "International Atomic Energy Agency" => "IAEA",  \
             "World Health Organization" => "WHO");  \
             s:g/@(%h.keys)/%h{$/}/;'   file

または:

~$ raku -ne 'BEGIN my %h = ("United Nations" => "UN",  \
             "United States Environmental Protection Agency" => "EPA",  \
             "International Atomic Energy Agency" => "IAEA",  \
             "World Health Organization" => "WHO");  \
             put S:g/@(%h.keys)/%h{$/}/ given $_;'   file

入力例:

This has been covered by both the United Nations and World Health Organization. This is the main domain of the International Atomic Energy Agency. United States Environmental Protection Agency is a federal agency supervising this matter.

出力例:

This has been covered by both the UN and WHO. This is the main domain of the IAEA. EPA is a federal agency supervising this matter.

RakuはPerlプログラミング言語シリーズのプログラミング言語です。 Rakuの最良の「ユースケース」は、おそらくUnicode置換を一貫して処理する必要がある場合です。 Rakuは組み込みのUnicodeの高度なサポートを提供しているからです。

つまり、興味のあるペアを使用してハッシュを作成します%h。注 - Raku正規表現でハッシュを直接使用しようとすると、次の警告が表示されます。keyvalue正規表現にハッシュ変数を引き続き使用してください。代わり%h.keysに、ハッシュ値をkeys最初に取得し、@(…)一致子の半分の配列にキャストします(正規表現一致子の-sigiledまたは-sigiled変数は、$Rakuに文字通り文字列化されたコンテンツを挿入するように指示します)。代替クラスでは、一致変数は/ペアでデコードされた対応する値です。@$/valuekeyvalue

[2番目の例では、-neRakuの "big-S"表記と一緒にコマンドラインフラグを使用してS///結果文字列を返します]。

もちろん、与えられた他の答えをより完全に複製するには、Rakuの幅が0の単語境界アンカーであるか、またはを<|w>使用できます。これは他の言語のアンカーと同じです。したがって、上記の最後の行は次のようになります。<?wb>\b

s:g/ <?wb> @(%h.keys) <?wb> /%h{$/}/;

<<Rakuの左右の境界を使用することもできます>>(Unicodeシンボルも機能します«»

s:g/ << @(%h.keys) >> /%h{$/}/;


TSVファイルで始まります。

上記のインラインではなく、2列TSVファイルからキーと値のペアをインポートすると、コードがはるかに簡単になります。Text::CSV次のようにコマンドラインでRakuモジュールを使用してください(注:.skip(1)TSVファイルにヘッダーがない場合はこの呼び出しを削除してください)。.[*;*]Rakuは各行をハッシュに追加された2つの要素(キーと値)として扱うため、インデックスの角かっこコードを含めることを忘れないことが重要です%h

~$ raku -MText::CSV -pe 'BEGIN my %h = csv(in => "/path/to/kv_pairs.tsv", sep => "\t").skip(1).[*;*];   
                         s:g/ << @(%h.keys) >> /%h{$/}/;'   file
This has been covered by both the UN and WHO. This is the main domain of the IAEA. EPA is a federal agency supervising this matter.

または:

~$ raku -MText::CSV -ne 'BEGIN my %h = csv(in => "/path/to/kv_pairs.tsv", sep => "\t").skip(1).[*;*];   
                         put S:g/ << @(%h.keys) >> /%h{$/}/ given $_;'   file
This has been covered by both the UN and WHO. This is the main domain of the IAEA. EPA is a federal agency supervising this matter.

https://docs.raku.org/言語/regexes
https://docs.raku.org
https://raku.org

答え4

私たちが言うと

  • 代替価値Key-Value マップファイル自体には次のものを含めることはできません。これには、代替項目(自己関連キーを含む!)が必要です。
  • マッピングファイルはタブで区切られます。

次のawk手順が機能します。

awk -F'\t' 'NR==FNR{repl[$1]=$2;klen[$1]=length($1);next}
            {for (key in repl) {
               while (i=index($0,key)) {
                 $0=substr($0,1,i-1) repl[key] substr($0,i+klen[key])
               }
             }
            }1' mapfile.txt input.txt

これにより、最初に入力フィールド区切り文字がTABに設定され、マッピングファイルが最初に処理され、次に実際の入力ファイルが処理されます。

  • 最初のファイル(グローバルラインカウンタFNRと同じファイル別のラインカウンタとして表示されます)を処理するときに実行する代替項目で配列を入力し、別の配列で「キー」の長さを追跡します。その後、処理のために次の行に移動します。NRreplklen
  • 2番目のファイルを処理すると、条件が満たされなくなったためNR==FNRスキップします。repl各入力行のすべての代替キー(配列のすべてのインデックスなど)を繰り返し、このindex()関数を使用して入力行に表示されることを確認します。
  • keyその場合は、サブストリング内の入力行を再組み立てして発生項目を置き換えます。今後、交換key後、次の部分文字列key
  • 特定の入力ラインが複数回表示されるwhile場合は、すべての項目が置き換えられるようにループでこれを行います。key
  • 正規表現ベースではなく、この「手動」アプローチを使用する理由は、この方法を使用すると、gsub()値の表示方法に制限がないためです。正規表現関連文字を使用すると、予期しない動作が発生する可能性がありますkeygsub()key

入力例の場合、出力は次のようになります。

これは国連と世界保健機関(WHO)が報告した内容です。これがIAEAの主な分野です。 EPAはこの問題を監督する連邦機関です。

ノートすべてのawkバージョンと実装が内部編集を実行できるわけではありません(flagsと同じ-i)。かなり新しいGNU Awk(> 4.1.0)がある場合は、-i inplaceこの機能の拡張を使用できます。

さらに、現在の形式では、このプログラムは置換の「単語境界」制約を実装しません。

関連情報