約10万のファイルがあり、各ファイルに対して次のことをしたいと思います。
次のASCIIコードを含むファイルの5番目の文字と6番目の文字の間に文字0x1f
列があります。これで、アプリケーションにすべての置換リストを含む特定のファイルを開くようにしたいと思います。後でこの置換ファイルの形式を参照してください。代替エントリがファイルにない場合は、ファイル名をstderrに書き込むことで、後でファイルを手動で変更できます。これで、asciiコードの16〜17番目の文字の間に0x1f
置き換える内容が再び表示されるようになります。今回は、そのフィールドが単に置き換える内容ではなく、置き換える内容を入れたhtml文字列で置き換えることができます。一度または複数回。フィールドの最初の項目だけを変更したいです。
代替ファイルの形式は非常に簡単です。各置換項目は、1行にスペースで区切られています。交換される長さに応じてソートされます。
はい
代替ファイル:
CCCC 3
BCC 233
CCA 331
CCB 332
ACC 133
AA 11
AB 12
BA 21
BB 22
CC 33
A 1
B 2
上記の文字と数字を必ず含める必要はありません。これは単なる例であり、UTF-8を含めることができます。
ファイル:(0x1f文字は次の例では^_として記録されています。)
field1^_field2^_field3^_field4^_field5^_BB^_field7^_hai
this field contains a newline^_some UTF-8オイ^_the next field is empty^_^_
another newline^_field14^_field15^_<b>BB</b>stuff BB^_the previous field contains something to replace^_^_^_more fields...
ファイルは次のようになります。
field1^_field2^_field3^_field4^_field5^_22^_field7^_hai
this field contains a newline^_some UTF-8オイ^_the next field is empty^_^_
another newline^_field14^_field15^_<b>22</b>stuff BB^_the previous field contains something to replace^_^_^_more fields...
私の入力の実際の例をアップロードしました。ここ。このファイルの目的の出力は次のとおりです。ここ(RYO
で置き換える必要がありますリョ
)。
少し背景
いくつかの愚か者は、データベースに別々の列を作成するのではなく、代わりに単一の列を生成し、フィールドを0x1f文字で区切ることにしました。彼はまた、私が変更したい情報を2つの異なる分野にコピーすることが可能だと思いました。データベースの情報をprファイルに抽出します。行にはフィールドがある列のみが含まれているため、作業が簡単になりそうですが、SQLiteデータベースに提供できるステートメントを作成できる場合はそれも良いでしょう。
答え1
このPerlスクリプトはそれを行います。あなたの例をPastebinでテストしましたが、期待どおりに動作します。
#!/usr/bin/env perl
use strict;
my %k; ## This hash will store the target/replacement pairs
## Read the list of replacements
open(my $r,"$ARGV[0]")||die "Couldn't open replacements list\n";
while(<$r>){
chomp;
my @F=split(/\s+/);
$k{$F[0]}=$F[1]
}
close($r);
$/=undef;
open(my $fh, "$ARGV[1]")||die "Couldn't open input file\n";
while(<$fh>){
## Read the entire file at once
$/=undef;
my @F=split(/\x1f/);
## If this exists in the replacements list
if (defined($k{$F[5]})) {
## Modify the 17th field. This will only replace the first
## occurence. Use 's///g' for all.
$F[16]=~s/$F[5]/$k{$F[5]}/;
## Replace the 6th field
$F[5]=$k{$F[5]};
}
## If it doesn't
else {
## Print the file name to STDERR unless the 5th field
## was empty.
print STDERR "Problematic file: $ARGV[1]\n" unless $F[5]=~/^\s*$/;
}
## print each field separated by '0x1f' again.
print join "\x1f",@F;
}
close($fh);
これをfixidiocy.pl
あなたの$HOME
ディレクトリとcd
ターゲットファイルを含むディレクトリに保存してください。各ファイルでそれを実行して、代替ファイルのファイル名とパスを引数として指定します。
for file in *; do
perl ~/fixidiocy.pl /path/to/replacements "$file" > "$file".fixed
done