แยกไฟล์ข้อความเป็นบรรทัดที่มีจำนวนคำคงที่


11

ที่เกี่ยวข้อง แต่ไม่มีคำตอบที่น่าพอใจ: ฉันจะแยกไฟล์ข้อความขนาดใหญ่เป็นชิ้น ๆ จำนวน 500 คำได้อย่างไร

ฉันพยายามที่จะใช้ไฟล์ข้อความ ( http://mattmahoney.net/dc/text8.zip ) ด้วย> 10 ^ 7 คำทั้งหมดในบรรทัดเดียวและแยกเป็นบรรทัดด้วยคำที่ไม่มีคำแต่ละคำ วิธีการปัจจุบันของฉันใช้งานได้ แต่ค่อนข้างช้าและน่าเกลียด (ใช้เชลล์สคริปต์):

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

มีคำแนะนำใด ๆ เกี่ยวกับวิธีที่ฉันจะทำให้ขนาดเล็กลงหรือเร็วขึ้นได้อย่างไร


ถ้าคุณต้องการมันเร็วขึ้นคุณต้องใช้อย่างอื่นแล้วทุบตีสคริปต์ ฉันอยากจะแนะนำบาง 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

นี่คือช้ากว่าคำตอบที่ฉันได้รับการยอมรับ (21s 12s เทียบกับไฟล์ของฉัน) เล็กน้อย
คอรี Schillaci

1
ความคิดที่ดี +1 แต่ระวังxargs's อ้างปอกพฤติกรรม
Iruvar

ยิ่งน้อยnเท่าไหร่ก็ยิ่งช้าลงเท่านี้คุณก็รู้ ด้วย-n10ฉันยกเลิกมันหลังจากประมาณ 8 นาทีของการรอ ...
don_crissti

7

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 วินาทีในไฟล์อินพุตของฉัน


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แต่ ฉันคิดว่าการติดตั้งใช้งานบางอย่างนั้นมีข้อ จำกัด เกี่ยวกับจำนวนไฟล์ args / input และ / หรือความยาวบรรทัดเอาท์พุท ... )
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

prคำสั่งcoreutils เป็นตัวเลือกอื่น: รอยย่นเดียวที่ดูเหมือนว่าจำเป็นต้องบีบให้ความกว้างของหน้ากระดาษใหญ่พอที่จะรองรับความกว้างของเอาต์พุต

การใช้ไฟล์ที่สร้างโดยใช้โปรแกรมสร้างคำ 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

[โซลูชัน perl ของ 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])
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.