Metin dosyasını sabit sayıda kelime içeren satırlara bölme


11

İlgili, ancak tatmin edici bir cevap yok: Büyük bir metin dosyasını 500 kelimelik parçalara nasıl bölebilirim?

Ben bir satırda> 10 ^ 7 kelime ile bir metin dosyası ( http://mattmahoney.net/dc/text8.zip ) almak ve her N kelime ile satırlara bölmek çalışıyorum. Mevcut yaklaşımım işe yarıyor, ancak oldukça yavaş ve çirkin (kabuk komut dosyası kullanarak):

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

Bunu nasıl daha hızlı veya daha kompakt hale getirebileceğime dair bir ipucu var mı?


daha hızlı istiyorsanız, bash betiğinden sonra başka bir şey kullanmanız gerekir. Biraz tavsiye ederim. Birkaç satıra sığabilir.
Jakuje

Yanıtlar:


5

Kelime tanımınızın boşluklarla ayrılmış bir dizi boş olmayan karakter olduğunu varsayarsak, awktek satırlı dosyanız için bir çözüm

awk '{for (i=1; i<=NF; ++i)printf "%s%s", $i, i % 500? " ": "\n"}i % 500{print ""}' file

11

Kullan xargs(17 saniye):

xargs -n1000 <file >output

Maksimum argüman sayısını tanımlayan -nbayrağını kullanır xargs. Sadece istediğiniz veya istediğiniz limite 1000geçin 500.

10 ^ 7 kelimelik bir test dosyası yaptım:

$ wc -w file
10000000 file

Zaman istatistikleri:

$ time xargs -n1000 <file >output
real    0m16.677s
user    0m1.084s
sys     0m0.744s

Bu kabul ettiğim cevaptan biraz daha yavaş (21s vs 12s)
Cory Schillaci

1
Mükemmel bir fikir 1 Ancak dikkat xargss' alıntı çıkma davranışı
Iruvar

Ne kadar düşük olursa n, o kadar yavaş olur, sadece bilirsiniz. İle -n10yaklaşık 8 dakika bekledikten sonra iptal ettim ...
don_crissti

7

Perl bu konuda oldukça şaşırtıcı görünüyor:

10.000.000 boşlukla ayrılmış sözcük içeren bir dosya oluşturun

for ((i=1; i<=10000000; i++)); do printf "%s " $RANDOM ; done > one.line

Şimdi, her 1000 kelimeden sonra yeni satır eklemek için perl

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

Zamanlama

real    0m1.074s
user    0m0.996s
sys     0m0.076s

sonuçları doğrula

$ wc one.line many.line
        0  10000000  56608931 one.line
    10000  10000000  56608931 many.line
    10000  20000000 113217862 total

Kabul edilen awk çözümü girdi dosyamda 5 saniyenin biraz üzerinde sürdü.


5

NKelimelerin umber'i büyük bir sayı olduğunda gerçekten uygun değildir , ancak küçük bir sayıysa (ve ideal olarak, tek satırlı dosyanızda önde gelen / sondaki boşluk yoksa) bu oldukça hızlı olmalıdır (örneğin, satır başına 5 kelime):

tr -s '[[:blank:]]' '\n' <input.txt | paste -d' ' - - - - - >output.txt

1
Bu, büyük sayılarla da gayet iyi ve körü körüne hızlı. Dizeyi pasteanında oluşturun. Örneğin:tr -s '[[:blank:]]' '\n' < text8 | paste -d' ' $(perl -le 'print "- " x 1000')
terdon

@terdon - Gerçek, büyük sayılar için bir sen yaptın ya aracılığıyla olarak komut argümanlar örneğin kurmak zorunda olsa setvb ... ve o zaman bile, bağımsız değişkenler Sytem belirli maksimum sayı Ben tüm lezzetleri ile aşina değilim (var pasteama Bazı uygulamalar ile
argüman

3

Aynı sed komutu, kaç kelime-boşluk kalıbı eşleştirmek istediğinizi belirterek basitleştirilebilir. Test etmek için herhangi bir büyük dize dosyası yoktu, ancak orijinal komut dosyanızdaki döngüler olmadan, işlemcinizin verileri aktarabildiği kadar hızlı çalışmalıdır. Ek avantaj, çok satırlı dosyalar üzerinde eşit derecede iyi çalışır.

n=500; sed -r "s/((\w+\s){$n})/\1\n/g" <input.txt >output.txt

3

Saygıdeğer fmt(1)komut, "belirli sayıda kelime" üzerinde kesin olarak işlem görmemekle birlikte, uzun satırları belirli bir hedef (veya maksimum) genişliğe oldukça hızlı bir şekilde sarabilir:

perl -e 'for (1..100) { print "a"x int 3+rand(7), " " }' | fmt

Ya da modern perl ile, örneğin 10, belirli sayıda kelime için ve kelime sınırı olarak tek bir boşluk varsayarak:

... | perl -ple 's/(.*? ){10}\K/\n/g'

2

Coreutils prkomutu başka bir adaydır: tek kırışıklık, sayfa genişliğini çıktı genişliğini alacak kadar büyük olmaya zorlamak gibi görünmektedir.

@ Glenn_Jackman'ın 10.000.000 kelime üreteci kullanılarak oluşturulan bir dosyayı kullanarak,

$ time tr '[[:blank:]]' '\n' < one.line | pr -s' ' -W 1000000 -JaT -1000 > many.line

real    0m2.113s
user    0m2.086s
sys 0m0.411s

sayıların aşağıdaki gibi onaylandığı

$ wc one.line multi.line 
        0  10000000  56608795 one.line
    10000  10000000  56608795 many.line
    10000  20000000 113217590 total

[Glenn'in perl çözümü hala biraz daha hızlı, bu makinede ~ 1.8s].


1

Go'da böyle denerdim

//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])
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.