辞書ベースの検索と置換を実行しようとしていますが、大文字と小文字を区別/正確に一致させる方法がわかりませんが、非常に難しいことがわかりました。
3つのファイルがあります。fileAは編集するテキスト、FileBは検索する単語のリスト、FileCは置き換える単語のリストです。
paste -ds///g /dev/null /dev/null <(sed 's|[[\.*^\b$\b/]|\\&|g' fileB) <(sed 's|[\&/]|\\\b&\b|g' fileC) /dev/null /dev/null | sed -f - fileA
私が知っている限り、sedで正確な一致を検索して置き換えるには、次のことを行う必要があります。sed 's/\<exact_word_to_replace\>/exact_replacement/g' filename
\<
しかし、上記のコードのどこにあるのかわかりません。\>
行かなければなりません!
もっと良くなるか\b
?それでは、どこに行くのでしょうか?
誰かが私を正しい方向に押してくれることを願っています...
乾杯、ニオブ
これは以下に基づいています。 https://unix.stackexchange.com/a/271108
答え1
私はpaste
これをまったく使用しません。sed
私はawkまたはperlを使用します。たとえば、
まず、いくつかのサンプル入力ファイルです。 (わかりやすくするために)File[ABC]
ファイルAとBは、検索パターンとその代替項目の意味を変更したことに注意してください。 FileC は変更する入力テキストファイルです。
重要なのは、クエリを含むファイルがスクリプトの最初の引数で、代替文字列を含むファイルが2番目の引数であることです。変更される実際の入力は、3番目(存在する場合は後続)の引数および/または標準入力から来ます。
$ cat FileA
house
$ cat FileB
dwelling
$ cat FileC
Mr House does not live in a land-based house, his house is a houseboat.
Perlスクリプトもあります。別の名前で保存しreplace.pl
て実行可能にしますchmod +x replace.pl
。
$ cat replace.pl
#!/usr/bin/perl
use strict;
# Variables to hold the first two filenames.
my $FileA = shift;
my $FileB = shift;
# An associative array ("hash") called %RE. The keys are the search
# regexes and the values are the replacements.
my %RE;
# Read both FileA and FileB at the same time, to build a
# hash of pre-compiled regular expressions (%RE) and their
# replacements.
open(my $A,'<',$FileA) || die "Couldn't open $FileA for read: $!\n";
open(my $B,'<',$FileB) || die "Couldn't open $FileB for read: $!\n";
while(my $a = <$A>) { # loop reading lines from first file
die "$FileA is longer than $FileB" if (eof $B);
my $b = <$B>; # read in a line from 2nd file
die "$FileB is longer than $FileA" if (eof $A && ! eof $B);
chomp($a,$b);
# Uncomment only ONE of the following four lines:
$RE{qr/\b$a\b/} = $b; # regular expression match
#$RE{qr/\b\Q$a\E\b/} = $b; # exact-match version.
#$RE{qr/(?<!-)\b$a\b(?!-)/} = $b; # regexp match, no hyphen allowed
#$RE{qr/(?<!-)\b\Q$a\E\b(?!-)/} = $b; # exact match, no hyphen allowed.
}
close($A);
close($B);
# process stdin and/or any remaining filename argument(s) on
# the command line (e.g. FileC).
while (<>) {
foreach my $a (keys %RE) {
s/$a/$RE{$a}/g;
};
print;
}
メモ:
Perlの
chomp
機能は、変数または変数のリストから末尾の入力レコード区切り文字($/
-テキストファイルの種類とオペレーティングシステムに応じて改行やCR + LFなどの行末文字)を削除します。望むよりperldoc -f chomp
。Perlの
qr
参照演算子はコンパイルされた正規表現を返します。perldoc -f qr
詳細より。検索、置換、およびテキストファイルがすべて小さい場合、プリコンパイルされた正規表現には違いはありません。検索と置換リスト(ファイルAとB)が長い場合、または入力(ファイルC)が大きい場合、パフォーマンスに大きな違いがあります。正規表現を繰り返しコンパイルするオーバーヘッドにより、CPU処理能力と時間が大幅に消費されます。
正規表現は FileA でコンパイルされるため、
\b$a\b
FileA の値の周りに幅 0 の単語境界表示が含まれます。表示man perlre
と検索word boundary
「幅なし」とは、\b
実際には、入力テキストを一致させたり使用したりせずにそこに表示されると予想されるものだけを主張することを意味します。幅0のアサーションの他の例には、^
(Row Anchor Start)および$
(Row Anchor End)があります。Assertions
同じマニュアルページ内で検索してください。FileAのパターンを固定文字列として処理するには(つまり、すべての正規表現メタ文字が特別な意味を持たないリテラル文字列
*
として扱われます)、パターンを囲むことでメタ文字を無効にする(引用符)。重要なことは何か?
\Q
\E
\b
外部のと。\Q
\E
コメント付きの例を追加しました。これも文書化されていますman perlre
。FileAのパターンが
\
エスケープされていない文字で終わると、スクリプトは中断されます。また、\E
固定文字列バージョンを使用すると、埋め込みパターンによってバージョンが破損する可能性があります。また、\Q
固定されていない文字列バージョンでも問題が発生する可能性があります。ゴミは入り、ゴミは出てきます。入力内容を整理してください。また
man perlre
:Perlは文字(\w
)という単語を次のように定義します。英数字と「_」、その他の接続句読点文字とUnicodeタグハイフンと他のほとんどの句読点文字は単語を終了します。
houseboat
FileCの内容は同じままですが、に変更さhouse-boat
れ、dwelling-boat
これは理想的ではありません。share-house
share-dwelling
この問題は、REのハイフン文字(または)の幅がゼロの否定的な予測と振り返るアサーション(それぞれおよび)を使用するようにスクリプトを変更することで
(?!pattern)
解決できます。簡単に言えば、これはPerlの正規表現エンジンに「私たちが探しているパターンがその前後に存在する場合は一致しません」と伝えます。(?<!pattern)
$RE{qr/(?<!-)\b$a\b(?!-)/} = $b;
$RE{qr/(?<!-)\b\Q$a\E\b(?!-)/} = $b;
-
REが次の文字を捕まえるのを防ぐために、ここでは幅0のアサーションを使用することが重要です(そのような否定的な文字クラスだけでなく
[^-]
)(同じ理由で幅0のアサーションは\b
実際に入力を一致または消費しません。 )。今回もログインして検索man perlre
をしてみてくださいLookaround Assertions
。この例もスクリプトに追加しました。
修飾子は使用されないため、
/i
正規表現の一致では大文字と小文字が区別されます。スクリプトには非常に原始的なパラメータ処理機能があります。より良いものが必要な場合は、Perlの多くのコマンドライン引数/オプション処理モジュールの1つを使用してください。GetSelect::標準またはGetopt::長い。これはコア Perl モジュールであり、Perl に含まれています。
最後に、いくつかのサンプル出力は次のようになります。
$ ./replace.pl FileA FileB FileC
Mr House does not live in a land-based dwelling, his dwelling is a houseboat.
スクリプトが実際に各個々の入力ファイルを変更するようにするには(単に標準出力として印刷するのではなく)、最初の行を次のように変更します。
#!/usr/bin/perl
到着
#!/usr/bin/perl -i
または(元のファイルを.bakとして保存したい場合):
#!/usr/bin/perl -i.bak
ただし、-i
内部編集オプションを使用しても、入力がファイルの代わりに標準入力から来る場合、スクリプトは引き続き機能します。