Dividi il file di testo in righe con un numero fisso di parole


11

Risposte correlate, ma non soddisfacenti: come posso dividere un grande file di testo in blocchi di circa 500 parole?

Sto cercando di prendere un file di testo ( http://mattmahoney.net/dc/text8.zip ) con> 10 ^ 7 parole tutte in una riga e dividerlo in righe con N parole ciascuna. Il mio approccio attuale funziona, ma è piuttosto lento e brutto (usando lo script della shell):

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

Qualche consiglio su come posso renderlo più veloce o più compatto?


se lo vuoi più velocemente, devi usare qualcos'altro, quindi bash script. Consiglierei un po 'di C. Può adattarsi a poche righe.
Jakuje,

Risposte:


5

Supponendo che la tua definizione di parola sia una sequenza di caratteri non vuoti separati da spazi, ecco una awksoluzione per il tuo file a riga singola

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

11

Usa xargs(17 secondi):

xargs -n1000 <file >output

Utilizza il -nflag di xargscui definisce il numero massimo di argomenti. Basta cambiare 1000per 500o qualsiasi limite che si desidera.

Ho creato un file di prova con 10 ^ 7 parole:

$ wc -w file
10000000 file

Ecco le statistiche del tempo:

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

Questo è leggermente più lento della risposta che ho accettato (21s contro 12s nel mio file)
Cory Schillaci

1
Ottima idea +1, però state attenti xargss' quote-strippaggio comportamento
Iruvar

Più basso è il npiù lento questo diventerà, solo per questo lo sai. Con l' -n10ho annullato dopo circa 8 minuti di attesa ...
don_crissti

7

Perl sembra sorprendentemente bravo in questo:

Crea un file con 10.000.000 di parole separate da spazio

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

Ora, perl per aggiungere una nuova riga dopo ogni 1.000 parole

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

sincronizzazione

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

verificare i risultati

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

La soluzione awk accettata ha impiegato poco più di 5 secondi sul mio file di input.


5

Non proprio adatto quando il numero Ndi parole è un numero elevato, ma se è un numero piccolo (e idealmente, nessuno spazio iniziale / finale nel file di una riga) dovrebbe essere abbastanza veloce (ad esempio 5 parole per riga):

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

1
Questo va benissimo anche con grandi numeri e accecantemente veloce. Basta generare la pastestringa al volo. Ad esempio:tr -s '[[:blank:]]' '\n' < text8 | paste -d' ' $(perl -le 'print "- " x 1000')
terdon

@terdon - vero, anche se per grandi numeri si devono costruire gli argomenti del comando, ad es. come si è fatto o via setecc ... e anche allora, c'è un numero massimo specifico di argomenti di sistema (non ho familiarità con tutti i gusti di pastema Penso che con alcune implementazioni ci siano limiti al numero di args / file di input e / o lunghezza della linea di output ...)
don_crissti

3

Lo stesso comando sed può essere semplificato specificando quanti schemi di spazio di parole si desidera abbinare. Non avevo grandi file di stringhe su cui provarlo, ma senza i loop nello script originale questo dovrebbe funzionare più velocemente che il tuo processore può trasmettere i dati. Aggiunto vantaggio, funzionerà ugualmente bene su file multilinea.

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

3

Il venerabile fmt(1)comando, pur non operando rigorosamente su "un determinato numero di parole", può avvolgere abbastanza rapidamente le linee lunghe per un determinato obiettivo (o larghezza massima):

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

O con il perl moderno, per un numero specifico di parole, diciamo 10, e assumendo un singolo spazio come limite di parole:

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

2

Il prcomando coreutils è un altro candidato: l'unica ruga sembra essere che è necessario forzare la larghezza della pagina in modo che sia sufficientemente grande da contenere la larghezza dell'output.

Utilizzando un file creato utilizzando il generatore di 10.000.000 di parole di @ Glenn_Jackman,

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

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

dove i conteggi sono confermati come segue

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

[La soluzione perl di Glenn è ancora un po 'più veloce, ~ 1.8s su questa macchina].


1

in Go lo proverei così

//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])
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.