関連していますが、満足のいく答えはありません: 大きなテキストファイルを500ワード程度のチャンクに分割するにはどうすればよいですか?
私はテキストファイル( http://mattmahoney.net/dc/text8.Zip )を1行に> 10 ^ 7ワードすべて取り、Nワードの行に分割しようとしています各。私の現在のアプローチは機能しますが、かなり遅くて醜いです(シェルスクリプトを使用)。
i=0
for Word in $(sed -e 's/\s\+/\n/g' input.txt)
do
echo -n "${Word} " > output.txt
let "i=i+1"
if [ "$i" -eq "1000" ]
then
echo > output.txt
let "i=0"
fi
done
これをより速く、またはよりコンパクトにするためのヒントはありますか?
Wordの定義が空白で区切られた空白以外の文字のシーケンスであると仮定すると、単一行ファイルのawk
ソリューションがあります
awk '{for (i=1; i<=NF; ++i)printf "%s%s", $i, i % 500? " ": "\n"}i % 500{print ""}' file
xargs
を使用(17秒):
xargs -n1000 <file >output
-n
フラグの最大数を定義するxargs
のフラグ。変更するだけ1000
から500
または必要な制限。
10 ^ 7語のテストファイルを作成しました。
$ wc -w file
10000000 file
時間の統計は次のとおりです。
$ time xargs -n1000 <file >output
real 0m16.677s
user 0m1.084s
sys 0m0.744s
Perlはこれで驚くほど良いようです:
スペースで区切られた10,000,000語のファイルを作成する
for ((i=1; i<=10000000; i++)); do printf "%s " $RANDOM ; done > one.line
さて、Perlは1,000ワードごとに改行を追加します
time Perl -pe '
s{
(?:\S+\s+){999} \S+ # 1000 words
\K # then reset start of match
\s+ # and the next bit of whitespace
}
{\n}gx # replace whitespace with newline
' one.line > many.line
タイミング
real 0m1.074s
user 0m0.996s
sys 0m0.076s
結果を確認する
$ wc one.line many.line
0 10000000 56608931 one.line
10000 10000000 56608931 many.line
10000 20000000 113217862 total
受け入れられたawkソリューションは、私の入力ファイルでわずか5秒以上かかりました。
単語のN
umberが大きい場合はあまり適していませんが、小さい場合(理想的には、1行のファイルに先頭/末尾のスペースがない場合)、これは非常に高速です(例:1行あたり5単語) :
tr -s '[[:blank:]]' '\n' <input.txt | paste -d' ' - - - - - >output.txt
由緒あるfmt(1)
コマンドは、「特定の数の単語」を厳密に操作していなくても、長い行を特定の目標(または最大)幅にかなりすばやく折り返すことができます。
Perl -e 'for (1..100) { print "a"x int 3+Rand(7), " " }' | fmt
または、最新のPerlでは、特定の単語数、たとえば10に対して、単語の境界として単一のスペースを想定します。
... | Perl -ple 's/(.*? ){10}\K/\n/g'
一致させるワードスペースパターンの数を指定することにより、同じsedコマンドを簡略化できます。テストする大きな文字列ファイルはありませんでしたが、元のスクリプトにループがなければ、プロセッサがデータをストリーミングできるのと同じ速さで実行できます。利点が追加されました。複数行のファイルでも同様に機能します。
n=500; sed -r "s/((\w+\s){$n})/\1\n/g" <input.txt >output.txt
Coreutils pr
コマンドも候補の1つです。唯一の問題は、ページ幅を出力幅に対応するのに十分な大きさにする必要があるということです。
@Glenn_Jackmanの10,000,000 Wordジェネレータを使用して作成されたファイルを使用して、
$ time tr '[[:blank:]]' '\n' < one.line | pr -s' ' -W 1000000 -JaT -1000 > many.line
real 0m2.113s
user 0m2.086s
sys 0m0.411s
カウントは次のように確認されます
$ wc one.line multi.line
0 10000000 56608795 one.line
10000 10000000 56608795 many.line
10000 20000000 113217590 total
[GlennのPerlソリューションはまだ少し高速で、このマシンでは約1.8秒]。
囲碁ではこんな風にやってみます
//wordsplit.go
//$ go run wordsplit.go bigtext.txt
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"strings"
)
func main() {
myfile, err := os.Open(os.Args[0])
if err != nil {
log.Fatal(err)
}
defer myfile.Close()
data, err := ioutil.ReadAll()
if err != nil {
log.Fatal(err)
}
words := strings.Split(data, " ")
newfile, err := os.Create("output.txt")
if err != nil {
log.Fatal(err)
}
defer newfile.Close()
for i := 0; i < len(words)-10; i+10 {
newfile.WriteString(words[i:i+10])
}
newfile.WriteString(words[-(len(words)%10):])
fmt.Printf("Formatted %s into 10 Word lines in output.txt", os.Args[0])
}