Розділіть текстовий файл на рядки з фіксованою кількістю слів


11

Відповідні, але не задовільні відповіді: Як я можу розділити великий текстовий файл на шматки з 500 слів?

Я намагаюся взяти текстовий файл ( http://mattmahoney.net/dc/text8.zip ) з> 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

Будь-які поради, як зробити це швидше чи компактніше?


якщо ви хочете швидше, вам потрібно скористатися чимось іншим, а потім bash script. Я порекомендував би трохи C. Він може вміститись у кілька рядків.
Jakuje

Відповіді:


5

Припустимо, що ваше визначення слова - це послідовність непорожніх символів, розділених пробілами, ось awkрішення для вашого однорядкового файлу

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

11

Використання 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

Це трохи повільніше, ніж відповідь, яку я прийняв (21-го проти 12-го в моєму файлі)
Cory Schillaci

1
Відмінна ідея +1, однак остерігайтеся xargs«s цитата зачистки поведінку
Iruvar

Чим нижче, тим nповільніше це вийде, просто так ви знаєте. Коли -n10я скасував це приблизно через 8 хвилин очікування ...
don_crissti

7

Perl здається досить дивним у цьому:

Створіть файл з 10000000000 пробілів

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

Тепер заздалегідь додайте новий рядок після кожні 1000 слів

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 секунд на моєму вхідному файлі.


5

Не дуже підходить, коли Nумб слів є великою кількістю, але якщо це невелика кількість (і в ідеалі, у вашому однорядковому файлі немає провідних / кінцевих пробілів), це має бути досить швидким (наприклад, 5 слів на рядок):

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

1
Це ідеально добре і з великою кількістю, і сліпуче швидко. Просто генеруйте pasteрядок на льоту. Наприклад:tr -s '[[:blank:]]' '\n' < text8 | paste -d' ' $(perl -le 'print "- " x 1000')
terdon

@terdon - правда, хоча для великої кількості потрібно нарощувати аргументи команд, наприклад, як ви це робили або через setтощо ... і навіть тоді є специфічна максимальна кількість аргументів для системи (я не знайомий з усіма ароматами, pasteале Я думаю, що з деякими реалізаціями є обмеження щодо кількості аргументів / файлів вводу та / або довжини рядка виходу ...)
don_crissti

3

Цю ж команду sed можна спростити, вказавши, скільки шаблонів слово-простору потрібно відповідати. У мене не було жодних великих рядкових файлів, щоб перевірити це, але без циклів у вашому оригінальному сценарії це повинно працювати так швидко, як ваш процесор може передавати дані. Додаткова вигода, вона буде однаково добре працювати з багаторядковими файлами.

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

3

Поважна fmt(1)команда, не строго оперуючи "певною кількістю слів", може досить швидко перегорнути довгі рядки до певної цілі (або максимальної) ширини:

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

Або з сучасним perl, для певної кількості слів, скажімо, 10, і припускаючи один пробіл як межу слова:

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

2

Команда coreutils prє ще одним кандидатом: єдиною зморшкою здається, що потрібно змусити ширину сторінки бути достатньо великою для розміщення вихідної ширини.

Використовуючи файл, створений за допомогою генератора слів на 10 000 000 @ @ 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

де підрахунки підтверджуються наступним чином

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

[Рішення Perlen Glenn все ще трохи швидше, ~ 1,8 секунди на цій машині].


1

у Go я б спробував це так

//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])
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.