
string.txtとlengths.txtという2つのテキストファイルがあります。
文字列.txt:
abcdefghijklmnopqrstuvwxyz
長さ.txt
5
4
10
7
ファイルを受け取りたい
>Entry_1
abcde
>Entry_2
fghi
>Entry_3
jklmnopqrs
>Entry_4
tuvwxyz
私は200文字から56,000文字の長さの約28,000項目を作業しています。
現在私は以下を使用しています:
start=1
end=0
i=0
while read read_l
do
let i=i+1
let end=end+read_l
echo -e ">Entry_$i" >>outfile.txt
echo "$(cut -c$start-$end String.txt)" >>outfile.txt
let start=start+read_l
echo $i
done <lengths.txt
しかし、これは非常に非効率的です。より良いアイデアがありますか?
答え1
一般的に言えば、テキストを処理するためにシェルループを使用したくありません。。ここでは以下を使用しますperl
。
$ perl -lpe 'read STDIN,$_,$_; print ">Entry_" . ++$n' lengths.txt < string.txt
>Entry_1
abcde
>Entry_2
fghi
>Entry_3
jklmnopqrs
>Entry_4
tuvwxyz
それは一つ(バッファリングを使用してread
一度に1バイト(または通常のファイルの場合は複数バイト)を読み取るシェルコマンドよりも効率的です)両方のファイルを一度だけ読み取る(メモリに完全に保存せずに)コマンドは次のとおりです。シェルループで外部コマンドを実行するソリューションよりもはるかに効率的です。
(-C
数値がバイトではなく現在のロケールの文字である必要がある場合は、このオプションを追加してください。例のASCII文字には違いはありません。)
答え2
あなたはできます
{
while read l<&3; do
{
head -c"$l"
echo
} 3<&-
done 3<lengths.txt
} <String.txt
説明が必要です。
主なアイデアは、{ head ; } <file
過小評価された@mikeservから使用および派生しました。回答。ただし、この場合は多くhead
を使用する必要があるため、while
ループが導入され、ファイルディスクリプタがわずかに調整され、head
2つのファイルへの入力が渡されます(ファイルはString.txt
処理する基本ファイル、行はlength.txt
引数として渡されます)。オプション-c
)。アイデアは、またはString.txt
同じコマンドが呼び出されるたびに検索する必要がないため、速度の利点が必要です。各繰り返し後に改行文字を印刷します。head
cut
echo
どれだけ速いか(もしあれば)>Entry_i
練習で行の間に内容を追加することです。
答え3
バッシュ、バージョン4
mapfile -t lengths <lengths.txt
string=$(< String.txt)
i=0
n=0
for len in "${lengths[@]}"; do
echo ">Entry_$((++n))"
echo "${string:i:len}"
((i+=len))
done
出力
>Entry_1
abcde
>Entry_2
fghi
>Entry_3
jklmnopqrs
>Entry_4
tuvwxyz
答え4
何についてawk
?
次のコードを使用してというファイルを作成しますprocess.awk
。
function idx(i1, v1, i2, v2)
{
# numerical index comparison, ascending order
return (i1 - i2)
}
FNR==NR { a[FNR]=$0; next }
{ i=1;PROCINFO["sorted_in"] = "idx";
for (j in a) {
print ">Entry"j;
ms=substr($0, i,a[j])
print ms
i=i+length(ms)
}
}
保存と実行awk -f process.awk lengths.txt string.txt