कैसे कुशलतापूर्वक एक बड़ी पाठ फ़ाइल को विभाजित करने के लिए अलग-अलग मल्टीलाइन रिकॉर्ड को विभाजित किया जाता है?


9

मेरे पास एक बड़ी टेक्स्ट फ़ाइल है (~ 50Gb जब gz'ed)। फ़ाइल में 4*Nलाइनें या Nरिकॉर्ड हैं; हर रिकॉर्ड में 4 लाइनें होती हैं। मैं इस फाइल को ४ छोटी फाइलों में विभाजित करना चाहता हूं, जो प्रत्येक इनपुट फ़ाइल के लगभग २५% आकार की हैं। मैं फ़ाइल को रिकॉर्ड सीमा पर कैसे विभाजित कर सकता हूं?

एक भोली दृष्टिकोण zcat file | wc -lलाइन की गिनती प्राप्त करने के लिए होगा , उस संख्या को 4 से विभाजित करें और फिर उपयोग करें split -l <number> file। हालाँकि, यह दो बार फाइल पर चला जाता है और लाइन-काउंट बेहद धीमा (36mins) है। क्या कोई बेहतर तरीका है?

यह करीब आता है, लेकिन वह नहीं है जिसकी मुझे तलाश है। स्वीकृत उत्तर भी एक पंक्ति की गणना करता है।

संपादित करें:

फ़ाइल में फास्टैक प्रारूप में अनुक्रमण डेटा है। दो रिकॉर्ड इस तरह दिखते हैं (अज्ञात):

@NxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxGCGA+ATAGAGAG
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxTTTATGTTTTTAATTAATTCTGTTTCCTCAGATTGATGATGAAGTTxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
AAAAA#FFFFFFFFFFFFAFFFFF#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF<AFFFFFFFFFFAFFFFFFFFFFFFFFFFFFF<FFFFFFFFFAFFFAFFAFFAFFFFFFFFAFFFFFFAAFFF<FAFAFFFFA
@NxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxGCGA+ATAGAGAG
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxCCCTCTGCTGGAACTGACACGCAGACATTCAGCGGCTCCGCCGCCxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
AAAAA#FFFFF7FFFFFFAFFFFA#F7FFFFFFFFF7FFFFFAF<FFFFFFFFFFFFFFAFFF.F.FFFFF.FAFFF.FFFFFFFFFFFFFF.)F.FFA))FFF7)F7F<.FFFF.FFF7FF<.FFA<7FA.<.7FF.FFFAFF

प्रत्येक रिकॉर्ड की पहली पंक्ति ए से शुरू होती है @

EDIT2:

zcat file > /dev/null 31mins लेता है।

EDIT3: केवल पहली पंक्ति के साथ शुरू होता है @। अन्य कोई भी कभी नहीं होगा। देखें यहाँ । रिकॉर्ड क्रम में रहने की जरूरत है। परिणामी फ़ाइल में कुछ भी जोड़ना ठीक नहीं है।


एक एकल को कितना समय लगता है zcat file > /dev/null?
कोरोबा

क्या आप प्रश्न में फ़ाइल का एक छोटा सा नमूना प्रदान कर सकते हैं?
फ्लोइम स्वयं

आप कहते हैं कि प्रत्येक रिकॉर्ड के साथ शुरू होता है @और यह भी कि प्रति रिकॉर्ड 4 लाइनें हैं। क्या ये दोनों पूर्ण हैं? - और लाइनों 2,3,4 के साथ शुरू कर सकते हैं @? और क्या फ़ाइल में पाद लेख लाइनों का कोई गैर-रिकॉर्ड हैडर है?
पीटर।

1
क्या आप एक समाधान की तलाश कर रहे हैं जो संपीड़ित इनपुट को संभालता है और / या संपीड़ित आउटपुट का उत्पादन करता है? क्या आप चार समान रूप से संकुचित फ़ाइलों की तलाश कर रहे हैं?
स्टीफन किट

जवाबों:


4

मुझे नहीं लगता कि आप ऐसा कर सकते हैं - मज़बूती से नहीं, और आपके पूछने के तरीके से नहीं। बात यह है, संग्रह का संपीड़न अनुपात संभवतः सिर से पूंछ तक समान रूप से वितरित नहीं किया जाएगा - संपीड़न एल्गोरिदम दूसरों की तुलना में कुछ हिस्सों में बेहतर लागू होगा। बस यह कैसे काम करता है। और इसलिए आप संपीड़ित फ़ाइल के आकार पर अपने विभाजन को कारक नहीं बना सकते।

क्या अधिक है, gzipबस आकार में 4gbs से अधिक संकुचित फ़ाइलों के मूल आकार को संग्रहीत करने का समर्थन नहीं करता है - यह इसे संभाल नहीं सकता है। और इसलिए आप एक विश्वसनीय आकार प्राप्त करने के लिए संग्रह को क्वेरी नहीं कर सकते - क्योंकि यह आपको बेवकूफ बना देगा।

4 लाइन बात - यह वास्तव में बहुत आसान है। 4-फ़ाइल की बात - मुझे नहीं पता कि आप इसे कैसे मज़बूती से कर सकते हैं और पहले समान आकार प्राप्त करने के लिए संग्रह को निकाले बिना एक समान वितरण के साथ। मुझे नहीं लगता कि आप कोशिश कर सकते हैं क्योंकि मैंने कोशिश की थी।

हालाँकि, आप क्या कर सकते हैं, विभाजित आउटपुट फ़ाइलों के लिए अधिकतम आकार निर्धारित किया गया है, और सुनिश्चित करें कि वे हमेशा रिकॉर्ड अवरोधों पर टूटे हुए हैं। जिसे आप आसानी से कर सकते हैं। यहां एक छोटी स्क्रिप्ट है जो gzipआर्काइव को हटाकर और कुछ स्पष्ट ddपाइप-बफ़र्स के माध्यम से सामग्री को विशिष्ट count=$rptतर्कों के साथ पाइपिंग करेगी , जो कि lz4फ़्लाइ पर प्रत्येक फ़ाइल को डिकम्प्रेस / पुनः प्राप्त करने के माध्यम से गुजरने से पहले । मैं भी कुछ छोटे teeपाइप चाल में फेंक दिया और साथ ही साथ प्रत्येक वर्ग के लिए पिछले चार लाइनों को मुद्रित करने के लिए।

(       IFS= n= c=$(((m=(k=1024)*k)/354))
        b=bs=354xk bs=bs=64k
        pigz -d </tmp/gz | dd i$bs o$b |
        while   read -r line _$((n+=1))
        do      printf \\n/tmp/lz4.$n\\n
        { {     printf %s\\n "$line"
                dd count=$c i$b o$bs
        }|      tee /dev/fd/3|lz4 -BD -9 >/tmp/lz4.$n
        } 3>&1| tail -n4 |tee /dev/fd/2 |
                wc -c;ls -lh /tmp/[gl]z*
        done
)

यह सिर्फ तब तक चलता रहेगा जब तक कि यह सभी इनपुट को संभाल न ले। यह इसे कुछ प्रतिशत से विभाजित करने का प्रयास नहीं करता है - जो इसे प्राप्त नहीं कर सकता है - लेकिन इसके बजाय यह विभाजित होकर अधिकतम कच्ची बाइट गिनती प्रति विभाजित करता है। और वैसे भी, आपकी समस्या का एक बड़ा हिस्सा यह है कि आप अपने संग्रह पर एक विश्वसनीय आकार प्राप्त नहीं कर सकते क्योंकि यह बहुत बड़ा है - आप जो भी करते हैं, वह फिर से न करें - विभाजन को 4gbs से कम कर दें, यह गोल हो जाता है , शायद। यह छोटी स्क्रिप्ट, कम से कम, आपको डिस्क के असम्पीडित बाइट को लिखने के लिए बिना ऐसा करने में सक्षम बनाती है।

यहाँ एक छोटा संस्करण आवश्यक के लिए छीन लिया गया है - यह रिपोर्ट के सभी सामानों में नहीं जोड़ता है:

(       IFS= n= c=$((1024*1024/354))
        pigz -d | dd ibs=64k obs=354xk |
        while   read -r line _$((n+=1))
        do {    printf %s\\n "$line"
                dd count=$c obs=64k ibs=354xk
        }  |    lz4 -BD -9  >/tmp/lz4.$n
        done
)  </tmp/gz

यह सभी चीजों को पहले की तरह ही करता है, ज्यादातर, इसके बारे में कहने के लिए बस इतना ही नहीं है। इसके अलावा, कम अव्यवस्था है, इसलिए यह देखना आसान है कि क्या चल रहा है, शायद।

IFS=बात सिर्फ एक को संभालने के लिए है readयात्रा प्रति पंक्ति। हम readएक क्योंकि इनपुट समाप्त होने पर हमें अपने लूप की आवश्यकता होती है। यह आपके रिकॉर्ड के आकार पर निर्भर करता है - जो, आपके उदाहरण के अनुसार, 354 बाइट्स प्रति है। मैंने gzipइसे परीक्षण करने के लिए कुछ यादृच्छिक डेटा के साथ 4 + जीबी आर्क बनाया ।

यादृच्छिक डेटा इस तरह से मिला था:

(       mkfifo /tmp/q; q="$(echo '[1+dPd126!<c]sc33lcx'|dc)"
        (tr '\0-\33\177-\377' "$q$q"|fold -b144 >/tmp/q)&
        tr '\0-\377' '[A*60][C*60][G*60][N*16][T*]' | fold -b144 |
        sed 'h;s/^\(.\{50\}\)\(.\{8\}\)/@N\1+\2\n/;P;s/.*/+/;H;x'|
        paste "-d\n" - - - /tmp/q| dd bs=4k count=kx2k  | gzip
)       </dev/urandom >/tmp/gz 2>/dev/null

... लेकिन शायद आपको इसके बारे में इतना चिंतित होने की आवश्यकता नहीं है, क्योंकि आपके पास पहले से ही डेटा और सभी हैं। समाधान पर वापस जाएं ...

मूल रूप से pigz- जो करने के लिए की तुलना में थोड़ा तेजी से विघटित लगता है zcat- असम्पीडित धारा बाहर पाइप, और ddबफ़र्स कि उत्पादन में लिखने ब्लॉक विशेष रूप से 354-बाइट्स के एक से अधिक आकार। लूप readएक $lineबार प्रत्येक पुनरावृत्ति का परीक्षण करेगा कि इनपुट अभी भी आ रहा है, जिसे printfबाद printfमें lz4दूसरे से पहले ddब्लॉक करने के लिए कहा जाता है, विशेष रूप से 354-बाइट्स के एक से अधिक आकार को पढ़ने के लिए कहा जाता है dd- अवधि के लिए बफरिंग प्रक्रिया के साथ सिंक्रनाइज़ करने के लिए। प्रारंभिक के कारण प्रति पुनरावृत्ति में एक छोटा पाठ होगा read $line- लेकिन इससे कोई फर्क नहीं पड़ता, क्योंकि हम उस पर lz4- हमारी कलेक्टर प्रक्रिया - वैसे भी छाप रहे हैं।

मैंने इसे सेट किया है इसलिए प्रत्येक पुनरावृत्ति लगभग 1gb असम्पीडित डेटा को पढ़ेगी और उस इन-स्ट्रीम को 650Mb या इसके आसपास स्ट्रीम करेगी। lz4बहुत ज्यादा किसी भी अन्य उपयोगी संपीड़न विधि की तुलना में बहुत तेज है - यही कारण है कि मैंने इसे यहां चुना क्योंकि मुझे इंतजार करना पसंद नहीं है। xzवास्तविक कंप्रेसिंग पर बहुत बेहतर काम करेगा, शायद, हालांकि। lz4हालाँकि, इसके बारे में एक बात यह है कि यह रैम की गति के करीब अक्सर कम हो सकता है - जिसका अर्थ है कि आप कई बार किसी lz4संग्रह को इतनी तेज़ी से विघटित कर सकते हैं जितना कि आप इसे वैसे भी मेमोरी में लिख पाएंगे।

बड़ा प्रति पुनरावृत्ति कुछ रिपोर्ट करता है। दोनों लूप ddट्रांसफर की गई कच्ची बाइट्स की संख्या और गति वगैरह पर रिपोर्ट छापेंगे। बड़ा लूप भी प्रति चक्र इनपुट की अंतिम 4 पंक्तियों को प्रिंट करेगा, और उसी के लिए एक बाइट काउंट करेगा, उसके बाद lsडायरेक्टरी जिसमें मैं lz4अभिलेख लिखता हूं । यहाँ कुछ राउंड ऑफ़ आउटपुट हैं:

/tmp/lz4.1
2961+1 records in
16383+1 records out
1073713090 bytes (1.1 GB) copied, 169.838 s, 6.3 MB/s
@NTACGTANTTCATTGGNATGACGCGCGTTTATGNGAGGGCGTCCGGAANGC+TCTCTNCC
TACGTANTTCATTGGNATGACGCGCGTTTATGNGAGGGCGTCCGGAANGCTCTCTNCCGAGCTCAGTATGTTNNAAGTCCTGANGNGTNGCGCCTACCCGACCACAACCTCTACTCGGTTCCGCATGCATGCAACACATCGTCA
+
I`AgZgW*,`Gw=KKOU:W5dE1m=-"9W@[AG8;<P7P6,qxE!7P4##,Q@c7<nLmK_u+IL4Kz.Rl*+w^A5xHK?m_JBBhqaLK_,o;p,;QeEjb|">Spg`MO6M'wod?z9m.yLgj4kvR~+0:.X#(Bf
354

-rw-r--r-- 1 mikeserv mikeserv 4.7G Jun 16 08:58 /tmp/gz
-rw-r--r-- 1 mikeserv mikeserv 652M Jun 16 12:32 /tmp/lz4.1

/tmp/lz4.2
2961+1 records in
16383+1 records out
1073713090 bytes (1.1 GB) copied, 169.38 s, 6.3 MB/s
@NTTGTTGCCCTAACCANTCCTTGGGAACGCAATGGTGTGANCTGCCGGGAC+CTTTTGCT
TTGTTGCCCTAACCANTCCTTGGGAACGCAATGGTGTGANCTGCCGGGACCTTTTGCTGCCCTGGTACTTTTGTCTGACTGGGGGTGCCACTTGCAGNAGTAAAAGCNAGCTGGTTCAACNAATAAGGACNANTTNCACTGAAC
+
>G-{N~Q5Z5QwV??I^~?rT+S0$7Pw2y9MV^BBTBK%HK87(fz)HU/0^%JGk<<1--7+r3e%X6{c#w@aA6Q^DrdVI0^8+m92vc>RKgnUnMDcU:j!x6u^g<Go?p(HKG@$4"T8BWZ<z.Xi
354

-rw-r--r-- 1 mikeserv mikeserv 4.7G Jun 16 08:58 /tmp/gz
-rw-r--r-- 1 mikeserv mikeserv 652M Jun 16 12:32 /tmp/lz4.1
-rw-r--r-- 1 mikeserv mikeserv 652M Jun 16 12:35 /tmp/lz4.2

gzip -lकेवल <2GiB असम्पीडित फ़ाइलें IIRC (वैसे भी ओपी की फ़ाइल से कुछ छोटा) के लिए काम करता है।
स्टीफन चेजलस

@ स्टीफनचेज़ेलस - लानत। यही कारण है कि मैं एक असम्पीडित आकार पाने पर पता लगा सकता है। इसके बिना, यह बिल्कुल काम नहीं करता है।
चाटुकार

4

रिकॉर्ड सीमाओं पर फ़ाइलों को विभाजित करना वास्तव में बहुत आसान है, बिना किसी कोड के:

zcat your_file.gz | split -l 10000 - output_name_

यह प्रत्येक से 10000 पंक्तियों की आउटपुट फाइलें बनाएगा, जिनमें नाम output_name_aa, output_name_ab, output_name_ac, ... एक इनपुट के साथ है, जैसा कि आपका एक इनपुट है, इससे आपको बहुत सारी आउटपुट फाइलें मिलेंगी। 10000चार में से किसी भी एक के साथ बदलें , और आप आउटपुट फ़ाइलों को अपनी इच्छानुसार बड़ा या छोटा बना सकते हैं। दुर्भाग्य से, अन्य उत्तरों के साथ, इनपुट के बारे में कुछ अनुमान किए बिना आपको आउटपुट फ़ाइलों की वांछित संख्या (लगभग) समान आकार प्राप्त करने की गारंटी देने का एक अच्छा तरीका नहीं है। (या वास्तव में पूरी बात के माध्यम से पाइपिंग wc।) यदि आपके रिकॉर्ड लगभग बराबर आकार के हैं (या कम से कम, लगभग समान रूप से वितरित) तो आप इस तरह एक अनुमान के साथ आने की कोशिश कर सकते हैं:

zcat your_file.gz | head -n4000 | gzip | wc -c

यह आपको आपकी फ़ाइल के पहले 1000 रिकॉर्ड के संकुचित आकार को बताएगा। उसके आधार पर, आप संभवतः एक अनुमान के साथ आ सकते हैं कि आप प्रत्येक फ़ाइल में कितनी पंक्तियों को चार फाइलों के साथ समाप्त करना चाहते हैं। (यदि आप नहीं चाहते हैं कि एक छोटी सी पाँचवीं फ़ाइल बची रहे, तो अपने अनुमान को थोड़ा बढ़ाएँ, या पाँचवीं फ़ाइल को चौथे की पूँछ से निपटने के लिए तैयार रहें।)

संपादित करें: यहां एक और चाल है, यह मानते हुए कि आप संपीड़ित आउटपुट फ़ाइलें चाहते हैं:

#!/bin/sh

base=$(basename $1 .gz)
unpigz -c $1 | split -l 100000 --filter='pigz -c > _$FILE.gz' - ${base}_

batch=$((`ls _*.gz | wc -l` / 4 + 1))
for i in `seq 1 4`; do
  files=`ls _*.gz | head -$batch`
  cat $files > ${base}_$i.gz && rm $files
done

यह बहुत सारी छोटी फ़ाइलों को बनाएगा और फिर जल्दी से उन्हें एक साथ वापस करेगा। (आपको अपनी फ़ाइलों में लाइनें कितनी लंबी हैं, इसके आधार पर -l पैरामीटर को ट्विस्ट करना पड़ सकता है।) यह मानता है कि आपके पास GNU कोरूटिल्स (स्प्लिट - फ़िल्टर के लिए) का अपेक्षाकृत हालिया संस्करण है और आपकी इनपुट फ़ाइल आकार का लगभग 130% है खाली डिस्क स्पेस। अगर आपके पास नहीं है, तो पिगज़ / अनपिगज़ के लिए गज़िप / ज़कात का विकल्प दें। मैंने सुना है कि कुछ सॉफ्टवेयर लाइब्रेरीज़ (जावा?) इस तरह से गज़िप फाइल को संभाल नहीं सकती है, लेकिन मुझे अब तक इससे कोई समस्या नहीं है। (पिग्मेंट संपीड़न को समानांतर करने के लिए एक ही चाल का उपयोग करता है।)


यदि आपने पिग स्थापित किया है, तो आप 'zcat' के लिए 'pigz-LCD' को प्रतिस्थापित करके चीजों को थोड़ा बढ़ा सकते हैं।
आकर्षित किया

2
आह, मैंने अभी ध्यान दिया है कि आपने पहले ही प्रश्न में विभाजन का उल्लेख किया है। लेकिन वास्तव में, बस किसी भी समाधान के बारे में उसी चीज के बारे में किया जा रहा है जो हुड के नीचे विभाजित है। कठिन हिस्सा यह पता लगा रहा है कि आपको प्रत्येक फ़ाइल में कितनी पंक्तियाँ डालनी हैं।
ड्रयू

3

Google- क्षेत्र की जाँच करने के बाद मैं जो कुछ भी इकट्ठा करता हूँ, और एक 7.8 GiB .gzफ़ाइल का परीक्षण करता हूँ, उससे लगता है कि मूल असम्पीडित फ़ाइल का आकार बड़ी फ़ाइलों के लिए सटीक (यानी गलत ) नहीं है ( .gz4 जीआईबी से अधिक (शायद 2GBB) कुछ के लिए। के संस्करण gzip)।
गज़िप के मेटाडेटा के मेरे परीक्षण:

* The compressed.gz file is  7.8 GiB ( 8353115038 bytes) 
* The uncompressed  file is 18.1 GiB (19436487168 bytes)
* The metadata says file is  2.1 GiB ( 2256623616 bytes) uncompressed

तो ऐसा लगता है कि वास्तव में इसे असंपीड़ित किए बिना असम्पीडित आकार को निर्धारित करना संभव नहीं है (जो कि थोड़ा मोटा है, कम से कम कहने के लिए!)

किसी भी तरह, यहां रिकॉर्ड सीमाओं पर एक असंपीड़ित फ़ाइल को विभाजित करने का एक तरीका है, जहां प्रत्येक रिकॉर्ड में 4 लाइनें होती हैं

यह बाइट्स (के माध्यम से stat) में फ़ाइल के आकार का उपयोग करता है , और awkगिनती बाइट्स (वर्ण नहीं) के साथ। लाइन एंडिंग है या नहीं LF| CR| CRLF, यह स्क्रिप्ट बिल्ट वैरिएबल के माध्यम से लंबाई समाप्त होने वाली लाइन को संभालती है RT)।

LC_ALL=C gawk 'BEGIN{"stat -c %s "ARGV[1] | getline inSize
                      segSiz=int(inSize/4)+((inSize%4)==0?0:1)
                      ouSplit=segSiz; segNb=0 }
               { lnb++; bytCt+=(length+length(RT))
                 print $0 > ARGV[1]"."segNb
                 if( lnb!=4 ) next
                 lnb=0
                 if( bytCt>=ouSplit ){ segNb++; ouSplit+=segSiz }
               }' myfile

नीचे वह परीक्षण है जिसका उपयोग मैंने जांचने के लिए किया था कि प्रत्येक फ़ाइल की लाइन काउंट है mod 4 == 0

for i in myfile  myfile.{0..3}; do
    lc=$(<"$i" wc -l)
    printf '%s\t%s\t' "$i" $lc; 
    (( $(echo $lc"%4" | bc) )) && echo "Error: mod 4 remainder !" || echo 'mod 4 ok'  
done | column -ts$'\t' ;echo

परीक्षण उत्पादन:

myfile    1827904  mod 4 ok
myfile.0  456976   mod 4 ok
myfile.1  456976   mod 4 ok
myfile.2  456976   mod 4 ok
myfile.3  456976   mod 4 ok

myfile द्वारा उत्पन्न किया गया था:

printf %s\\n {A..Z}{A..Z}{A..Z}{A..Z}—{1..4} > myfile

2

यह गंभीर जवाब देने के लिए नहीं है! मैं बस के साथ कर रहा हूँ flexऔर यह शायद ~ 50Gb के साथ एक इनपुट फ़ाइल पर काम नहीं करेगा (यदि बिल्कुल, मेरी परीक्षण फ़ाइल की तुलना में बड़े इनपुट डेटा पर):

यह मेरे लिए ~ 1Gb फ़ाइल input.txt पर काम करता है :

flexइनपुट फ़ाइल splitter.l को देखते हुए :

%{
#include <stdio.h>
extern FILE* yyin;
extern FILE* yyout;

int input_size = 0;

int part_num;
int part_num_max;
char **part_names;
%}

%%
@.+ {
        if (ftell(yyout) >= input_size / part_num_max) {
            fclose(yyout);
            if ((yyout = fopen(part_names[++part_num], "w")) == 0) {
                exit(1);
            }
        }
        fprintf(yyout, "%s", yytext);
    }
%%

int main(int argc, char *argv[]) {

    if (argc < 2) {
        return 1;
    } else if ((yyin = fopen(argv[1], "r")) == 0) {
        return 1;
    } else if ((yyout = fopen(argv[2], "w")) == 0) {
        fclose(yyin);
        return 1;
    } else {

        fseek(yyin, 0L, SEEK_END);
        input_size = ftell(yyin);
        rewind(yyin);

        part_num = 0;
        part_num_max = argc - 2;
        part_names = argv + 2;

        yylex();

        fclose(yyin);
        fclose(yyout);
        return 0;
    }
}

lex.yy.c को जनरेट करना और इसे splitterबाइनरी के साथ संकलित करना :

$ flex splitter.l && gcc lex.yy.c -ll -o splitter

उपयोग:

$ ./splitter input.txt output.part1 output.part2 output.part3 output.part4

1 जीबी के लिए समय चल रहा है input.txt :

$ time ./splitter input.txt output.part1 output.part2 output.part3 output.part4

real    2m43.640s
user    0m48.100s
sys     0m1.084s

यहाँ वास्तविक लेक्सिंग इतना सरल है, आपको वास्तव में lex से कोई लाभ नहीं है। बस कॉल करें getc(stream)और कुछ सरल तर्क लागू करें । इसके अलावा, क्या आप जानते हैं कि (dot) regex character में (f) lex न्यूलाइन को छोड़कर किसी भी कैरेक्टर से मेल खाता है , है ना? जबकि ये रिकॉर्ड मल्टी-लाइन हैं।
कज़

@Kaz जबकि अपने बयान आम तौर पर corrent कर रहे हैं, यह वास्तव में प्र में उपलब्ध कराए गए आंकड़ों के साथ काम करता
FloHimself

केवल गलती से, क्योंकि एक डिफ़ॉल्ट नियम है जब कुछ भी मेल नहीं खाता है: एक चरित्र का उपभोग करें और इसे आउटपुट पर प्रिंट करें! अन्य पासवर्डों में, आप अपनी फ़ाइल को केवल एक नियम के साथ बदल सकते हैं जो कि @चरित्र को पहचानता है , और फिर डिफ़ॉल्ट नियम को डेटा की प्रतिलिपि बनाने देता है। अब आपके पास डेटा के हिस्से को एक बड़े टोकन के रूप में कॉपी करने का आपका नियम है, और फिर एक समय में दूसरी पंक्ति एक वर्ण प्राप्त करने वाला डिफ़ॉल्ट नियम।
कज़

स्पष्टीकरण देने के लिए धन्यवाद। मुझे आश्चर्य है, आप इस कार्य को कैसे हल करेंगे txr
फ्लोइम खुद

मुझे यकीन नहीं है कि मैं करूंगा क्योंकि कार्य बड़ी मात्रा में डेटा के साथ एक बहुत ही सरल काम करना है, जितनी जल्दी हो सके।
काज

1

यहां पाइथन में एक समाधान है जो आउटपुट फाइल को लिखने के साथ-साथ आउटपुट फाइल पर एक पास करता है क्योंकि यह साथ जाता है।

उपयोग करने के बारे में एक विशेषता wc -lयह है कि आप मान रहे हैं कि यहां प्रत्येक रिकॉर्ड समान आकार का है। यहाँ यह सच हो सकता है, लेकिन नीचे का समाधान तब भी काम करता है जब ऐसा न हो। यह मूल रूप wc -cसे फ़ाइल में बाइट्स की संख्या का उपयोग कर रहा है। पायथन में, यह os.stat () के माध्यम से किया जाता है

तो यहां बताया गया है कि कार्यक्रम कैसे काम करता है। हम पहले बाइट ऑफसेट के रूप में आदर्श विभाजन बिंदुओं की गणना करते हैं। फिर आप इनपुट फाइल राइटिंग की लाइनों को उचित आउटपुट फाइल में पढ़ते हैं। जब आप देखते हैं कि आपने इष्टतम अगले विभाजन बिंदु को पार कर लिया है और आप एक रिकॉर्ड सीमा पर हैं, तो अंतिम आउटपुट फ़ाइल बंद करें और अगला खोलें।

कार्यक्रम इस अर्थ में इष्टतम है, यह एक बार इनपुट फ़ाइल के बाइट्स को पढ़ता है; फ़ाइल का आकार प्राप्त करना फ़ाइल डेटा को पढ़ने की आवश्यकता नहीं है। भंडारण की आवश्यकता एक पंक्ति के आकार के लिए आनुपातिक है। लेकिन पायथन या सिस्टम में संभवतः आई / ओ की गति बढ़ाने के लिए उचित फ़ाइल बफ़र्स हैं।

मैंने कितने फ़ाइल को विभाजित करने के लिए पैरामीटर जोड़ा है और भविष्य में इसे समायोजित करने के लिए रिकॉर्ड आकार क्या है।

और स्पष्ट रूप से इसे अन्य प्रोग्रामिंग भाषाओं में भी अनुवादित किया जा सकता है।

एक और बात, मुझे यकीन नहीं हो रहा है कि अगर विंडोज अपने क्रॉफ़्ट के साथ लाइन की लंबाई को ठीक से हैंडल करता है जैसा कि यूनिक्स-वाई सिस्टम पर होता है। यदि लेन () यहां एक-एक करके बंद है, तो मुझे उम्मीद है कि यह स्पष्ट है कि कार्यक्रम को कैसे समायोजित किया जाए।

#!/usr/bin/env python
import os

# Adjust these
filename = 'file.txt'
rec_size = 4
file_splits = 4

size = os.stat(filename).st_size
splits = [(i+1)*size/file_splits for i in range(file_splits)]
with open(filename, 'r') as fd:
    linecount = 0
    i = 0 # File split number
    out = open('file%d.txt' % i, 'w')
    offset = 0  # byte offset of where we are in the file: 0..size
    r = 0 # where we are in the record: 0..rec_size-1
    for line in fd:
        linecount += 1
        r = (r+1) % rec_size
        if offset + len(line) > splits[i] and r == 1 :
            out.close()
            i += 1
            out = open('file%d.txt' % i, 'w')
        out.write(line)
        offset += len(line)
    out.close()
    print("file %s has %d lines" % (filename, linecount))

यह एक रिकॉर्ड सीमा पर विभाजित नहीं है। जैसे। इस इनपुट के साथ तीसरी लाइन के बाद पहली सब फाइल स्प्लिट होती हैprintf %s\\n {A..Z}{A..Z}{A..Z}{A..Z}—{1..4}
पीटर।

1

उपयोगकर्ता FloHimself एक TXR समाधान के बारे में उत्सुक लग रहा था । यहाँ एम्बेडेड TXR लिस्प का उपयोग कर एक है :

(defvar splits 4)
(defvar name "data")

(let* ((fi (open-file name "r"))                 ;; input stream
       (rc (tuples 4 (get-lines fi)))            ;; lazy list of 4-tuples
       (sz (/ (prop (stat name) :size) splits))  ;; split size
       (i 1)                                     ;; split enumerator
       (n 0)                                     ;; tuplecounter within split
       (no `@name.@i`)                           ;; output split file name
       (fo (open-file no "w")))                  ;; output stream
  (whilet ((r (pop rc)))  ;; pop each 4-tuple
    (put-lines r fo) ;; send 4-tuple into output file
    ;; if not on the last split, every 1000 tuples, check the output file
    ;; size with stat and switch to next split if necessary.
    (when (and (< i splits)
               (> (inc n) 1000)
               (>= (seek-stream fo 0 :from-current) sz))
      (close-stream fo)
      (set fo (open-file (set no `@name.@(inc i)`) "w")
           n 0)))
  (close-stream fo))

टिप्पणियाँ:

  1. एक ही कारण के लिए pop-ट्यूपल्स की आलसी सूची से प्रत्येक टपल को महत्वपूर्ण है, ताकि आलसी सूची का उपभोग हो। हमें उस सूची के शुरू होने का संदर्भ नहीं रखना चाहिए क्योंकि तब मेमोरी बढ़ेगी जब हम फ़ाइल के माध्यम से मार्च करेंगे।

  2. (seek-stream fo 0 :from-current)का नो-ऑप केस है seek-stream, जो वर्तमान स्थिति को वापस करके खुद को उपयोगी बनाता है।

  3. प्रदर्शन: यह उल्लेख नहीं है। उपयोग करने योग्य, लेकिन किसी भी ट्राफियां को घर नहीं लाया जाएगा।

  4. चूँकि हम केवल हर 1000 टुपल्स की जाँच करते हैं, इसलिए हम टपल के आकार को 4000 रेखाएँ बना सकते हैं।


0

यदि आपको नई फ़ाइलों को मूल फ़ाइल के सन्निहित होने की आवश्यकता नहीं है, तो आप इसे sedनिम्न तरीके से पूरी तरह से कर सकते हैं :

sed -n -e '1~16,+3w1.txt' -e '5~16,+3w2.txt' -e '9~16,+3w3.txt' -e '13~16,+3w4.txt'

-nप्रत्येक पंक्ति मुद्रण से रोकता है, और से प्रत्येक -eस्क्रिप्ट अनिवार्य रूप से एक ही बात कर रही है। 1~16पहली पंक्ति से मेल खाता है, और हर 16 वीं पंक्ति के बाद। ,+3इसका मतलब है कि उनमें से प्रत्येक के बाद अगली तीन पंक्तियों का मिलान करें। w1.txtफ़ाइल में उन सभी पंक्तियों को लिखें 1.txt। यह 4 लाइनों के हर चौथे समूह को ले जा रहा है और इसे 4 पंक्तियों के पहले समूह से शुरू करते हुए एक फाइल पर लिख रहा है। अन्य तीन कमांड एक ही काम करते हैं, लेकिन वे प्रत्येक को 4 लाइनों द्वारा आगे की ओर स्थानांतरित करते हैं, और एक अलग फाइल पर लिखते हैं।

यदि फ़ाइल आपके द्वारा निर्धारित विनिर्देश से बिल्कुल मेल नहीं खाती है, तो यह बहुत बुरी तरह से टूट जाएगा, लेकिन अन्यथा यह आपके इच्छित उद्देश्य के अनुसार काम करना चाहिए। मैंने इसे प्रोफाइल नहीं किया है, इसलिए मुझे नहीं पता कि यह कितना कुशल होगा, लेकिन sedस्ट्रीम एडिटिंग में यथोचित कुशल है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.