फ़ाइल को लाइन नंबर द्वारा फ़िल्टर करें


17

प्रति पंक्ति और पाठ फ़ाइल F के एक गैर-नकारात्मक पूर्णांक के साथ एक फ़ाइल L को देखते हुए, केवल उन पंक्तियों को F में रखने का एक तेज़ तरीका क्या होगा, जिनकी लाइन संख्या फ़ाइल L में दिखाई देती है?

उदाहरण:

$ cat L.txt
1
3

$ cat F.txt
Hello World
Hallo Welt
Hola mundo

$ command-in-question -x L.txt F.txt
Hello World
Hola mundo

मैं एक कमांड की तलाश कर रहा हूं जो 500 मिलियन या अधिक प्रविष्टियों के साथ एक फ़ाइल एल को संभाल सकता है; फ़ाइल L को संख्यात्मक रूप से क्रमबद्ध किया गया है।

नोट: मैं एक कार्यान्वयन के लिए आधे रास्ते में command-in-questionहूं, लेकिन मैं अभी आश्चर्यचकित हूं कि क्या कोई यहां कुछ यूनिक्स उपकरणों का उपयोग करने में सक्षम हो सकता है।


अद्यतन: सभी उत्तर के लिए धन्यवाद, मैंने आज बहुत कुछ सीखा! मैं एक से अधिक उत्तर स्वीकार करना चाहूंगा, लेकिन यह संभव नहीं है।


फ़ाइल F में कितनी लाइनें हैं?
पीटर।

1
F में L की तरह कम से कम कई पंक्तियाँ हैं
miku

जवाबों:


8

Cसार्थक त्रुटि संदेशों को छोड़ने के साथ :

#include <stdio.h>
#include <stdlib.h>

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

    FILE *L;
    FILE *F;

    unsigned int to_print;
    unsigned int current = 0;
    char *line = NULL;
    size_t len = 0;

    if ((L = fopen(argv[1], "r")) == NULL) {
        return 1;
    } else if ((F = fopen(argv[2], "r")) == NULL) {
        fclose(L);
        return 1;
    } else {

        while (fscanf(L, "%u", &to_print) > 0) {
            while (getline(&line, &len, F) != -1 && ++current != to_print);
            if (current == to_print) {
                printf("%s", line);
            }
        }

        free(line);
        fclose(L);
        fclose(F);
        return 0;
    }
}

2
यह यहां सबसे अधिक जवाब देने वाला उत्तर है। कम से कम, मेरे परीक्षणों से ऐसा है। में मामला किसी को दिलचस्पी है, मैं इसे पसंद संकलित: xsel -bo | cc -xc - -o cselect। और यह सिर्फ काम किया - यह केवल दो libs की जरूरत है।
माइकस

1
धन्यवाद, यह बहुत अच्छा है! मुझे आशा है कि आपको कोई आपत्ति नहीं है, लेकिन मैंने आपके कोड को एक छोटे टूल में लपेट दिया है ।
मिकू

1
@ मीकू आगे बढ़ो, मुझे खुशी है कि मैं मदद कर सका। मैंने देखा कि आप LINE_MAXअपने संस्करण में बढ़ गए हैं, इसलिए आप अपनी फ़ाइलों में बहुत बड़ी लाइनों के साथ काम करते हैं। मैंने getline()लाइन आकार सीमा को हटाने के लिए एक संस्करण के साथ ए को अपडेट किया है।
फ्लोइम खुद

@ पूरी तरह से, ठीक है, फिर से धन्यवाद:) वास्तव में, कुछ इनपुट लाइनें अधिक हो सकती हैं LINE_MAX, इसलिए getlineयह सही लगता है।
मिकू

10

मैं उपयोग करूंगा awk, लेकिन मेमोरी की पूरी सामग्री को स्टोर नहीं करूंगा L.txtऔर अनावश्यक हैश लुक अप ;-) करूंगा।

list=L.txt file=F.txt
LIST="$list" awk '
  function nextline() {
    if ((getline n < list) <=0) exit
  }
  BEGIN{
    list = ENVIRON["LIST"]
    nextline()
  }
  NR == n {
    print
    nextline()
  }' < "$file"

वास्तव में, मैंने हैश-मैप्स की कोशिश की और वे स्मृति को पार कर जाएंगे; बिटसेट्स आपको अधिक हेडरूम खरीदेंगे; लेकिन इस तथ्य का उपयोग करके, कि इनपुट को क्रमबद्ध किया गया है, आप पूरी तरह से इस (स्थान) समस्या से छुटकारा पा सकते हैं।
मिकू

1
@Janis; ऐसा नहीं है कि सिर्फ मानक अच्छा कोडिंग अभ्यास का मामला है: हार्ड कोड शाब्दिक मत बनो - बजाय चर का उपयोग करें ... (अधिक लचीला और कम त्रुटि प्रवण, और बनाए रखने में आसान)
पीटर।

1
@ स्टीफनचेज़लस: इसे प्री-लूप इनिशियलाइज़ेशन की जरूरत है n, अन्यथा (जैसे-जैसे) इसमें चूक होती 1हैL.txt
पीटरो

1
@ पीटर.ओ। उफ़, यह वही है जिसे मैंने NR> = n के साथ संबोधित करने की कोशिश की थी, लेकिन यह गलत था। अब बेहतर होना चाहिए।
स्टीफन चेज़लस

1
@ जानिस, विचार यह था कि यदि उस कोड को किसी command-in-questionस्क्रिप्ट में एम्बेड किया जाना था , तो आप कोड में फ़ाइल नाम नहीं डाल सकते। -v list="$opt_x"बैकस्लैश-प्रिसेसिंग की वजह से या तो काम नहीं करता है, जो उस पर जागता है। यही कारण है कि मैं यहाँ के बजाय ENVIRON का उपयोग करता हूं।
स्टीफन चेज़लस

10

grep -n | sort | sed | cut

(   export LC_ALL=C
    grep -n ''   | sort -t:  -nmk1,1 ./L - |
    sed /:/d\;n  | cut  -sd: -f2-
)   <./F

किसी भी आकार के इनपुट के साथ बहुत जल्दी काम करना चाहिए (कुछ समयबद्ध परीक्षण नीचे शामिल हैं) । कुछ नोट्स कैसे:

  • export LC_ALL=C
    • क्योंकि निम्न ऑपरेशन की बात यह है कि ./Fइसकी ./Lलाइनिनो की फाइल के साथ स्टैक्ड अप इनलाइन की पूरी फाइल प्राप्त करना है , केवल उन पात्रों के बारे में जिन्हें हमें चिंता करने की आवश्यकता होगी ASCII [0-9]अंक और :कोलन।
    • इस कारण से यह अधिक सरल है कि यूटीएफ -8 अन्यथा शामिल होने की तुलना में 128 कब्जे के सेट में उन 11 पात्रों को खोजने के बारे में चिंता करना।
  • grep -n ''
    • यह स्ट्रिंग LINENO:को स्टाइन में हर पंक्ति के सिर में सम्मिलित करता है - या <./F
  • sort -t: -nmk1,1 ./L -
    • sortअपनी इनपुट फाइलों को क्रमबद्ध करने के लिए उपेक्षा करता है, और इसके बजाय (सही ढंग से) मान लिया जाता है कि वे हैं और क्रमबद्ध क्रम -mमें उन्हें मिटा देता है -numerically, वैसे भी मूल रूप से किसी भी संभावित -k1,1सेंट -t:बृहदान्त्र चरित्र से परे कुछ भी अनदेखा कर रहा है।
    • हालांकि ऐसा करने के लिए कुछ अस्थायी स्थान की आवश्यकता हो सकती है (यह निर्भर करता है कि कुछ क्रम कितने अलग हो सकते हैं) , इसे उचित प्रकार की तुलना में बहुत अधिक आवश्यकता नहीं होगी, और यह बहुत तेज़ होगा क्योंकि इसमें शून्य बैकट्रैकिंग शामिल है।
    • sortएक एकल स्ट्रीम का उत्पादन करेगा जहां किसी भी लिनेनो में ./Lतुरंत संबंधित लाइनों को पूर्ववर्ती किया जाएगा ./F./Lकी लाइनें हमेशा पहले आती हैं क्योंकि वे छोटी होती हैं।
  • sed /:/d\;n
    • यदि वर्तमान लाइन एक /:/बृहदान्त्र से मेल खाती है तो dइसे आउटपुट से हटा दिया जाता है। एल्स, वर्तमान और nएक्सट्रीम लाइन को ऑटो-प्रिंट करें ।
    • और इसलिए sedआलूबुखारा sortकरने के उत्पादन केवल अनुक्रमिक लाइन जोड़े जो एक बृहदान्त्र और निम्न पंक्ति मेल नहीं - से केवल एक लाइन के लिए या, ./Lऔर फिर अगले।
  • cut -sd: -f2-
    • cut -sइसके उन इनपुट लाइनों के आउटपुट से ऊपर की ओर जो कि इसके -d:एलिमिटर स्ट्रिंग्स में से कम से कम एक नहीं होती है - और इसलिए इन ./Lलाइनों को पूरी तरह से काट दिया जाता है।
    • उन पंक्तियों के लिए जो करते हैं, उनका पहला :बृहदान्त्र-सीमांकित -fआईलिड cutदूर होता है - और इसलिए सभी grepलिनेनो के सम्मिलित होते हैं।

छोटे इनपुट परीक्षण

seq 5 | sed -ne'2,3!w /tmp/L
        s/.*/a-z &\& 0-9/p' >/tmp/F

... नमूना इनपुट की 5 लाइनें उत्पन्न करता है। फिर...

(   export LC_ALL=C; </tmp/F \
    grep -n ''   | sort -t:  -nmk1,1 ./L - |
    sed /:/d\;n  | cut  -sd: -f2-
)|  head - /tmp[FL]

... प्रिंट ...

==> standard input <==
a-z 1& 0-9
a-z 4& 0-9
a-z 5& 0-9

==> /tmp/F <==
a-z 1& 0-9
a-z 2& 0-9
a-z 3& 0-9
a-z 4& 0-9
a-z 5& 0-9

==> /tmp/L <==
1
4
5

बड़े समय परीक्षण

मैंने बहुत बड़ी फ़ाइलों का एक जोड़ा बनाया:

seq 5000000 | tee /tmp/F |
sort -R | head -n1500000 |
sort -n >/tmp/L

... जो में 5mil लाइनों /tmp/Fऔर 1.5mil बेतरतीब ढंग से चयनित लाइनों में डाल दिया /tmp/L। मैंने तब किया:

time \
(   export LC_ALL=C
    grep -n ''   | sort -t:  -nmk1,1 ./L - |
    sed /:/d\;n  | cut  -sd: -f2-
)   <./F |wc - l

यह मुद्रित:

1500000
grep -n '' \
    0.82s user 0.05s system 73% cpu 1.185 total
sort -t: -nmk1,1 /tmp/L - \
    0.92s user 0.11s system 86% cpu 1.185 total
sed /:/d\;n \
    1.02s user 0.14s system 98% cpu 1.185 total
cut -sd: -f2- \
    0.79s user 0.17s system 80% cpu 1.184 total
wc -l \
    0.05s user 0.07s system 10% cpu 1.183 total

(मैं वहाँ backslashes जोड़ा)

वर्तमान में यहां दिए गए समाधानों के बीच, यह उन सभी में से सबसे तेज़ है, लेकिन जब मेरी मशीन पर उपर्युक्त डेटासेट के खिलाफ पोज दिया जाता है। दूसरों में से केवल एक ही दूसरे स्थान के लिए चुनाव लड़ने के करीब आया, और वह perl यहाँ meuh है

यह किसी भी तरह से मूल समाधान की पेशकश नहीं है - यह दूसरों द्वारा की पेशकश की सलाह / प्रेरणा के लिए अपने निष्पादन समय का एक तिहाई गिरा दिया है। धीमे समाधान के लिए पोस्ट इतिहास देखें (लेकिन क्यों?)

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

लेकिन सबसे तेज़ समाधान है ...

लेकिन यह सबसे तेज समाधान नहीं है। सबसे तेजी से समाधान यहां प्रस्तुत किया गया है, हाथ से नीचे, सी कार्यक्रम है । मैंने उसे फोन किया cselect। इसे मेरे एक्स क्लिपबोर्ड पर कॉपी करने के बाद, मैंने इसे संकलित किया:

xsel -bo | cc -xc - -o cselect

मैंने तब किया:

time \
    ./cselect /tmp/L /tmp/F |
wc -l

... और परिणाम थे ...

1500000
./cselect /tmp/L /tmp/F  \
    0.50s user 0.05s system 99% cpu 0.551 total
wc -l \
    0.05s user 0.05s system 19% cpu 0.551 total

1
आप इसे तेज कर सकते हैं (मल्टी-कोर सिस्टम पर मेरा जितना जल्दी हो)sed -ne'/:/!{n;p;}' | cut -d: -f2-sed -ne'/:/!N;/\n/s/[^:]*://p'
स्टीफन चेज़लस

@ स्टीफनचेज़ेलस - यदि आप seds स्विच करते हैं तो आपको बेहतर परिणाम मिल सकते हैं - sedमैं जो उपयोग कर रहा हूं वह हीरलोम है sed- आप परिणामों aliasमें मूल्य देख सकते हैं time। वैसे, मेरी विरासत पैकेज, संवैधानिक रूप से एक संगीत परिवाद के खिलाफ संकलित है - रेगेक्स कार्यान्वयन जिसके लिए TRE पर आधारित है । जब मैं इसे जीएनयू में स्विच करता हूं sed- और इसके बिना इसे चलाता हूं cut- यह पूर्ण समय (2.8 सेकंड) के लिए एक पूर्ण सेकंड जोड़ता है - इसे एक तिहाई से अधिक से कम करता है। और वह केवल .3 मेरे सिस्टम पर आपकी तुलना में तेजी से सुरक्षित है।
माइकस

1
sort -mnविरोध के रूप में sort -nmk1,1बेहतर हो सकता है क्योंकि आपको यहां विभाजन करने की आवश्यकता नहीं है (परीक्षण नहीं किया गया है)
स्टीफन चेज़लस

@ स्टीफनचेज़ेलस - हाँ, मैंने ऐसा ही सोचा और मैंने इसे हर तरह से आजमाया। -nकेवल एक लाइन पर पहला न्यूमेरिक स्ट्रिंग करने का अनुमान है, तो मुझे लगा कि ठीक है, -mnया -nm, जिस कारण से कभी-कभी पूरा होने के समय में 2sec से नीचे डूबा हुआ एकमात्र समय था जब मैंने सभी विकल्पों में जोड़ा है। यह अजीब है - और यही कारण है कि कल मैंने -mपहली जगह पर कोई सौदा नहीं किया था - मुझे पता था कि मैं किस बारे में था, लेकिन यह सिर्फ ऑटो-ऑप्टिमाइज़ेशन चीज़ के रूप में काम करना प्रतीत होता था। दिलचस्प बात यह है कि, sort-z-[cm]
हिरलूम

-nलाइन पर पहला संख्यात्मक स्ट्रिंग नहीं है । यह सिर्फ लाइन को एक संख्या के रूप में मानता है, abc 123इसलिए यह 0. होगा इसलिए यह इससे कम कुशल नहीं हो सकता है-t: -k1,1
स्टीफन चेज़लस

9

मैं उपयोग करूँगा awk:

awk 'NR==FNR {a[$1]; next}; FNR in a' L.txt F.txt

अपडेट: मैंने प्रदर्शन के उपाय किए हैं; ऐसा लगता है कि यह संस्करण बहुत बड़े डेटा सेटों (जैसा कि बताई गई आवश्यकताओं के साथ है) के साथ और भी बेहतर है, क्योंकि तुलना बहुत तेज है और हैश तालिका बनाने के लिए आवश्यक प्रयास को ओवरकम्पेन करता है।


1
@miku; हाँ, यह एक अच्छा कॉम्पैक्ट समाधान है। लेकिन एक चेतावनी; सभी awkबड़े डेटा सेटों को संभालने में सक्षम नहीं हो सकते हैं। - मैं जीएनयू का उपयोग कर रहा हूं awkऔर कोई समस्या नहीं है; 500 मिलियन लाइनों के डेटा के साथ परीक्षण को 7 मिनट की आवश्यकता थी।
Janis

1
यह धीमी गति से (तुलना करके) real 16m3.468s- user 15m48.447s- sys 0m10.725s। इसने ५०,०००,००० लाइनों के Lसाथ एक १/१० वें आकार के ३.३ जीबी रैम परीक्षण का उपयोग किया ; और 500,000,000 पंक्तियों के साथ - बनाम स्टीफन चेज़ेलस अवेक एसेर के लिए समय: - - - - मैं एक तेज बॉक्स का उपयोग नहीं कर रहा हूं, लेकिन तुलना दिलचस्प है। Freal 2m11.637suser 2m2.748ssys 0m6.424s
पीटर।

@ Peter.O; डेटा के लिए धन्यवाद! एक धीमी गति की उम्मीद थी, यह देखते हुए कि (मेरे अपने परीक्षण के मामले में) एक साहचर्य सरणी में आधा बिलियन लाइनें संग्रहीत की गई थीं। (यही कारण है कि मैंने स्टीफन के प्रस्ताव के लिए "(+1)" ऊपर टिप्पणी की।) - हालांकि मैं हैरान था कि यह प्रतिसाद समाधान अभी भी प्रति सेकंड 1 मिलियन लाइनों को संसाधित कर रहा था! मुझे लगता है कि यह इस कोड पैटर्न को बनाता है (क्योंकि यह सादगी है!) एक व्यवहार्य विकल्प है, और विशेष रूप से कम चरम डेटा आकारों वाले मामलों में।
Janis

यह निश्चित रूप से एक व्यवहार्य समाधान है। टेस्ट डेटा पर मैंने इस्तेमाल किया (5mil lines / 1.5mil L) आपका 4 सेकंड में थोड़ा सा पूरा हुआ - स्टीफन के उत्तर के केवल एक सेकंड बाद। परीक्षण सेट को जीन करने के लिए उपयोग किया जाने वाला कोड मेरे उत्तर में है, लेकिन यह ज्यादातर केवल seqआउटपुट है और फिर एल में उसी का एक छोटा, यादृच्छिक रूप से चयनित सबसेट है ।
मिकसेर

1
मैंने सिर्फ 500 मिलियन लाइनों के डेटा फ़ाइल आकार और 50 मिलियन की कुंजी फ़ाइल आकार और सम्मान के साथ कुछ और प्रदर्शन उपाय किए हैं। 500 मिलियन लाइनों, एक उल्लेखनीय अवलोकन के साथ। छोटी कुंजी फ़ाइल के साथ समय 4 मिनट (स्टीफ़न) बनाम 8 मिनट (जेनिस) होता है, जबकि बड़ी कुंजी फ़ाइल के साथ यह 19 मिनट (स्टीफ़न) बनाम 12 मिनट (जेनिस) होता है।
Janis

3

बस पूर्णता के लिए: हम स्टीफन चेज़लस द्वारा जवाब में उत्कृष्ट awk स्क्रिप्ट को मर्ज कर सकते हैं, और kos द्वारा उत्तर में पर्ल स्क्रिप्ट, लेकिन पूरी सूची को स्मृति में रखे बिना, इस उम्मीद में कि पर्ल awk से तेज हो सकता है। (मैंने मूल प्रश्न से मिलान करने के लिए आर्ग का क्रम बदल दिया है)।

#!/usr/bin/env perl
use strict;

die "Usage: $0 l f\n" if $#ARGV+1 != 2;
open(L,$ARGV[0]) or die "$ARGV[0]: $!";
open(F,$ARGV[1]) or die "$ARGV[1]: $!";

while(my $number = <L>){
    #chop $number;
    while (<F>) {
        if($. == $number){
            print;
            last;
        }
    }
}

इस तरह से तेजी से है awk। यह मेरा जितना तेज़ है - मैंने दोनों को अभी-अभी तीन बार टेस्ट किया और हर बार मेरा 5 मीटर लाइन का टेस्टसेट 1.8 ... सेकंड और तुम्हारा 1.9 ... सेकंड में हर बार हैंडल किया। टेस्टसेट जनरल कोड मेरे उत्तर में है यदि आप ध्यान रखते हैं, लेकिन मुद्दा यह है कि यह बहुत अच्छा है। क्या अधिक है, आउटपुट सही है - मैं अभी भी awkकाम नहीं कर सकता ... फिर भी, हमारे दोनों जवाब फ्लोटहाइट्स द्वारा शर्म की बात है ।
चाटुकार

@mikeserv, हमारे पास अलग-अलग awks होना चाहिए । आपके नमूने पर, मुझे gawk के साथ 1.4s (Janis के लिए 4s), mawk के साथ 0.9s, इस पर्ल समाधान के साथ 1.7s, 2.3 kos के साथ ', 4.5s आपके (GNU sed), और आपके साथ 1.4 s के साथ मिलेंगे GNU sed) और मेरा सुझाया हुआ सुधार (और C समाधान के लिए 0.5 s)।
स्टीफन चेज़लस

@mikeserv, आह! बेशक आपके दृष्टिकोण के साथ, स्थान में फर्क पड़ता है। यूएफटी -8 से सी पर स्विच करने पर यहां 4.5 से 2.3 के बीच नीचे
स्टीफन चेज़लस

3

मैंने ऐसा करने के लिए एक सरल पर्ल स्क्रिप्ट लिखी:

Usage: script.pl inputfile_f inputfile_f

#!/usr/bin/env perl

$number_arguments = $#ARGV + 1;
if ($number_arguments != 2) {
    die "Usage: script.pl inputfile_f inputfile_l\n";
}

open($f, '<', $ARGV[0])
    or die "$ARGV[0]: Not found\n";
open($l, '<', $ARGV[1])
    or die "$ARGV[1]: Not found\n";

@line_numbers = <$l>;

while ($line = <$f>) {
    $count_f ++;
    if ($count_f == @line_numbers[$count_l]) {
        print $line;
        $count_l ++;
    }
}
  • भार F.txt
  • भार L.txt
  • L.txtकिसी सरणी में प्रत्येक पंक्ति को संग्रहीत करता है
  • F.txtलाइन द्वारा लाइन पढ़ता है , इसकी वर्तमान लाइन संख्या और वर्तमान सरणी सूचकांक पर नज़र रखता है; F.txtवर्तमान लाइन संख्या बढ़ाता है ; यदि F.txtवर्तमान लाइन संख्या वर्तमान सरणी सूचकांक में सरणी की सामग्री से मेल खाती है, तो यह वर्तमान रेखा को प्रिंट करती है और सूचकांक को बढ़ाती है

लागत और जटिलता के विचार :

असाइनमेंट करने की लागत, तुलना करने की लागत और लाइनों को प्रिंट करने की लागत को देखते हुए, एन 1 को लाइनों की संख्या के रूप में एन 1F.txt और एन 2 को लाइनों की संख्या के रूप में दिया जाता है L.txt, whileलूप सबसे अधिक एन 1 बार चलता है, 2N के लिए अग्रणी 1 + N 2 कार्य (जाहिर है यह सोचते हैं एन 1 > एन 2 ), 2N को 1 तुलना और एन के लिए 2 प्रिंट; प्रत्येक ऑपरेशन की लागत के बराबर दिया गया, whileलूप को चलाने की कुल लागत 4N 1 + 2N 2 है , जो O (N) की स्क्रिप्ट की जटिलता की ओर ले जाती है।

10 मिलियन-लाइन इनपुट फ़ाइल पर परीक्षण करें :

10 लाख लाइनों का उपयोग करते हुए F.txtयादृच्छिक 50 वर्ण-लंबी लाइनों युक्त फ़ाइल और एक 10 लाख लाइनों L.txt10000000 1 से संख्या से युक्त फ़ाइल (सबसे खराब स्थिति):

~/tmp$ for ((i=0; i<3; i++)); do time ./script.pl F.txt L.txt > output; done

real    0m15.628s
user    0m13.396s
sys 0m2.180s

real    0m16.001s
user    0m13.376s
sys 0m2.436s

real    0m16.153s
user    0m13.564s
sys 0m2.304s

2

यह पर्ल समाधान अन्य आवक या पर्ल समाधानों की तुलना में 20% या उससे अधिक तेज़ है, लेकिन सी में समाधान के रूप में उतनी तेजी से नहीं है।

perl -e '
  open L, shift or die $!;
  open F, shift or die $!;
  exit if ! ($n = <L>);
  while (1) {
    $_ = <F>;
    next if $. != $n;
    print;
    exit if ! ($n = <L>);
  }
' -- L F

0
cat <<! >L.txt
1
3
!

cat <<! >F.txt
Hello World
Hallo Welt
Hola mundo
!

cmd(){
 L=$1 F=$2
 cat -n $F |
 join $L - |
 sed 's/[^ ]* //'
}

cmd L.txt F.txt
Hello World
Hola mundo

चूंकि L.txt को सॉर्ट किया जाता है, आप इसमें शामिल हो सकते हैं। F.txt में बस हर लाइन को नंबर दें, दो फाइलों को मिलाएं, फिर लाइन नंबर को हटा दें। कोई बड़ी मध्यवर्ती फ़ाइलों की आवश्यकता नहीं है।

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

cmd(){
 L=$1 F=$2
 cat -n $F |
 sed 's/^ *//;s/\t/|/' |
 join -t'|' $L - |
 sed 's/[^|]*|//'
}

पहली सीड "कैट-एन" आउटपुट से अग्रणी रिक्त स्थान को निकालता है और टैब को बदलता है। दूसरा सेड लाइन लाइन हटाता है और "|" |


मुझे डर है कि यह बड़ी फ़ाइलों पर काम नहीं करेगा। इसे <10 लाइनों की जरूरत है। मेरे पास एक ही विचार था और कोशिश की गई थी join L.txt <(nl F.txt )लेकिन यह बड़ी फ़ाइलों पर काम नहीं करेगा। वैसे, साइट पर आपका स्वागत है, अक्सर ऐसा नहीं होता है कि हमें नए उपयोगकर्ताओं से ऐसे स्पष्ट और अच्छी तरह से तैयार किए गए उत्तर मिलें!
terdon

@terdon, हाँ, एक शर्म की बात है कि join/ commसंख्यात्मक रूप से हल किए गए इनपुट के साथ काम नहीं कर सकता है।
स्टीफन चेजलस

@terdon: मैंने आपका नेतृत्व किया (अब हटा दिया गया), और कोशिश की join -t' ' <(<L.txt awk '{printf("%010s\n",$0)}') <(<F.txt awk '{printf("%010s %s\n",NR,$0)}') | cut -d' ' -f2-- यह धीमा था! - और यहां तक ​​कि जब मैंने उपयुक्त 0-गद्देदार कुंजियों के साथ तैयार फाइलों में खिलाया join -t' ' L.txt F.txt | cut -d' ' -f2- , तो यह अभी भी धीमा था (तैयारी के समय सहित नहीं) - awk@ जानिस द्वारा जवाब की तुलना में धीमा (जहां मैंने एक टिप्पणी पोस्ट की है दोनों के लिए वास्तविक समय फिर से लिया है उसका और @
स्टीफनचैजलैस का

@ पीटर। हाँ। मैंने एक समान दृष्टिकोण की कोशिश की, जो एक अक्स से बचा जाता है, लेकिन मुझे यह काम करने और इसके लायक होने का कोई रास्ता नहीं मिला।
terdon

@terdon और अन्य: join+ awk printf प्रक्रिया विकल्प के लिए वास्तविक समय real 20m11.663s user 19m35.093s sys 0m10.513s बनाम स्टीफन चेज़लस का real 2m11.637s user 2m2.748s sys 0m6.424s उपयोग करके L50 मिलियन लाइनों, F500 मिलियन लाइनों का उपयोग किया गया था ।
पीटर।

0

पूर्णता के लिए, joinसमाधान पर एक और प्रयास :

sed -r 's/^/00000000000000/;s/[0-9]*([0-9]{15})/\1/' /tmp/L | join <( nl -w15 -nrz /tmp/F ) - | cut -d' ' -f2-

यह लाइन-संख्या कॉलम को प्रारूपित करके काम करता है जो प्रमुख शून्य के साथ निश्चित लंबाई के रूप में काम करता है, ताकि संख्या हमेशा 15 अंकों की लंबी हो। यह सामान्य संख्यात्मक प्रकार के क्रम को पसंद नहीं करने की समस्या को रोकता है, क्योंकि स्तंभ को प्रभावी रूप से अब शब्दकोश क्रमबद्ध होने के लिए मजबूर किया गया है। nlइस फॉर्मेट में लाइन नंबर जोड़ने के लिए F.txt का उपयोग किया जाता है। दुर्भाग्य से sedL.txt में नंबरिंग को पुन: स्वरूपित करने के लिए उपयोग करने की आवश्यकता है।

यह तरीका @ mikeserv की विधि का उपयोग करके उत्पन्न परीक्षण डेटा पर ठीक काम करने लगता है। लेकिन यह अभी भी बहुत धीमा है - मेरी मशीन पर सी समाधान 60x तेज है। समय का लगभग 2/3 sedऔर / 1/3 में खर्च होता है join। शायद एक बेहतर sed अभिव्यक्ति है ...


ठीक है - लेकिन हम सभी शून्य को क्यों पसंद कर रहे हैं? मैं इसके लिए एक महसूस करने की कोशिश कर रहा हूं। इसके अलावा, nlसुपर शांत है, लेकिन आप इसे मजबूती से अप्राप्त इनपुट पर उपयोग नहीं कर सकते। एक चीज़ जो इसे इतना अच्छा बनाती है वह है इसका लॉजिकल पेज -d एलिमिटर। डिफ़ॉल्ट रूप से यदि इनपुट में कोई रेखा है जिसमें केवल तार :\` (लेकिन अनुगामी कब्र से w / बाहर) 1, 2, 3 या तीन बार उत्तराधिकार में है, तो आपकी गिनती थोड़ी पागल हो जाएगी। प्रयोग w / यह - यह बहुत साफ है। विशेष रूप से क्या होता है पर एक नज़र है जब nl` 1 सीमांकक स्ट्रिंग के साथ एक पंक्ति पढ़ता है और फिर बाद में एक और w / 3 या 2
mikeserv

0

चूंकि स्वीकृत उत्तर C में है, इसलिए मुझे लगा कि अजगर के समाधान को यहां फेंकना ठीक है:

# Read mask
with open('L.txt', 'r') as f:
    mask = [int(line_num) for line_num in f.read().splitlines()]

# Filter input file
filtered_lines = []
with open('F.txt', 'r') as f:
    for i, line in enumerate(f.read().splitlines()):
        if (i+1) in mask:
            filtered_lines.append(line)

# Write newly filtered file
with open('F_filtered.txt', 'w') as f:
    for line in filtered_lines:
        f.write('%s\n' % line)

यदि बाहरी पुस्तकालय जैसे कि खसखस ​​का उपयोग किया जाता है, तो एक समाधान और भी अधिक सुरुचिपूर्ण दिखाई देगा:

import numpy as np

with open('L.txt', 'r') as f:
    mask = np.array([int(line_num)-1 for line_num in f.read().splitlines()])

with open('F.txt', 'r') as f:
    lines = np.array(f.read().splitlines())
filtered_lines = lines[mask]

with open('F_filtered.txt', 'w') as f:
    for line in filtered_lines:
        f.write('%s\n' % line)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.