লাইন নম্বর দ্বারা ফাইল ফিল্টার


17

প্রতি লাইন এবং পাঠ্য ফাইল এফ-তে একটি অ-নেতিবাচক পূর্ণসংখ্যার সাথে একটি ফাইল এল দেওয়া হয়েছে, কেবলমাত্র এফগুলিতে সেই রেখাগুলি রাখার দ্রুত উপায় কী হবে, যার লাইন নম্বরটি ফাইল এলটিতে প্রদর্শিত হবে?

উদাহরণ:

$ 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 মিলিয়ন বা তারও বেশি এন্ট্রি সহ একটি ফাইল এল পরিচালনা করতে পারে; ফাইল এল সংখ্যাগতভাবে বাছাই করা হয়।

দ্রষ্টব্য: আমি একটি বাস্তবায়নের অর্ধেক পথ পেরিয়ে এসেছি command-in-questionতবে আমি কেবল অবাক হয়েছি যে কেউ এখানে কিছু ইউনিক্স সরঞ্জাম ব্যবহার করতে সক্ষম হবে কিনা।


আপডেট: সমস্ত উত্তরের জন্য ধন্যবাদ, আমি আজ অনেক কিছু শিখেছি! আমি আরও একটি উত্তর গ্রহণ করতে চাই, তবে এটি সম্ভব নয়।


এফ ফাইলটিতে কতগুলি লাইন রয়েছে?
পিটার.ও

1
এল
যতটা

উত্তর:


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। এবং এটি সবেমাত্র কাজ করেছে - এটি কেবল দুটি লিবসের প্রয়োজন।
মাইকজারভেজ

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
@ পিটার.ও, ওফস, এটাই আমি এনআর> = এন দ্বারা সম্বোধন করার চেষ্টা করেছি, তবে এটি ভুল ছিল। এখনই আরও ভাল হওয়া উচিত।
স্টাফেন চেজেলাস

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এক্সট লাইনটি স্বয়ংক্রিয়ভাবে মুদ্রণ করুন ।
    • এবং সুতরাং sedprunes sortএর আউটপুটটি কেবল ক্রমযুক্ত লাইন জোড়া যা কোনও কোলন এবং নিম্নলিখিত লাইনের সাথে মেলে না - বা, কেবল একটি লাইন থেকে ./Lএবং তারপরে পরের।
  • cut -sd: -f2-
    • cut -sআউটপুট থেকে বড়গুলি এর ইনপুট লাইনগুলিতে অন্তত একটির -d:এলিমিটার স্ট্রিং ধারণ করে না - এবং ./Lএর লাইনগুলি সম্পূর্ণ ছাঁটাই হয়।
    • যে লাইনগুলি করে তাদের জন্য, তাদের প্রথম :কোলন-সীমাবদ্ধ -fআইল্ডটি cutদূরে রয়েছে - এবং তাই সমস্ত greplinোকানো লিনেনোর।

ছোট ইনপুট পরীক্ষা

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

... যা এতে 5 /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

(আমি সেখানে ব্যাকস্ল্যাশ যুক্ত করেছি)

বর্তমানে এখানে প্রদত্ত সমাধানগুলির মধ্যে, এটি আমার মেশিনে উপরে উত্পন্ন ডেটাসেটের বিরুদ্ধে যখন খড়খড়ি করে তবে এটি সবার দ্রুততম। অন্যদের মধ্যে কেবল একজনই দ্বিতীয় স্থান অর্জনের পক্ষে লড়াইয়ের কাছাকাছি এসেছিল, এবং তা হ'ল perl এখানে মেহ ।

এটি কোনওভাবেই দেওয়া আসল সমাধানটি নয় - এটি অন্যের দেওয়া পরামর্শ / অনুপ্রেরণার জন্য তার কার্যকর সময়টির এক তৃতীয়াংশ বাদ পড়েছে। ধীর সমাধানের জন্য পোস্টের ইতিহাস দেখুন (তবে কেন?)

এছাড়াও, এটি লক্ষণীয় যে এটি যদি আমার সিস্টেমের মাল্টি-সিপিইউ আর্কিটেকচার এবং সেই পাইপলাইনের প্রতিটি প্রক্রিয়াটির একযোগে সম্পাদনের জন্য না হয় তবে অন্য কিছু উত্তর খুব ভালভাবে লড়াই করতে পারে। এগুলি সমস্ত একই সময়ে কাজ করে - প্রত্যেকে তার নিজস্ব প্রসেসরের মূল অংশে - ডেটা ঘুরে এবং পুরো অংশে তাদের ছোট অংশটি করে। বেশ সুন্দর।

তবে দ্রুততম সমাধানটি হ'ল ...

তবে এটি দ্রুততম সমাধান নয়। এখানে দেওয়া দ্রুততম সমাধানটি হ্যান্ড-ডাউন, সি প্রোগ্রাম । আমি বলেছি 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'
Stéphane Chazelas

@ স্টাফেনচাজেলাস - আপনি স্যুইচ করলে আপনি আরও ভাল ফলাফল পেতে পারেন sed- sedআমি ব্যবহার করছি উত্তরাধিকারী sed- ফলাফলের aliasমানটি আপনি দেখতে পাচ্ছেন time। আমার উত্তরাধিকারী প্যাকেজটি যাইহোক, স্ট্র্যাটেলিভাবে একটি মাসল লিবিসি-র বিরুদ্ধে সংকলিত হয় - রেজেক্স বাস্তবায়ন যার জন্য টিআরই এর উপর ভিত্তি করে । আমি যখন এটি জিএনইউতে স্যুইচ করি sed- এবং এগুলি চালনা ছাড়াই cut- এটি সম্পূর্ণ হওয়ার সময়টিতে একটি সম্পূর্ণ দ্বিতীয় যোগ করে (২.৮ সেকেন্ড) - এটি একটি তৃতীয়াংশের বেশি দিয়ে মিশ্রণ করে। এবং এটি আমার সিস্টেমে আপনার থেকে মাত্র .3 সেকেন্ড দ্রুত।
মাইকজার্জ

1
sort -mnবিরোধী হিসাবে sort -nmk1,1ভাল হতে পারে আপনি এখানে বিভাজন করতে হবে না (পরীক্ষা করা হয়নি)
স্টাফেন চেজেলাস

@ স্টাফেনচাজেলাস - হ্যাঁ, আমিও একই ধারণা করেছি এবং আমি এটি সর্বদাই চেষ্টা করেছি। -nঠিক তাই আমি মূর্ত ঠিক একটি লাইন প্রথম সাংখ্যিক স্ট্রিং করতে spec'd হয় -mnবা -nmএবং কারনের শুধুমাত্র বার জন্য এটি আগের সমাপ্তির সময় 2sec নিচে চুবান যখন আমি সমস্ত বিকল্পগুলির যোগ হিসাবে। এটি অদ্ভুত - এবং এর কারণ -mহ'ল গতকাল আমি প্রথম স্থানে সামলেছি না - আমি জানতাম যে আমি কী সম্পর্কে ছিলাম তবে এটি কেবল এক ধরণের অটো-অপ্টিমাইজেশান জিনিস হিসাবে কাজ করার মতো বলে মনে হয়েছিল। মজার বিষয় হল, উত্তরাধিকারের sortএকটি -zস্ট্রিং-দৈর্ঘ্যের বিকল্প রয়েছে যা কেবলমাত্র প্রযোজ্য -[cm]....
মাইকজার্ভ

-nলাইনের প্রথম সংখ্যাযুক্ত স্ট্রিং নয় । এটি কেবল লাইনটিকে একটি সংখ্যা হিসাবে abc 1230 হিসাবে বিবেচনা করবে তাই এটি এর চেয়ে কম দক্ষ হতে পারে না-t: -k1,1
স্টাফেন চেজেলাস

9

আমি ব্যবহার করব awk:

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

আপডেট: আমি কর্মক্ষমতা ব্যবস্থা করেছি; দেখে মনে হচ্ছে এই সংস্করণটি খুব বড় ডেটা সেটগুলির সাথে আরও ভাল স্কেল করে (যেমন বর্ণিত প্রয়োজনীয়তার ক্ষেত্রে রয়েছে), যেহেতু তুলনাটি খুব দ্রুত এবং হ্যাশ টেবিলটি তৈরির জন্য প্রয়োজনীয় প্রচেষ্টাকে পরিবেশন করে।


1
@miku; হ্যাঁ, এটি একটি দুর্দান্ত কমপ্যাক্ট সমাধান। তবে একটি সাবধানবাণী; সমস্ত লোক এ awkজাতীয় বিশাল ডেটা সেটগুলি পরিচালনা করতে সক্ষম হতে পারে না । - আমি জিএনইউ ব্যবহার করছি awkএবং কোনও সমস্যা নেই; 500 মিলিয়ন লাইন ডেটা দিয়ে পরীক্ষাটি 7 মিনিটের জন্য প্রয়োজন।
জ্যানিস

1
এটি বরং ধীর (তুলনা করে) real 16m3.468s- user 15m48.447s- sys 0m10.725s। এটি 50,000,000 লাইন Lসহ 1 / 10' মাপের পরীক্ষার 3.3 গিগাবাইট র‌্যাম ব্যবহার করেছে ; এবং সঙ্গে 500.000.000 লাইন - বনাম Stéphane Chazelas 'awk আনসার জন্য সময়: - - - আমি ফাস্ট বক্স ব্যবহার করছি না, কিন্তু তুলনা আকর্ষণীয়। Freal 2m11.637suser 2m2.748ssys 0m6.424s
পিটার.ও

@ Peter.O; তথ্য জন্য ধন্যবাদ! একটি ধীর গতি প্রত্যাশা করা হয়েছিল, প্রদত্ত যে (আমার নিজের পরীক্ষার ক্ষেত্রে) অর্ধ বিলিয়ন লাইনগুলি একটি সহযোগী অ্যারেতে সংরক্ষণ করা হয়েছিল were (এজন্যই আমি স্টিফেনের প্রস্তাবের জন্য উপরে "(+1)" মন্তব্য করেছি।) - যদিও আমি অবাক হয়ে গিয়েছিলাম যে এই ঘর্ষণ দ্রবণটি এখনও প্রতি সেকেন্ডে 1 মিলিয়ন লাইন প্রসেস করছে! আমি মনে করি এটি এই কোড প্যাটার্নটিকে (এটি সরলতার কারণে!) একটি কার্যকর বিকল্প হিসাবে তৈরি করে এবং বিশেষত কম চরম ডেটা মাপের ক্ষেত্রে।
জানিস

এটি অবশ্যই একটি কার্যকর সমাধান। পরীক্ষার ডেটাতে আমি ব্যবহার করেছি (5 মিলিল লাইন / 1.5 মিলিল এল) আপনার 4 সেকেন্ডের মধ্যে কিছুটা শেষ হয়েছে - স্টিফেনের উত্তরের পিছনে মাত্র এক সেকেন্ড। পরীক্ষার সেটটি জিন করতে ব্যবহৃত কোডটি আমার উত্তরে তবে এটি বেশিরভাগই কেবল seqআউটপুট এবং তারপরে এল এর মধ্যে একটি ছোট, এলোমেলোভাবে নির্বাচিত উপসেট ।
মাইকেসার্ভ

1
আমি মাত্র 500 মিলিয়ন লাইনের ডেটা ফাইলের আকার এবং 50 মিলিয়ন এবং রেফারেন্সের কী ফাইলের আকারের সাথে আরও কিছু কার্যকারিতা ব্যবস্থা করেছি। একটি লক্ষণীয় পর্যবেক্ষণ সহ 500 মিলিয়ন লাইন। ছোট কী ফাইলের সাথে সময়গুলি 4 মিনিট (স্টিফেন) বনাম 8 মিনিট (জেনিস) হয়, তবে বড় কী ফাইলের সাথে এটি 19 মিনিট (স্টিফেন) বনাম 12 মিনিট (জেনিস) হয়।
জ্যানিস

3

কেবলমাত্র সম্পূর্ণতার জন্য: আমরা স্টাফেন চেজেলাসের উত্তরে দুর্দান্ত 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কাজটি করতে পারি না ... তবুও, আমাদের উভয় উত্তর ফ্লোহিমসেফের দ্বারা লজ্জিত হয় ।
মাইকসার্ভ

@ মিমকিজার, আমাদের অবশ্যই আলাদা আলাদা awkএস থাকতে হবে । আপনার নমুনায়, আমি গাওকের সাথে 1.4s (জ্যানিসের 4s), মাকের সাথে 0.9, এই পার্ল দ্রবণ সহ 1.7s, কোস দিয়ে 2.3s, আপনার (জিএনইউ সিড) সাথে 4.5 এবং আপনার (1.4 এস) পেয়েছি GNU সেড) এবং আমার প্রস্তাবিত উন্নতি (এবং সি সমাধানের জন্য 0.5s)।
স্টাফেন চেজেলাস

@ মাইকজার্, আহ! অবশ্যই আপনার পদ্ধতির সাথে, স্থানীয় একটি পার্থক্য করে। ইউএফটি -8 থেকে সি তে স্যুইচ করার সময় এখানে 4.5s থেকে নীচে 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 কে রেখার সংখ্যা হিসাবে F.txtএবং এন 2 কে লাইন সংখ্যা হিসাবে দেওয়া হয়েছে L.txt, whileলুপটি সর্বাধিক এন 1 বার চলে, 2 এন 1 + এন 2 অ্যাসাইনমেন্ট (স্পষ্টতই এন 1 > এন 2 ধরে নিচ্ছেন), 2 এন 1 তুলনা এবং এন 2 প্রিন্টের দিকে নিয়ে যাওয়া; প্রতিটি ক্রিয়াকলাপের সমান ব্যয় হিসাবে প্রদত্ত whileলুপটি চালাতে মোট ব্যয় হয় 4N 1 + 2N 2 , যা ও (এন) এর স্ক্রিপ্টের জটিলতার দিকে নিয়ে যায়।

একটি 10-মিলিয়ন-লাইনের ইনপুট ফাইলটিতে পরীক্ষা করুন :

১০ মিলিয়ন-লাইনের F.txtফাইলটি এলোমেলোভাবে 50-অক্ষর-দীর্ঘ লাইন এবং 10-মিলিয়ন-লাইন L.txtফাইল 1 থেকে 10000000 পর্যন্ত সংখ্যার সমন্বিত ফাইল ব্যবহার করে (সবচেয়ে খারাপ পরিস্থিতি):

~/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% বা তত বেশি দ্রুত হয় তবে স্পষ্টতই সি এর দ্রবণ হিসাবে তত দ্রুত নয় not

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 )কিন্তু এটি বড় ফাইলগুলিতে কাজ করবে না। সাইটে স্বাগতম, যাইহোক, এটি প্রায়শই আমরা নতুন ব্যবহারকারীদের কাছ থেকে পরিষ্কার এবং ভাল ফর্ম্যাট উত্তর পাই না!
টেরডন

@ এটারডন, হ্যাঁ, লজ্জাজনক যে join/ commসংখ্যা অনুসারে বাছাই করা ইনপুটটি দিয়ে কাজ করতে পারে না।
স্টাফেন চেজেলাস

@ ইটারডন: আমি আপনার সীসা অনুসরণ করেছি (এখন মুছে ফেলা হয়েছে), এবং চেষ্টা করেছি 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 এবং অন্যদের জন্য প্রকৃত সময় join+ + awk printf প্রক্রিয়া substiturion ছিল real 20m11.663s user 19m35.093s sys 0m10.513s বনাম Stéphane Chazelas ' 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 অঙ্ক দীর্ঘ হয়। এটি সাধারণ সংখ্যার বাছাইয়ের ক্রমটি পছন্দ না করার সাথে যোগ দেওয়ার সমস্যাটিকে সরিয়ে দেয়, কারণ কলামটি কার্যকরভাবে এখন অভিধান অনুসারে বাছাই করতে বাধ্য হয়েছে। nlF.txt এ বিন্যাসে লাইন নম্বর যুক্ত করতে ব্যবহৃত হয়। দুর্ভাগ্যবশতsed L.txt এ নম্বরটি পুনরায় ফর্ম্যাট করতে ব্যবহার করা দরকার।

এই মতামতটি @ মাইকজার্ভের পদ্ধতিটি ব্যবহার করে উত্পন্ন পরীক্ষার ডেটাগুলিতে ঠিক কাজ করছে বলে মনে হচ্ছে। তবে এটি এখনও খুব ধীর - আমার মেশিনে সি দ্রবণটি 60x দ্রুত faster প্রায় 2/3 সময় ব্যয় হয় sedএবং 1/3 ইন join। সম্ভবত আরও ভাল খারাপ ভাব প্রকাশ আছে ...


ঠিক আছে - তবে কেন আমরা সমস্ত শূন্যগুলি প্রিপেন্ড করছি? আমি এই জন্য একটি অনুভূতি পেতে চেষ্টা করছি। এছাড়াও, nlদুর্দান্ত দুর্দান্ত, তবে আপনি এটি দৃly়তার সাথে অনির্ধারিত ইনপুটটিতে ব্যবহার করতে পারবেন না। একটি জিনিস যা এটি এত শীতল করে তোলে তা হ'ল লজিকাল পৃষ্ঠা -d অ্যালিমিটার। ডিফল্টরূপে যদি ইনপুটটিতে কোনও পংক্তি থাকে কেবলমাত্র স্ট্রিংগুলি অন্তর্ভুক্ত থাকে :\` (তবে পিছনের কবরটি ডাব্লু / আউট) একের পর এক, ২, 3 বা তিনবার, আপনার গুনগুলি কিছুটা ক্রেজি হয়ে যাবে। W / it পরীক্ষা - এটি বেশ ঝরঝরে। বিশেষত যখন 1 টি ডিলিমিটার স্ট্রিং সহ এনএলএল একটি লাইন পড়ে এবং তারপরে আরও একটি ডাব্লু / 3 বা 2
মাইকেসার্ভ

0

গৃহীত উত্তর সি তে থাকাকালীন আমি বুঝতে পেরেছিলাম যে এখানে অজগর সমাধানটি ফেলে দেওয়া ঠিক আছে:

# 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)
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.