स्ट्रिंग को एक विशाल (70GB), एक पंक्ति, पाठ फ़ाइल में बदलें


126

मेरे पास एक विशाल (70GB), एक पंक्ति , पाठ फ़ाइल है और मैं इसमें एक स्ट्रिंग (टोकन) को बदलना चाहता हूं। मैं टोकन को बदलना चाहता हूं <unk>, एक और डमी टोकन ( दस्ताने का मुद्दा )।

मैंने कोशिश की sed:

sed 's/<unk>/<raw_unk>/g' < corpus.txt > corpus.txt.new

लेकिन आउटपुट फ़ाइल corpus.txt.newमें शून्य बाइट्स है!

मैंने भी पर्ल का उपयोग करने की कोशिश की:

perl -pe 's/<unk>/<raw_unk>/g' < corpus.txt > corpus.txt.new

लेकिन मुझे मेमोरी में त्रुटि हो गई।

छोटी फ़ाइलों के लिए, ऊपर दिए गए दोनों कमांड काम करते हैं।

मैं एक स्ट्रिंग को कैसे बदल सकता हूं ऐसी फाइल है? यह एक संबंधित प्रश्न है, लेकिन किसी भी उत्तर ने मेरे लिए काम नहीं किया।

संपादित करें : 10GB (या जो भी) में से प्रत्येक में फ़ाइल को विभाजित sedकरने और उनमें से प्रत्येक पर आवेदन करने और फिर उनके साथ विलय करने के बारे में catक्या है? क्या इसका कोई मतलब है? क्या अधिक सुरुचिपूर्ण समाधान है?


जैसा कि @ गिल्स ने कहा, क्या आप कुछ ऐसे दोहराए गए चरित्र का पता लगा सकते हैं, जो आपकी एकल पंक्ति में कस्टम सीमांकक के रूप में काम कर सकते हैं?
रोमनप्रेक्टर

मैं सोच रहा हूं कि एक उपकरण जो केवल खोज और प्रतिस्थापन कर सकता है, लेकिन कोई और अधिक जटिल रेगेक्स नहीं, तेज होगा। यह भी एक समय में एक लाइन करने से लाभ नहीं होगा, इसलिए इस फ़ाइल पर चोक नहीं होगा। दुर्भाग्य से मुझे इस तरह के उपकरण के अस्तित्व का कोई पता नहीं है, हालांकि इसे लिखना मुश्किल नहीं होगा। यदि यह एक बंद है, तो जवाब में से एक के रूप में newline वर्णों में प्रतिस्थापित करना शायद सबसे आसान होगा।
ctrl-alt-delor

क्या आपकी फ़ाइल में ASCII के अलावा कुछ और है? यदि हां, तो सभी यूनिकोड हैंडलिंग को छोड़ा जा सकता है और कच्चे बाइट्स को संसाधित किया जा सकता है।
पैट्रिक बुचर

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

2
आप बाइट्स में चंक फ़ाइल साइज़ को परिभाषित करने वाले विकल्प के splitसाथ उपयोग कर सकते हैं -b। बारी sed- बारी से और फिर से इकट्ठा होने की प्रक्रिया में प्रत्येक प्रक्रिया । एक जोखिम यह है कि <unk>दो फ़ाइलों में विभाजित किया जा सकता है और नहीं मिलेगा ...
व्लादिस्लाव डोभालगिक्स

जवाबों:


106

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

अगर कोई ऐसा ASCII वर्ण फ़ाइल में अक्सर दिखाई देता है और में प्रकट नहीं होता है कि <unk>या <raw_unk>, तो आप उस का उपयोग कर सकते रिकॉर्ड विभाजक के रूप में। चूंकि अधिकांश उपकरण कस्टम रिकॉर्ड विभाजकों की अनुमति नहीं देते हैं, इसलिए उस चरित्र और newlines के बीच स्वैप करें। trबाइट्स प्रक्रियाएं, रेखाएं नहीं, इसलिए यह किसी भी रिकॉर्ड आकार की परवाह नहीं करता है। ;काम करता है कि :

<corpus.txt tr '\n;' ';\n' |
sed 's/<unk>/<raw_unk>/g' |
tr '\n;' ';\n' >corpus.txt.new

आप अपने द्वारा खोजे जा रहे पाठ के पहले चरित्र पर लंगर डाल सकते हैं, यह मानते हुए कि यह खोज पाठ में दोहराया नहीं गया है और यह अक्सर पर्याप्त दिखाई देता है। यदि फ़ाइल के साथ शुरू हो सकता है unk>, sed '2,$ s/…एक शानदार मैच से बचने के लिए sed कमांड को बदलें ।

<corpus.txt tr '\n<' '<\n' |
sed 's/^unk>/raw_unk>/g' |
tr '\n<' '<\n' >corpus.txt.new

वैकल्पिक रूप से, अंतिम वर्ण का उपयोग करें।

<corpus.txt tr '\n>' '>\n' |
sed 's/<unk$/<raw_unk/g' |
tr '\n>' '>\n' >corpus.txt.new

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


8
मेरे पास परीक्षण करने के लिए ऐसी कोई फ़ाइल नहीं है, लेकिन Awk में आप "रिकॉर्ड सेपरेटर" और "आउटपुट रिकॉर्ड सेपरेटर" निर्दिष्ट कर सकते हैं। तो यह मानते हुए कि आपकी फ़ाइल में अल्पविरामों का एक अच्छा बू आ रहा है, यह संभव है कि आप इसे हल कर सकते हैं: awk -v RS=, -v ORS=, '{gsub(/<unk>/, "<raw_unk>"); print}' नहीं?
वाइल्डकार्ड

4
@Wildcard हाँ, यह एक और उपाय है। Awk को sed की तुलना में धीमा होना पसंद है, यही कारण है कि मैं इसे एक बड़ी फ़ाइल के लिए पसंदीदा समाधान के रूप में पेश नहीं करता हूं।
गिल्स

आप Perl में रिकॉर्ड सेपरेटर को कमांड लाइन विकल्प -0और एक $/
चार्ट

@ गिल्स: लेकिन awkदो बार स्ट्रीम पास करने से बचें tr। तो क्या यह अभी भी धीमा होगा?
user285259

2
@ user285259 आमतौर पर नहीं। trबहुत तेज है और पाइप को समानांतर भी किया जा सकता है।
गिल्स

110

इतनी बड़ी फ़ाइल के लिए, एक संभावना फ्लेक्स है। आज्ञा unk.lदेना:

%%
\<unk\>     printf("<raw_unk>");  
%%

फिर संकलित करें और निष्पादित करें:

$ flex -o unk.c  unk.l
$ cc -o unk -O2 unk.c -lfl
$ unk < corpus.txt > corpus.txt.new

5
makeइसके लिए डिफ़ॉल्ट नियम हैं, फ्लेक्स / cc के बजाय आप %option mainunk.l की पहली पंक्ति के रूप में जोड़ सकते हैं और फिर बस make unk। मैं अधिक-या-कम रिफ्लेक्सली उपयोग करता हूं %option main 8bit fast, और export CFLAGS='-march=native -pipe -Os'मेरे पास है .bashrc
jthill

1
@undercat: यदि यह ऑफ-टॉपिक नहीं था, तो मैं आपको विशेष-उद्देश्य इनपुट पार्सिंग के लिए जल-स्तर की समस्या को हल करने से लेकर कई गैर-संकलक फ्रंट एंड एप्लिकेशन दिखा सकता था। यह आश्चर्यजनक है कि आप इसके साथ क्या कर सकते हैं, यदि आप बॉक्स के बाहर सोचते हैं :-)
jamesqf

@jthill, धन्यवाद: %option main+ make+ वैकल्पिक रूप CFLAGSसे एक बहुत अच्छी चाल है !! क्या -march=nativeडिफ़ॉल्ट व्यवहार है?
जेजोआओ

1
@jamesqf जैसा कि आपने कहा - विषय पर एक प्रश्न बनाना कठिन होगा - लेकिन मैं इसे भी देखना चाहूंगा
स्टीवन पेनी

1
@jamesqf uni पर मेरा एक प्रोफेसर ने एक कारखाने के लिए कपड़े के प्रकारों को मान्यता देने के लिए फ्लेक्स का इस्तेमाल किया! कैसे के बारे में कुछ पूछना: "फ्लेक्स एक बहुत शक्तिशाली उपकरण की तरह लगता है, लेकिन मैं किसी भी कंपाइलर / पार्सर लिखने की संभावना नहीं हूं - क्या फ्लेक्स के लिए कोई अन्य उपयोग के मामले हैं?"
पॉल इवांस

41

इसलिए आपके पास एक बार में पूरी फ़ाइल रखने के लिए पर्याप्त भौतिक मेमोरी (RAM) नहीं है, लेकिन 64-बिट सिस्टम पर आपके पास पूरी फ़ाइल को मैप करने के लिए पर्याप्त वर्चुअल पता स्थान है। वर्चुअल मैपिंग इस तरह के मामलों में एक साधारण हैक के रूप में उपयोगी हो सकता है।

सभी आवश्यक कार्य पायथन में शामिल हैं। कई कष्टप्रद सूक्ष्मताएं हैं, लेकिन यह सी कोड लिखने से बचता है। विशेष रूप से, फ़ाइल को मेमोरी में कॉपी करने से बचने के लिए देखभाल की आवश्यकता होती है, जो बिंदु को पूरी तरह से हरा देगा। प्लस साइड पर, आपको मुफ्त (अजगर "अपवाद") के लिए त्रुटि-रिपोर्टिंग मिलती है :)।

#!/usr/bin/python3
# This script takes input from stdin
# (but it must be a regular file, to support mapping it),
# and writes the result to stdout.

search = b'<unk>'
replace = b'<raw_unk>'


import sys
import os
import mmap

# sys.stdout requires str, but we want to write bytes
out_bytes = sys.stdout.buffer

mem = mmap.mmap(sys.stdin.fileno(), 0, access=mmap.ACCESS_READ)
i = mem.find(search)
if i < 0:
    sys.exit("Search string not found")

# mmap object subscripts to bytes (making a copy)
# memoryview object subscripts to a memoryview object
# (it implements the buffer protocol).
view = memoryview(mem)

out_bytes.write(view[:i])
out_bytes.write(replace)
out_bytes.write(view[i+len(search):])

यदि मेरे सिस्टम में 8 gb में से लगभग 4 gb परिणामी मेमोरी मुफ्त है, तो क्या mem = mmap.mmap (sys.stdin.fileno (), 0, access = mmap.ACCESS_READ) का अर्थ है कि उस स्थान में डेटा है? या यह बहुत कम होगा (1 जीबी?)>
राहुल

1
@Rahul "तो आपके पास पर्याप्त रैम नहीं है, लेकिन 64-बिट सिस्टम पर आपके पास पूरी फ़ाइल को मैप करने के लिए पर्याप्त वर्चुअल एड्रेस स्पेस है।" यह मांग पर भौतिक राम के अंदर और बाहर (या इसकी कमी) है। इस कार्यक्रम को किसी भी बड़ी मात्रा में भौतिक रैम की आवश्यकता के बिना काम करना चाहिए। 64-बिट सिस्टम में अधिकतम भौतिक रैम की तुलना में बहुत अधिक वर्चुअल एड्रेस स्पेस है। इसके अलावा प्रत्येक चलने की प्रक्रिया का अपना वर्चुअल पता स्थान होता है। इसका मतलब यह है कि वर्चुअल एड्रेस स्पेस से पूरी तरह बाहर निकलने की प्रणाली एक चीज नहीं है, यह एक मान्य अवधारणा नहीं है।
sourcejedi

4
@ राहुल हां! अजगर mmap.mmap () C फ़ंक्शन mmap () के चारों ओर एक काफी पतला आवरण है। और एमएमएपी () एक ही तंत्र है जिसका उपयोग निष्पादन योग्य को चलाने के लिए किया जाता है, और साझा पुस्तकालयों से कोड।
सोर्सजेडी

2
@jamesqf मैं गलत हो सकता है, लेकिन मुझे लगता है कि यह सिर्फ एक व्यक्तिगत पसंद है। चूंकि प्रदर्शन घाटा नगण्य होगा (क्योंकि जैसा कि उन्होंने कहा, फ़ंक्शन वास्तविक सी फ़ंक्शन को कॉल करता है), ओवरहेड अपव्यय बहुत कम है, क्योंकि बीच में कोई अन्य सामान नहीं हो रहा है। सी बेहतर होता, लेकिन यह समाधान अनुकूलन के लिए लक्ष्य नहीं था, बस बड़े और मुश्किल 70 जीबी मुद्दे को हल करने के लिए।
राहुल

1
सामान्य तौर पर, अजगर में लिखना अधिक कॉम्पैक्ट होता है। इस मामले में यह पता चला कि अजगर संस्करण में कुछ विवरण हैं, और सी संस्करण लिखने के लिए अच्छे हो सकते हैं। (हालांकि यह इतना सरल नहीं है यदि searchइसमें NUL वर्ण हो सकता है। और मुझे लगता है कि अन्य C संस्करण यहाँ NUL वर्णों का समर्थन नहीं करता है replace।) तुलनात्मक उद्देश्यों के लिए C संस्करण प्राप्त करने के लिए आपका बहुत स्वागत है। हालाँकि यह याद रखें कि मेरे संस्करण में उन कार्यों के लिए बुनियादी त्रुटि रिपोर्टिंग शामिल है जो इसे निष्पादित करता है। सी संस्करण कम से कम IMO पढ़ने के लिए अधिक कष्टप्रद होगा , जब त्रुटि रिपोर्टिंग शामिल है।
sourcejedi

17

मुझे लगता है कि सी संस्करण बेहतर प्रदर्शन कर सकता है:

#include <stdio.h>
#include <string.h>

#define PAT_LEN 5

int main()
{
    /* note this is not a general solution. In particular the pattern
     * must not have a repeated sequence at the start, so <unk> is fine
     * but aardvark is not, because it starts with "a" repeated, and ababc
     * is not because it starts with "ab" repeated. */
    char pattern[] = "<unk>";          /* set PAT_LEN to length of this */
    char replacement[] = "<raw_unk>"; 
    int c;
    int i, j;

    for (i = 0; (c = getchar()) != EOF;) {
        if (c == pattern[i]) {
            i++;
            if (i == PAT_LEN) {
                printf("%s", replacement);
                i = 0;
            }
        } else {
            if (i > 0) {
                for (j = 0; j < i; j++) {
                    putchar(pattern[j]);
                }
                i = 0;
            }
            if (c == pattern[0]) {
                i = 1;
            } else {
                putchar(c);
            }
        }
    }
    /* TODO: fix up end of file if it ends with a part of pattern */
    return 0;
}

EDIT: टिप्पणियों से सुझावों के अनुसार संशोधित। पैटर्न के साथ बग भी तय किया गया <<unk>


2
आप (buf [j]) के बजाय (पैटर्न [j]) प्रिंट कर सकते हैं (वे इस बिंदु पर बराबर हैं, इसलिए आपको बफर की आवश्यकता नहीं है
RiaD

3
कोड भी स्ट्रिंग के लिए काम नहीं करेगा "<< unk>" ideone.com/ncM2yy
RiaD

10
0.3 सेकंड में 30 एमबी? वह केवल 90 एमबी / सेकंड है। memcpyगति (यानी मेमोरी टोंटी) हाल के x86 सीपीयू (जैसे स्काइलेक) पर 12GB / सेकंड की तरह कुछ है। यहां तक ​​कि stdio + सिस्टम कॉल ओवरहेड के साथ, डिस्क कैश में 30MB फ़ाइल हॉट के लिए, मैं एक कुशल कार्यान्वयन के लिए शायद 1GB / सेकंड की उम्मीद करूंगा। क्या आपने अनुकूलन अक्षम के साथ संकलित किया है, या एक-चार-ए-ए-टाइम I / O वास्तव में धीमा है? getchar_unlocked/ putchar_unlockedमदद कर सकते हैं, लेकिन शायद पढ़ने के लिए बेहतर है / शायद 128kiB (अधिकांश x86 सीपीयू पर एल 2 कैश आकार का आधा हिस्सा, इसलिए आप ज्यादातर एल 2 में हिट करते हुए पढ़ते हैं)
पीटर कॉर्डेस

2
मेरे सिर, getchar के शीर्ष और putchar से है धीमी गति से।
रुई एफ रिबेरो

3
fixके लिए कार्यक्रम के लिए "<<unk>"अभी भी अगर काम नहीं करता है patternवर्णों की एक दोहराया अनुक्रम के साथ शुरू होता है (यानी अगर आप ज़ेबरा साथ एर्डवार्क को बदलने के लिए कोशिश कर रहे थे काम नहीं होगा और आप aaardvak का अपना सहयोग दे पाते हैं, या आप ababc को बदलने के लिए कोशिश कर रहे थे और abababc का इनपुट था)। सामान्य तौर पर आप अपने द्वारा पढ़े गए पात्रों की संख्या से आगे नहीं बढ़ सकते हैं जब तक कि आप यह नहीं जानते कि आपके द्वारा पढ़े जाने वाले पात्रों में मैच शुरू होने की कोई संभावना नहीं है।
इकारस

16

replaceMariadb-server / mysql-server पैकेज में एक उपयोगिता है। यह सरल तार (नियमित अभिव्यक्ति नहीं) को बदल देता है और grep / sed / awk के विपरीत replaceपरवाह नहीं करता है \nऔर \0। मेमोरी की खपत किसी भी इनपुट फ़ाइल (मेरी मशीन पर लगभग 400kb) के साथ स्थिर है।

बेशक आपको उपयोग करने के लिए mysql सर्वर को चलाने की आवश्यकता नहीं है replace, यह केवल फेडोरा में उस तरह से पैक किया गया है। अन्य डिस्ट्रो / ऑपरेटिंग सिस्टम ने इसे अलग से पैक किया हो सकता है।


14

जीएनयू grepआपको "बाइनरी" फाइलों में मैच की ऑफसेट दिखा सकता है, बिना मेमोरी में पूरी लाइनें पढ़ने के। फिर आप ddइस ऑफ़सेट को पढ़ने के लिए उपयोग कर सकते हैं, मैच को छोड़ सकते हैं, फिर फ़ाइल से कॉपी करना जारी रख सकते हैं।

file=...
newfile=...
replace='<raw_unk>'
grep -o -b -a -F '<unk>' <"$file" |
(   pos=0
    while IFS=$IFS: read offset pattern
    do size=${#pattern}
       let skip=offset-pos
       let big=skip/1048576
       let skip=skip-big*1048576
       dd bs=1048576 count=$big <&3
       dd bs=1 count=$skip <&3
       dd bs=1 count=$size of=/dev/null <&3
       printf "%s" "$replace"
       let pos=offset+size
    done
    cat <&3
) 3<"$file" >"$newfile"

गति के लिए, मैंने ddएक बार में 1048576 और 1 बाइट के एक छोटे से पढ़ने को ब्‍लॉकेज के एक बड़े रीड में विभाजित किया है , लेकिन यह ऑपरेशन अभी भी इतनी बड़ी फ़ाइल पर थोड़ा धीमा होगा। grepउत्पादन उदाहरण के लिए, है, 13977:<unk>है, और इस चर में पढ़ने से पेट के पर विभाजित है offsetऔर pattern। हमें इस बात पर नज़र रखनी होगी posकि फ़ाइल से कितने बाइट्स पहले ही कॉपी किए जा चुके हैं।


11

यहां एक और एकल UNIX कमांड लाइन है जो अन्य विकल्पों की तुलना में बेहतर प्रदर्शन कर सकती है, क्योंकि आप एक "ब्लॉक आकार" के लिए "शिकार" कर सकते हैं जो अच्छा प्रदर्शन करता है। इसके लिए मजबूत होने के लिए आपको यह जानना होगा कि आपके पास प्रत्येक X वर्णों में कम से कम एक स्थान है, जहाँ X आपका मनमाना "ब्लॉक आकार" है। नीचे दिए गए उदाहरण में मैंने 1024 अक्षरों का "ब्लॉक आकार" चुना है।

fold -w 1024 -s corpus.txt | sed 's/<unk>/<raw_unk>/g' | tr '/n' '/0'

यहां, गुना 1024 बाइट्स तक ले जाएगा , लेकिन -s यह सुनिश्चित करता है कि यह अंतिम ब्रेक के बाद से कम से कम एक जगह पर टूट जाता है।

Sed कमांड आपकी है और जो आप उम्मीद करते हैं वह करता है।

फिर tr कमांड उन नईलाइन्स को "अनफोल्ड" कर देगा जो उन नईलाइन्स को परिवर्तित करती हैं जिन्हें कुछ भी नहीं डाला गया था।

यदि आप तेजी से प्रदर्शन करते हैं तो आपको यह देखने के लिए बड़े ब्लॉक आकारों को आज़माने पर विचार करना चाहिए। 1024 के बजाय, आप -w के तह विकल्प के लिए 10240 और 102400 और 1048576 की कोशिश कर सकते हैं।

यहां प्रत्येक चरण द्वारा एक उदाहरण को तोड़ा गया है जो सभी N को लोअरकेस में परिवर्तित करता है:

[root@alpha ~]# cat mailtest.txt
test XJS C4JD QADN1 NSBN3 2IDNEN GTUBE STANDARD ANTI UBE-TEST EMAIL*C.34X test

[root@alpha ~]# fold -w 20 -s mailtest.txt
test XJS C4JD QADN1
NSBN3 2IDNEN GTUBE
STANDARD ANTI
UBE-TEST
EMAIL*C.34X test

[root@alpha ~]# fold -w 20 -s mailtest.txt | sed 's/N/n/g'
test XJS C4JD QADn1
nSBn3 2IDnEn GTUBE
STAnDARD AnTI
UBE-TEST
EMAIL*C.34X test

[root@alpha ~]# fold -w 20 -s mailtest.txt | sed 's/N/n/g' | tr '\n' '\0'
test XJS C4JD QADn1 nSBn3 2IDnEn GTUBE STAnDARD AnTI UBE-TEST EMAIL*C.34X test

यदि आपके पास एक है तो आपको फ़ाइल के बहुत अंत में एक नई पंक्ति जोड़ने की आवश्यकता होगी, क्योंकि tr कमांड इसे हटा देगा।


1
आप यह कैसे सुनिश्चित कर सकते हैं कि आप किन मामलों में पैटर्न को नहीं तोड़ रहे हैं, जहां पर्याप्त व्हाट्सएप उपलब्ध नहीं है?
15

1
जैसा कि कहा गया है, इसके लिए मजबूत होना एक आवश्यकता है कि हर X अक्षर में कम से कम एक स्थान हो। आप उस विश्लेषण को आसानी से कर सकते हैं, आपके द्वारा चुने गए किसी भी ब्लॉक के साथ: fold -w X mailtest.txt | grep -v "" | wc -l यह वह संख्या है जो संभावित बढ़त के मामलों के साथ मुड़ी हुई लाइनों की संख्या है। यदि यह शून्य है, तो समाधान काम करने की गारंटी है।
अल्फरेमा

10

का उपयोग करते हुए perl

अपने बफ़र्स का प्रबंधन करना

आप उपयोग कर सकते हैं IO::Handleकी setvbufडिफ़ॉल्ट बफ़र्स प्रबंधन करने के लिए, या आप के साथ अपने स्वयं बफ़र्स प्रबंधन कर सकते हैं sysreadऔर syswrite। जाँच करें perldoc -f sysreadऔर perldoc -f syswriteअधिक जानकारी के लिए, अनिवार्य रूप से वे बफ़र किए गए io को छोड़ देते हैं।

यहां हम अपने स्वयं के बफर IO को रोल करते हैं, लेकिन हम इसे मैन्युअल रूप से और 1024 बाइट्स पर मनमाने ढंग से करते हैं। हम आरडब्ल्यू के लिए फाइल भी खोलते हैं इसलिए हम एक ही एफएच पर यह सब करते हैं।

use strict;
use warnings;
use Fcntl qw(:flock O_RDWR);
use autodie;
use bytes;

use constant CHUNK_SIZE => 1024 * 32;

sysopen my $fh, 'file', O_RDWR;
flock($fh, LOCK_EX);

my $chunk = 1;
while ( sysread $fh, my $bytes, CHUNK_SIZE * $chunk ) {
  if ( $bytes =~ s/<unk>/<raw_unk>/g ) {
    seek( $fh, ($chunk-1)* CHUNK_SIZE, 0 );
    syswrite( $fh, $bytes, 1024);
    seek( $fh, $chunk * CHUNK_SIZE, 0 );
  }
  $chunk++;
}

यदि आप इस मार्ग पर जाने वाले हैं

  1. सुनिश्चित करें <unk>और <raw_unk>एक ही बाइट आकार हैं।
  2. CHUNKSIZEयदि आप 1 बाइट से अधिक की जगह ले रहे हैं, तो आप यह सुनिश्चित कर सकते हैं कि हमारी बफर विधि सीमा पार नहीं करती है ।

2
क्या होगा अगर <unk>चूजों के बीच एक सीमा पर गिरता है?
लियोरी

8

आप bbe ( बाइनरी ब्लॉक एडिटर ), " sedबाइनरी फ़ाइलों के लिए " कोशिश कर सकते हैं ।

मुझे 7GB टेक्स्ट फाइल पर इसका उपयोग करने में अच्छी सफलता मिली EOL, जिसमें कोई भी वर्ण नहीं है , एक स्ट्रिंग की कई घटनाओं को अलग-अलग लंबाई के साथ। किसी भी अनुकूलन का प्रयास किए बिना इसने> 50 एमबी / एस के औसत प्रसंस्करण थ्रूपुट दिया।


5

इसके साथ perl, आप निश्चित लंबाई रिकॉर्ड के साथ काम कर सकते हैं जैसे:

perl -pe 'BEGIN{$/=\1e8}
          s/<unk>/<raw_unk>/g' < corpus.txt > corpus.txt.new

और आशा है कि <unk>उन 100 एमबी रिकॉर्ड्स में से दो में फैले नहीं होंगे ।


मैं भी इस विधि के बारे में सोच रहा था, लेकिन while read -N 1000 chunk;( 1000उदाहरण के रूप में उठाया गया) का उपयोग कर रहा था । <unk>चंक्स के बीच टूटा हुआ, के लिए समाधान फ़ाइल के माध्यम से दो पास है: पहला 100 एमबी चंक्स के साथ और दूसरा '100 एमबी + 5 बाइट' विखंडू के साथ। लेकिन यह 70GB फ़ाइल के मामले में इष्टतम समाधान नहीं है।
मिनीमैक्स

3
आपको दो पास की भी आवश्यकता नहीं है। ब्लॉक ए पढ़ें। ईओएफ नहीं है, तो ब्लॉक बी पढ़ें। ए / बी में बदलें / बदलें। ए: = बी लूप। जटिलता सुनिश्चित कर रही है कि आप प्रतिस्थापन के अंदर प्रतिस्थापित न करें।
रोज़ा

@MiniMax, कि दूसरी पास जरूरी मदद नहीं करेगा क्योंकि पहली घटना में प्रत्येक घटना के लिए 5 बाइट्स जोड़े गए होंगे <unk>
स्टीफन चेज़लस

1
@roaima, हाँ यह एक बहुत अधिक शामिल समाधान होगा। यहाँ यह एक सरल दृष्टिकोण है जो केवल अत्यधिक संभावित है (यह मानते हुए कि <unk>घटनाएँ सही हैं, यदि नहीं, तो उपयोग करें $/ = ">"और s/<unk>\z/<raw_unk>/g) सही होने की।
स्टीफन चेजेलस

5

यहाँ एक छोटा गो कार्यक्रम है जो कार्य करता है ( unk.go):

package main

import (
    "bufio"
    "fmt"
    "log"
    "os"
)

func main() {
    const (
        pattern     = "<unk>"
        replacement = "<raw_unk>"
    )
    var match int
    var char rune
    scanner := bufio.NewScanner(os.Stdin)
    scanner.Split(bufio.ScanRunes)
    for scanner.Scan() {
        char = rune(scanner.Text()[0])
        if char == []rune(pattern)[match] {
            match++
            if match == len(pattern) {
                fmt.Print(replacement)
                match = 0
            }
        } else {
            if match > 0 {
                fmt.Print(string(pattern[:match]))
                match = 0
            }
            if char == rune(pattern[0]) {
                match = 1
            } else {
                fmt.Print(string(char))
            }
        }
    }
    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
}

बस इसे बनाने go build unk.goऔर इसे चलाने के रूप में ./unk <input >output

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

क्षमा करें, मैंने नहीं पढ़ा कि सब कुछ एक पंक्ति में है, इसलिए मैंने अब चरित्र द्वारा फ़ाइल वर्ण को पढ़ने की कोशिश की।

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

सी कार्यक्रम के रूप में एक ही तय लागू।


1
क्या यह पूरी फ़ाइल को मेमोरी में पढ़ने से रोकता है?
बिल्ली

1
यह चरित्र द्वारा फ़ाइल चरित्र को पढ़ता है और कभी भी पूरी फ़ाइल को मेमोरी में नहीं रखता है , केवल व्यक्तिगत वर्ण।
पैट्रिक बुचर

1
scanner.Split(bufio.ScanRunes)जादू करता है।
पैट्रिक बुचर

go doc bufio.MaxScanTokenSizeडिफ़ॉल्ट बफर आकार की भी जांच करें ।
पैट्रिक बुचर

आपके Cकार्यक्रम की तरह , यह एर्डवार्क को ज़ेब्रा के साथ एवर्दार्क के इनपुट के साथ बदलने के लिए काम नहीं करता है।
इकारस

1

यह 70GB फ़ाइल और सरल खोज और प्रतिस्थापित करने के लिए ओवरकिल हो सकता है, लेकिन Hadoop MapReduce ढांचा आपकी समस्या को अभी किसी भी कीमत पर हल करेगा (स्थानीय रूप से इसे चलाने के लिए इसे सेट करते समय 'सिंगल नोड' विकल्प चुनें) - और हो सकता है अपने कोड को संशोधित करने की आवश्यकता के बिना भविष्य में अनंत क्षमता तक पहुंचा।

Https://hadoop.apache.org/docs/stable/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html पर आधिकारिक ट्यूटोरियल (अत्यंत सरल) जावा का उपयोग करता है, लेकिन आप पर्ल या के लिए क्लाइंट लाइब्रेरी पा सकते हैं। जो भी भाषा आपको प्रयोग करने का मन करे।

इसलिए यदि बाद में आपको पता चलता है कि आप 7000GB टेक्स्ट फ़ाइलों पर अधिक जटिल ऑपरेशन कर रहे हैं - और प्रति दिन 100 बार ऐसा करने पर - आप कई नोड्स में वर्कलोड वितरित कर सकते हैं जो आप प्रावधान करते हैं या जो स्वचालित रूप से आपके लिए क्लाउड द्वारा प्रावधानित हैं- Hadoop क्लस्टर आधारित है।


1
हाँ हाँ यह है। "Hadoop का उपयोग न करें - आपका डेटा इतना बड़ा नहीं है" । यह एक बहुत ही सरल स्ट्रीमिंग IO समस्या है।
sourcejedi

0

पिछले सभी सुझावों में संपूर्ण फ़ाइल को पढ़ने और संपूर्ण फ़ाइल को लिखने की आवश्यकता है। यह न केवल एक लंबा समय लेता है, बल्कि 70GB मुक्त स्थान की भी आवश्यकता होती है।

1) अगर मैं तुम्हें विशेष मामले को समझने के लिए सही ढंग से यह <UNK> वही लंबाई के कुछ अन्य तार के साथ बदलने के लिए स्वीकार्य होगा?

2a) क्या कई घटनाएं हैं? 2 बी) यदि आप जानते हैं कि कितने हैं?

मुझे यकीन है कि आपने इस वर्ष-प्लस समस्या को पहले ही हल कर लिया है और मैं जानना चाहता हूं कि आपने किस समाधान का उपयोग किया है।

मैं एक समाधान (सी में सबसे अधिक संभावना है) का प्रस्ताव दूंगा जो फ़ाइल के BLOCKS को पढ़ेगा, जिसमें से प्रत्येक के लिए स्ट्रिंग को ध्यान में रखते हुए क्रॉसिंग के लिए खोज की जाएगी। एक बार एक ही लंबाई के साथ स्ट्रिंग की जगह पाया वैकल्पिक और केवल लिखने कि ब्लॉक। घटनाओं की ज्ञात संख्या के लिए जारी या फ़ाइल के अंत तक। इसके लिए कम-से-कम संख्याएँ लिखने की आवश्यकता होगी और अधिक से अधिक दो बार (यदि प्रत्येक घटना को 2 ब्लॉकों के बीच विभाजित किया गया था)। इसके लिए कोई अतिरिक्त स्थान की आवश्यकता नहीं होगी!


-1

यदि हमारे पास न्यूनतम राशि है <unk>(जैसा कि जिपफ के कानून द्वारा अपेक्षित है),

awk -v RS="<unk>" -v ORS="<raw_unk>" 1

1
नहीं sed, स्मृति में एक समय पर एक पंक्ति पढ़ता है, भले ही। यह इस लाइन को फिट नहीं कर पाएगा।
कुसलानंद

1
मुझे कोई दस्तावेज नहीं मिल रहा है जो कहता है कि इस ध्वज का उपयोग करते समय GNU के अलावा कुछ भी sedइनपुट / आउटपुट बफरिंग नहीं करेगा । मैं यह नहीं देख सकता कि यह आंशिक लाइनें पढ़ेगी।
Kusalananda
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.