2016-05-03 2 views
1

私は1,700,000語を含むファイルを持っています。私は単語の素朴なステミングをしたい、単語の長さが6文字以上の場合、私は6番目の位置の後にすべての文字を削除します。たとえば:bashを使ってすべての単語のn番目の位置からすべての文字を削除するには?

Input: 
Everybody is around 
Everyone keeps talking 

Output: 
Everyb is around 
Everyo keeps talkin 

私は次のスクリプトを書きました:

INPUT=train.txt 
while read line; do 
for word in $line; do 
new="$(echo $word | awk '{print substr($0,1,6);exit}')" 
echo -n $new >> train_stem_6.txt 
echo -n ' ' >> train_stem_6.txt 
done 
echo ' ' >> train_stem_6.txt 
done < "$INPUT" 

これは完全に質問に答えるが、それは非常に遅いです、と私は170万言葉を持っているので、それは永遠にかかります。 bashスクリプトを使ってこれを行うより速い方法がありますか?

ありがとう、

+0

が、追加のプロセスが開始されたためにプロセスが遅くなります。 'bash'を使った部分文字列の選択を検討してください:http://tldp.org/LDP/abs/html/string-manipulation.html – Bernhard

+0

ハイフネーションされた単語はどうですか?句読点はありますか? – 123

+0

あなたのアルゴリズムによって引き起こされるフォークの数が多いため、コードが遅かったと思います。 'awk'はファイルを直接読み取ることができるので、1つのawkコマンドだけを使ってリファクタリングすることができます。 – Aif

答えて

3

sedの使用を検討しますか?

sed -r 's/([a-zA-Z]{6})[a-zA-Z]+/\1/g' 
+2

は '{} 'の中でただ一つの6を必要とします – 123

+1

あなたの正規表現は単語がASCII-7文字のみであるとみなします。 OPは単語が空白で区切られているとみなします。 – DevSolar

+0

これはどれくらい早くなりますか? – Bernhard

3

あなたは、このためのawkを使用することができます。

awk '{for(i=1;i<=NF;i++){$i=substr($i, 1, 6)}}1' train.txt 

は内訳:

{       
    for(i=1;i<=NF;i++) {  # Iterate over each word 
    $i = substr($i, 1, 6); # Shrink it to a maximum of 6 characters 
    }       
}       
1       # Print the row 

しかしこれは単語としてAwesome,を扱うので、あなたはこれを使用することができますe,

+0

'[[:space:]]'ではないすべての文字を数えるので、すべての句読点を数えます。 – 123

+0

@ 123私はすでに回答でそれを明確にした – andlrc

+0

私がコメントしていた前ではありません。 – 123

4

を削除しますカスタムを使用するgnu awk RS

awk -v RS='[[:space:]]' '{ORS=RT; print substr($0, 1, 6)}' file 

Everyb is around 
Everyo keeps talkin 

11メガバイトの入力ファイルの3つのコマンドの

タイミング:

のsed:

time sed -r 's/([a-zA-Z]{6})[a-zA-Z]+/\1/g' file >/dev/null 

real 0m2.913s 
user 0m2.878s 
sys  0m0.020s 

AWKコマンド@andlrcによって:

time awk '{for(i=1;i<=NF;i++){$i=substr($i, 1, 6)}}1' file >/dev/null 

real 0m1.191s 
user 0m1.174s 
sys  0m0.011s 

私はawkコマンドを提案:

time awk -v RS='[[:space:]]' '{ORS=RT; print substr($0, 1, 6)}' file >/dev/null 

real 0m1.926s 
user 0m1.905s 
sys  0m0.013s 

だから、両方のawkコマンドは、ジョブを完了するためにほとんど同じ時間を取って、sedをしている大きなファイルに遅くなる傾向があります。 167メガバイトのファイルに


3コマンド

$ time awk -v RS='[[:space:]]+' 'RT{ORS=RT} {$1=substr($1, 1, 6)} 1' test > /dev/null 

real 0m29.070s 
user 0m28.898s 
sys  0m0.060s 
$ time awk '{for(i=1;i<=NF;i++){$i=substr($i, 1, 6)}}1' test >/dev/null 

real 0m13.897s 
user 0m13.805s 
sys  0m0.036s 

$ time sed -r 's/([a-zA-Z]{6})[a-zA-Z]+/\1/g' test > /dev/null 

real 0m40.525s 
user 0m40.323s 
sys  0m0.064s 
+0

これは、デフォルトのRSを使用しないため、他のほとんどのawkソリューションよりも遅くなります。 – 123

+0

どのようにデフォルトの 'RS'を使って、それを遅くするのですか? – anubhava

+1

デフォルトのRSとFSに最適化されています。他のawkの答えとこれを大きなファイルで試して、完了までの時間差を確認してください。 – 123

0

ピュアbashの、(すなわちないPOSIX)、ワンライナーとして:

while read x ; do set -- $x ; for f in $* ; do echo -n ${f:0:6}" " ; done ; echo ; done < train.txt 

...と同じコード明瞭にするために再フォーマットされた:

while read x ; do 
    set -- $x 
    for f in $* ; do 
     echo -n ${f:0:6}" " 
    done 
    echo 
done < train.txt 

注:繰り返し空白は1つのスペースになります。

試運転、最初に標準入力で、コードの上に使用して機能します

len6() { while read x ; do set -- $x ; for f in $* ; do echo -n ${f:0:6}" " ; done ; echo ; done ; } 

を呼び出す:

COLUMNS=90 man bash | tail | head -n 5 | len6 

出力:私は今テストすることはできません

gracef when proces suspen is attemp When a proces is stoppe the 
shell immedi execut the next comman in the sequen It suffic to 
place the sequen of comman betwee parent to force it into a subshe 
which may be stoppe as a unit. 
+0

ゆっくりと進みます。連結された_man bash_の167MBを突き破るために約8分かかります。 – agc

+0

POSIXバージョン(_bash_ismsなし): 'len6(){読み取りx;設定する - $ x; $ *の中のfのために; do n = $ {f%$ {f#??????}};エコー-n "$ {n: - $ {f}}";完了しました。エコー ;完了しました。 } '。 _dash_で実行すると、同じ大きなファイルを悩ますのに約2mかかります。 – agc

関連する問題