स्ट्रिंग प्रतिस्थापन के लिए गैर-लाइन उन्मुख उपकरण?


13

मैंने हाल ही में एक सवाल पूछा था कि अगर किसी अन्य विशिष्ट चरित्र के बाद न्यूलाइन वर्ण को कैसे हटाया जाए।

यूनिक्स टेक्स्ट-प्रोसेसिंग टूल बहुत शक्तिशाली हैं, लेकिन उनमें से लगभग सभी टेक्स्ट की लाइनों से निपटते हैं, जो कि उस समय ठीक होता है जब इनपुट उपलब्ध मेमोरी में फिट बैठता है।

लेकिन मुझे क्या करना चाहिए अगर मैं एक बड़ी फ़ाइल में एक पाठ अनुक्रम को बदलना चाहता हूं जिसमें कोई नया अंक नहीं है?

उदाहरण के लिए बदलने के <foobar>साथ \n<foobar>इनपुट पंक्ति-दर-पंक्ति पढ़ने के बिना? (चूंकि केवल एक ही रेखा है और यह 2.5G वर्ण लंबा है)।


1
क्या आप उपयोग करने के लिए खुले हैं perlया python?
इरिवर

पर्ल ठीक है। मुझे अभी-अभी gsar( home.online.no/~tjaberg ) मिला, जो मैं कोशिश करूँगा।
मैटबियानको

जवाबों:


12

इस तरह की समस्या का सामना करते समय मेरे लिए पहली बात यह है कि रिकॉर्ड विभाजक को बदलना है। अधिकांश टूल में, यह \nडिफ़ॉल्ट रूप से सेट होता है लेकिन इसे बदला जा सकता है। उदाहरण के लिए:

  1. पर्ल

    perl -0x3E -pe 's/<foobar>/\n$&/' file
    

    व्याख्या

    • -0: यह एक हेक्साडेसिमल मान को दिए गए इनपुट रिकॉर्ड विभाजक को एक वर्ण में सेट करता है । इस मामले में, मैं इसे निर्धारित कर रहा हूं कि >हेक्स मान किसका है 3E। सामान्य प्रारूप है -0xHEX_VALUE। यह सिर्फ प्रबंधनीय विखंडू में लाइन को तोड़ने के लिए एक चाल है।
    • -pe: द्वारा दी गई स्क्रिप्ट को लागू करने के बाद प्रत्येक इनपुट लाइन को प्रिंट करें -e
    • s/<foobar>/\n$&/: एक साधारण प्रतिस्थापन। $&जो कुछ भी मिलान किया गया था, इस मामले में है <foobar>
  2. awk

    awk '{gsub(/foobar>/,"\n<foobar>");printf "%s",$0};' RS="<" file
    

    व्याख्या

    • RS="<": इनपुट रिकॉर्ड विभाजक को सेट करें >
    • gsub(/foobar>/,"\n<foobar>"): के सभी मामलों स्थानापन्न foobar>के साथ \n<foobar>। ध्यान दें कि क्योंकि RSसेट कर दिया गया है <, सभी <को इनपुट फ़ाइल से हटा दिया जाता है (यह है कि कैसे awkकाम करता है) इसलिए हमें foobar>(बिना <) के मिलान करने और बदलने की आवश्यकता है \n<foobar>
    • printf "%s",$0: प्रतिस्थापन के बाद वर्तमान "लाइन" प्रिंट करें। $0वर्तमान रिकॉर्ड है, awkइसलिए यह जो कुछ भी पहले था धारण करेगा <

मैंने 2.3 GB पर इन कमांड के साथ बनाई गई सिंगल-लाइन फ़ाइल का परीक्षण किया:

for i in {1..900000}; do printf "blah blah <foobar>blah blah"; done > file
for i in {1..100}; do cat file >> file1; done
mv file1 file

दोनों awkऔर perlस्मृति की नगण्य मात्रा का इस्तेमाल किया।


क्या आपने कभी Tie::File perldoc.perl.org/Tie/File.html की कोशिश की है । मुझे लगता है कि Perlजब यह बड़ी फ़ाइलों के साथ काम करता है तो यह सबसे अच्छी सुविधाएँ होती हैं।
cuonglm

@Gnouc मैंने इसके साथ थोड़ा, हाँ खेला है। लेकिन i) ओपी ने पहले से ही एक और प्रश्न में पर्ल के प्रति अरुचिकर व्यवहार किया है, इसलिए मैं इसे सरल रखना चाहता था ii) मैं बाहरी मॉड्यूल का उपयोग करने से बचना चाहता हूं जब तक कि पूरी तरह से और iii) टाई का उपयोग न करें :: फ़ाइल मॉड्यूल वाक्यविन्यास को काफी कम कर देगा स्पष्ट।
terdon

इस बात से सहमत। एक छोटा नोट जो Tie::Fileएक कोर मॉड्यूल है v5.7.3
congonglm

9

gsar (सामान्य खोज और प्रतिस्थापित) वास्तव में इस उद्देश्य के लिए एक बहुत ही उपयोगी उपकरण है।

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

कई मामलों में यह बहुत ठीक है और यहां तक ​​कि पठनीय है। मैं समस्याओं है कि आसानी से किया जा सकता है / कुशलता से इस तरह के रूप में हर जगह-उपलब्ध उपकरणों के साथ हल पसंद करते हैं awk, tr, sedऔर बॉर्न शैल।

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

आप में से कुछ सोच सकते हैं कि यह धोखा है, लेकिन मैं नहीं देखता कि नौकरी के लिए सही उपकरण का उपयोग करना गलत कैसे हो सकता है। इस मामले में यह एक सी प्रोग्राम है जिसे जीपीएल v2 केgsar तहत लाइसेंस प्राप्त किया जाता है , इसलिए यह मुझे काफी हैरान करता है कि न तो जेंटू , रेडहैट और न ही ubuntu में इस बहुत उपयोगी उपकरण के लिए कोई पैकेज है ।

gsarबॉयर-मूर स्ट्रिंग खोज एल्गोरिथ्म के एक द्विआधारी संस्करण का उपयोग करता है ।

उपयोग सीधे-आगे है:

gsar -F '-s<foobar>' '-r:x0A<foobar>'

जहां -F"फ़िल्टर" मोड का अर्थ है, अर्थात पढ़ने के लिए stdinलिखें stdout। फाइलों पर भी काम करने के तरीके हैं। -sखोज स्ट्रिंग और -rप्रतिस्थापन निर्दिष्ट करता है । बृहदान्त्र-संकेतन का उपयोग मनमाने बाइट मूल्यों को निर्दिष्ट करने के लिए किया जा सकता है।

केस-असंवेदनशील मोड समर्थित है ( -i), लेकिन नियमित अभिव्यक्ति के लिए कोई समर्थन नहीं है, क्योंकि एल्गोरिथ्म खोज का अनुकूलन करने के लिए खोज स्ट्रिंग की लंबाई का उपयोग करता है।

उपकरण का उपयोग केवल खोज के लिए किया जा सकता है, थोड़ा सा grepgsar -bमिलान किए गए खोज स्ट्रिंग के बाइट ऑफ़सेट को आउटपुट करता है, और gsar -lयदि कोई संयोजन है, तो फ़ाइल नाम और मिलान की संख्या प्रिंट grep -lकरता है wc

टूल को टारमॉड टाबर्ग (प्रारंभिक) और हंस पीटर वर्ने (सुधार) द्वारा लिखा गया था ।


अगर यह GPL'd है तो आप इसे एक distro के लिए पैकेजिंग पर विचार करेंगे :)
Rqomey

1
वास्तव में मैं इसके लिए एक जेंटू पुनर्निर्माण बनाने के बारे में गंभीरता से सोच रहा हूं। शायद एक आरपीएम भी। लेकिन मैंने पहले कभी भी .deb पैकेज का निर्माण नहीं किया है, इसलिए मुझे आशा है कि कोई मुझे इसके लिए ले जाएगा (क्योंकि इसमें मुझे कुछ समय लगेगा)।
मैटबियनको

मुझे संदेह है कि यह बहुत सांत्वना है लेकिन ओएस एक्स के होमब्रेव के लिए सूत्र है gsar
crazysim

5

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

यहाँ एक पायथन उदाहरण दिया गया है, foobarजिसके साथ प्रतिस्थापित होता हैXXXXXX

#! /usr/bin/python
import mmap
import contextlib   
with open('test.file', 'r+') as f:
 with contextlib.closing(mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_WRITE)) as m:
   pos = 0
   pos = m.find('foobar', pos)
   while pos > 0:
    m[pos: pos+len('XXXXXX')] = 'XXXXXX'
    pos = m.find('foobar', pos)

4

इसके लिए कई उपकरण हैं:

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

tr -dc '[:graph:]' </dev/urandom | dd bs=32 count=1 cbs=8 conv=unblock,sync 2>/dev/null

###OUTPUT###

UI(#Q5\e BKX2?A:Z RAxGm:qv t!;/v!)N

मैं trऊपर भी उपयोग करता हूं क्योंकि यह किसी भी ASCII बाइट को किसी अन्य में परिवर्तित करने में सक्षम हो सकता है (या, इस मामले में, किसी भी ASCII बाइट को हटाने के लिए जो एक नहीं-जगह मुद्रण योग्य चरित्र नहीं है)। यह वही है जो मैंने आज सुबह आपके अन्य प्रश्न के उत्तर में इस्तेमाल किया है, वास्तव में, जब मैंने किया था:

tr '>\n' '\n>' | sed 's/^>*//' | tr '\n>' '>\n' 

कर रहे हैं इसी तरह के कई । उस सूची को एक न्यूनतम सामान्य-भाजक सबसेट प्रदान करना चाहिए, जिसके साथ आप परिचित हो सकते हैं।

लेकिन, अगर मैं द्विआधारी फ़ाइल के 2.5 ग्राम पर पाठ प्रसंस्करण करने जा रहा था, तो मैं इसके साथ शुरू कर सकता हूं od। यह आपको octal dumpया कई अन्य स्वरूपों में से एक दे सकता है। आप सभी प्रकार के विकल्पों को निर्दिष्ट कर सकते हैं - लेकिन मैं \Cबच गए प्रारूप में प्रति पंक्ति केवल एक बाइट करूंगा :

आपके द्वारा प्राप्त किया गया डेटा आपके द्वारा odनिर्दिष्ट किए गए अंतराल पर नियमित होगा - जैसा कि मैं नीचे दिखाता हूं। लेकिन पहले - यहाँ आपके प्रश्न का उत्तर है:

printf 'first\nnewline\ttab spacefoobar\0null' |
od -A n -t c -v -w1 |
sed 's/^ \{1,3\}//;s/\\$/&&/;/ /bd
     /\\[0nt]/!{H;$!d};{:d
    x;s/\n//g}'

पर delimits ऊपर यही कारण है कि छोटा सा \newlines, \0Nulls, \tपेट और <spaces>जबकि संरक्षण \Cसीमांकक के लिए भाग निकले स्ट्रिंग। नोट Hऔर xउपयोग किए गए फ़ंक्शंस - हर बार sedएक सीमांकक का सामना करता है जो इसकी मेमोरी बफ़र्स की सामग्री को स्वैप करता है। इस तरह से यह sedकेवल उतनी ही जानकारी रखता है जितना कि फ़ाइल को मज़बूती से जमा करना चाहिए और ओवररन को बफर नहीं करना चाहिए - ऐसा नहीं करता है, इसलिए जब तक यह वास्तव में अपने सीमांकक का सामना नहीं करता है। इतने लंबे समय के रूप में यह होता है के लिए, sedअपने इनपुट प्रक्रिया जारी रहेगी और odयह प्रदान करने के लिए जब तक यह मुठभेड़ों के लिए जारी रहेगा EOF

जैसा है, इसका आउटपुट इस तरह दिखता है:

first
\nnewline
\ttab
 spacefoobar
\0null

तो अगर मैं चाहता हूँ foobar:

printf ... | od ... | sed ... | 
sed 's/foobar/\
&\
/g'

###OUTPUT###

first
\nnewline
\ttab
 space
foobar

\0null

अब अगर आप Cबच के उपयोग करना चाहते हैं तो यह बहुत आसान है - क्योंकि sedपहले से ही डबल \\बैकस्लैश अपने सभी सिंगल इनपुट बैकस्लैश से बच गया है, इसलिए printfनिष्पादित से xargsआपके विनिर्देशन में आउटपुट उत्पन्न करने में कोई समस्या नहीं होगी। लेकिन xargs शेल उद्धरण खाता है, इसलिए आपको इसे फिर से डबल करने की आवश्यकता होगी:

printf 'nl\ntab\tspace foobarfoobar\0null' |
PIPELINE |
sed 's/./\\&/g' | 
xargs printf %b | 
cat -A

###OUTPUT###

nl$
tab^Ispace $
foobar$
$
foobar$
^@null%

यह आसानी से एक शेल चर और उत्पादन में बाद में समान फैशन में बचाया जा सकता था। अंतिम अपने इनपुट में प्रत्येक वर्ण से पहले sedएक \बैकस्लैश सम्मिलित करता है , और यह सब है।

और यहाँ यह सब ऐसा लगता है जैसे पहले कभी sedपकड़ लेता है:

printf 'nl\ntab\tspace foobarfoobar\0null' |
od -A n -t c -v -w1

   n
   l
  \n
   t
   a
   b
  \t
   s
   p
   a
   c
   e

   f
   o
   o
   b
   a
   r
   f
   o
   o
   b
   a
   r
  \0
   n
   u
   l
   l

2

अक्क लगातार रिकॉर्ड पर काम करता है। यह रिकॉर्ड विभाजक के रूप में किसी भी चरित्र का उपयोग कर सकता है (कई कार्यान्वयन पर अशक्त बाइट को छोड़कर)। कुछ कार्यान्वयन रिकॉर्ड विभाजक के रूप में मनमाने नियमित अभिव्यक्ति (खाली स्ट्रिंग से मेल नहीं खा रहे) का समर्थन करते हैं, लेकिन यह अस्पष्ट हो सकता है क्योंकि रिकॉर्ड विभाजक को रिकॉर्ड करने से पहले प्रत्येक रिकॉर्ड के अंत से छोटा किया जाता है $0(GNU awk चर RTको रिकॉर्ड विभाजक में सेट करता है वह वर्तमान रिकॉर्ड के अंत से छीन लिया गया था)। ध्यान दें कि printआउटपुट रिकॉर्ड विभाजक के साथ इसके आउटपुट को समाप्त करता है ORSजो डिफ़ॉल्ट रूप से एक नई लाइन है और इनपुट रिकॉर्ड विभाजक से स्वतंत्र रूप से सेट किया गया है RS

awk -v RS=, 'NR==1 {printf "input up to the first comma: %s\n", $0}'

आप प्रभावी रूप से अन्य उपकरणों (के लिए रिकॉर्ड विभाजक के रूप में एक अलग चरित्र का चयन कर सकते sort, sedसाथ कि चरित्र के साथ नई-पंक्तियों को स्वैप करके, ...) tr

tr '\n,' ',\n' |
sed 's/foo/bar/' |
sort |
tr '\n,' ',\n'

कई GNU टेक्स्ट यूटिलिटीज विभाजक के रूप में एक नई लाइन के बजाय एक अशक्त बाइट का उपयोग करते हैं।

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