テキストファイルから文字列を分割する高速な方法?


11

string.txtとlengths.txtの2つのテキストファイルがあります。

String.txt:

abcdefghijklmnopqrstuvwxyz

lengths.txt

5
4
10
7

ファイルを入手したい

>Entry_1
abcde
>Entry_2
fghi
>Entry_3
jklmnopqrs
>Entry_4
tuvwxyz

私は約28,000のエントリで作業しており、それらは200から56,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

しかし、それは非常に非効率的です。より良いアイデアはありますか?


str="$(cat string.txt)"; i=0; while read j; do echo "${file:$i:$j}"; i=$((i+j)); done <length.txtシェルだけで行うのと同じくらい高速に見えますか?
heemayl

正直に言うと、それほど速くはありません。まだかなり時間がかかります。私はlinux /プログラミングに非常に慣れていないので、シェルを使用するだけでなく、より高速な方法があると思うなら、私はアイデアを受け入れます。
user3891532

4
試してください{ while read l<&3; do head -c"$l"; echo; done 3<lengths.txt; } <String.txt
jimmij

@jimmij、それを答えに入れてはいかがですか
iruvar '12

回答:


7

できるよ

{
  while read l<&3; do
    {
      head -c"$l"
      echo
    } 3<&-
  done 3<lengths.txt
} <String.txt

説明が必要です:

主なアイデアは使用することで{ head ; } <fileあり、過小評価されている@mikeservの回答から導き出されます。ただし、この場合は多数headのを使用する必要whileがあるためhead、両方のファイルから入力に渡すためにループを導入し、ファイル記述子を少し調整します(String.txt処理するメインファイルとしてのファイルlength.txt-cオプションの引数としての行) 。アイデアは、速度の利点は、またはのString.txtようなコマンドが呼び出されるたびにシークスルーする必要がないことからもたらされるべきであるということです。これは、各反復の後に改行を印刷するだけです。headcutecho

それがどれだけ速いか(もしあれば)、>Entry_i行間に追加することは演習として残しておきます。


I / Oリダイレクトの適切な使用。タグは、Linuxであるので、あなたは合理的にシェルがbashのと使用することであると仮定することができread -u 3、ディスクリプタ3から読み出すために
ジョナサン・レフラー

@ JonathanLeffler、Linuxはほとんど関係ありませんbash。Linuxベースのシステムの大多数はbashインストールされていません(Androidやその他の組み込みシステムを考えてください)。bashすべての中で最も遅いシェルであるため、bashに切り替えると、からread <&3に切り替えることで得られる小さな利益よりもパフォーマンスが大幅に低下する可能性がありますread -u3(いずれの場合も、のような外部コマンドを実行するコストと比較すると重要ではありませんhead)。head組み込みの(そして非標準-cオプションをサポートする)ksh93に切り替えると、パフォーマンスが大幅に向上します。
ステファンChazelas

の引数head -chead非標準オプションが利用可能な実装の場合)は文字数ではなくバイト数であることに注意してください。これにより、マルチバイトロケールで違いが生じます。
ステファンChazelas

7

通常、テキストの処理にシェルループを使用することは望ましくありません。ここでは、私は使用しますperl

$ perl -lpe 'read STDIN,$_,$_; print ">Entry_" . ++$n' lengths.txt < string.txt
>Entry_1
abcde
>Entry_2
fghi
>Entry_3
jklmnopqrs
>Entry_4
tuvwxyz

これは1つのコマンドで、(バッファリングにより、read一度に1バイト(通常のファイルの場合は数バイト)を読み取るシェルのコマンドよりもはるかに効率的に)両方のファイルを1回だけ(メモリに完全に保存せずに)読み取ります。シェルループで外部コマンドを実行するソリューションよりも数桁効率がよくなります。

-Cこれらの数値がバイト数ではなく現在のロケールの文字数である必要がある場合はオプションを追加します。サンプルのようなASCII文字の場合、違いはありません)。


これは、への$_出力パラメーターと入力パラメーターの両方としてを複雑に再利用したものですreadが、スクリプトのバイト数が減ります。
Jonathan Leffler、2015

簡単なテスト(OPのサンプルが100000回繰り返された)では、このソリューションは@jimmijの約1200倍の速さであることがわかります(0.3秒vs 6分(でbash、16秒でPATH=/opt/ast/bin:$PATH ksh93))。
ステファンChazelas

6

bash、バージョン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


の使用に基づくとPROCINFO、これは標準awkではありませんが、gawkです。その場合、私は別のgawk唯一の機能であるFIELDWIDTHSawk -vFIELDWIDTHS="$(tr '\n' ' ' < lengths.txt)" '{for(i=1;i<=NF;i++)print">Entry"i ORS$i}' string.txt
manatworkを
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.