Saring file dengan nomor baris

17

Diberikan file L dengan satu bilangan bulat non-negatif per baris dan file teks F, apa yang akan menjadi cara cepat untuk menjaga hanya baris-baris di F, yang nomor barisnya muncul di file L?

Contoh:

$ 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

Saya mencari perintah yang dapat menangani file L dengan 500 juta atau lebih entri; File L diurutkan secara numerik.

Catatan: Saya setengah jalan dalam implementasi untuk command-in-questiontetapi saya hanya bertanya-tanya, apakah orang mungkin dapat menggunakan beberapa alat Unix di sini juga.


Pembaruan: Terima kasih atas semua jawaban, saya belajar banyak hari ini! Saya ingin menerima lebih banyak satu jawaban, tetapi itu tidak mungkin.

miku
sumber
Berapa banyak baris yang ada di file F?
Peter.O
1
Setidaknya ada banyak baris dalam F seperti pada L.
miku

Jawaban:

8

Dengan Cmenghilangkan pesan kesalahan yang bermakna:

#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;
    }
}
FloHimself
sumber
2
Ini adalah jawaban yang paling performant di sini. Setidaknya demikian dengan tes saya. Dalam kasus ada yang tertarik, saya dikompilasi seperti: xsel -bo | cc -xc - -o cselect. Dan itu hanya berhasil - itu hanya membutuhkan dua lib.
mikeserv
1
Terima kasih, ini luar biasa! Saya harap Anda tidak keberatan, tetapi saya membungkus kode Anda menjadi alat kecil .
miku
1
@miku Silakan, aku senang bisa membantu. Saya perhatikan Anda telah meningkatkan LINE_MAXdalam versi Anda, jadi Anda mungkin bekerja dengan garis yang sangat besar di file Anda. Saya telah memperbarui A dengan versi yang digunakan getline()untuk menghapus batas ukuran garis.
FloHimself
@ FloHself, well, terima kasih lagi:) Memang, beberapa jalur input mungkin melebihi LINE_MAX, jadi getlinesepertinya tepat.
miku
10

Saya akan menggunakan awk, tetapi tidak menyimpan seluruh konten L.txtdalam memori dan melakukan hash look up yang tidak perlu ;-).

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"
Stéphane Chazelas
sumber
Tepat, saya mencoba peta hash dan mereka akan melebihi memori; Bitet akan memberi Anda lebih banyak ruang kepala; tetapi dengan menggunakan fakta, bahwa input diurutkan, Anda dapat menyingkirkan masalah (spasi) ini sepenuhnya.
miku
1
@Janis; bukankah itu hanya kasus praktik pengkodean standar yang baik: jangan keras kode literal - sebagai gantinya gunakan variabel ... (lebih fleksibel dan lebih sedikit kesalahan cenderung, dan lebih mudah untuk mempertahankan)
Peter.O
1
@ StéphaneChazelas: Perlu pre loop inisialisasi n, jika tidak (as-is) itu meleset 1diL.txt
Peter.O
1
@ Peter.O, oops, itulah yang saya coba atasi dengan NR> = n, tapi itu salah. Seharusnya lebih baik sekarang.
Stéphane Chazelas
1
@ Janis, idenya adalah bahwa jika kode itu akan tertanam dalam command-in-questionskrip, maka Anda tidak bisa memasukkan nama file dalam kode tersebut. -v list="$opt_x"tidak bekerja karena proses backslash yang dilakukan oleh awk di atasnya. Itulah sebabnya saya menggunakan ENVIRON sebagai gantinya di sini.
Stéphane Chazelas
10

grep -n | sort | sed | cut

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

Itu harus bekerja cukup cepat (beberapa tes waktunya termasuk di bawah ini) dengan input ukuran berapa pun. Beberapa catatan tentang caranya:

  • export LC_ALL=C
    • Karena tujuan dari operasi berikut adalah untuk mendapatkan seluruh file yang ./Fditumpuk sejajar dengan ./Lfile lineno -nya , satu-satunya karakter yang benar-benar perlu kita khawatirkan adalah [0-9]digit ASCII dan :titik dua.
    • Untuk alasan itu, lebih mudah untuk khawatir menemukan 11 karakter itu dalam 128 possible daripada jika UTF-8 terlibat.
  • grep -n ''
    • Ini memasukkan string LINENO:ke kepala setiap baris di stdin - atau <./F.
  • sort -t: -nmk1,1 ./L -
    • sortsama sekali tidak menyortir file-file inputnya, dan sebaliknya (dengan benar) mengira file-file itu di - -mpresort dan menyesuaikannya dalam -numericallyurutan yang diurutkan, pada dasarnya mengabaikan apa pun yang melampaui karakter usus besar yang mungkin -k1,1terjadi -t:.
    • Meskipun ini mungkin memerlukan beberapa ruang temp untuk dilakukan (tergantung pada seberapa jauh jarak beberapa urutan mungkin terjadi) , itu tidak akan memerlukan banyak dibandingkan dengan jenis yang tepat, dan itu akan sangat cepat karena melibatkan nol backtracking.
    • sortakan menampilkan aliran tunggal di mana setiap lineno ./Lakan segera mendahului baris yang sesuai di ./F. ./LGaris selalu didahulukan karena lebih pendek.
  • sed /:/d\;n
    • Jika baris saat ini cocok dengan /:/titik dua, dhilangkan itu dari output. Lain, cetak otomatis jalur saat ini dan next.
    • Dan demikian, sedpangkas sortoutput hanya untuk pasangan garis berurutan yang tidak cocok dengan titik dua dan baris berikut - atau, hanya untuk satu baris dari ./Ldan kemudian berikutnya.
  • cut -sd: -f2-
    • cut -suppresses dari output mereka dari jalur input yang tidak mengandung setidaknya satu dari -d:string elimiter - dan begitu juga ./Lgaris dipangkas sepenuhnya.
    • Untuk baris-baris yang ada, ield yang :dibatasi usus besar pertama mereka -fada cut- dan begitu juga semua greplineno yang dimasukkan.

uji input kecil

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

... menghasilkan 5 baris input sampel. Kemudian...

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

... mencetak ...

==> 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

tes waktunya lebih besar

Saya membuat beberapa file yang cukup besar:

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

... yang memasukkan 5 mil garis /tmp/Fdan 1,5 mil garis yang dipilih secara acak ke dalam /tmp/L. Saya kemudian melakukan:

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

Itu dicetak:

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

(Saya menambahkan garis miring terbalik di sana)

Di antara solusi yang saat ini ditawarkan di sini, ini adalah yang tercepat dari mereka semua tetapi satu ketika diadu dengan dataset yang dihasilkan di atas pada mesin saya. Yang lainnya hanya satu yang hampir bersaing untuk tempat kedua, dan itu meuh di perl sini .

Ini sama sekali bukan solusi asli yang ditawarkan - ia telah menjatuhkan sepertiga dari waktu pelaksanaannya berkat saran / inspirasi yang ditawarkan oleh orang lain. Lihat riwayat kiriman untuk solusi yang lebih lambat (tapi mengapa?) .

Juga, perlu dicatat bahwa beberapa jawaban lain mungkin akan berpendapat lebih baik jika bukan karena arsitektur multi-CPU dari sistem saya dan eksekusi bersamaan dari setiap proses dalam pipa itu. Mereka semua bekerja pada saat yang sama - masing-masing pada inti prosesor sendiri - melewati data dan melakukan bagian kecil mereka secara keseluruhan. Itu sangat keren.

tetapi solusi tercepat adalah ...

Tetapi ini bukan solusi tercepat. Solusi tercepat yang ditawarkan di sini, tangan-down, adalah program C . Saya menyebutnya cselect. Setelah menyalinnya ke clipboard X saya, saya mengompilasinya seperti:

xsel -bo | cc -xc - -o cselect

Saya kemudian melakukannya:

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

... dan hasilnya adalah ...

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
mikeserv
sumber
1
Anda dapat membuatnya secara signifikan lebih cepat (hampir secepat tambang di sistem multi-core) dengan sed -ne'/:/!{n;p;}' | cut -d: -f2-bukansed -ne'/:/!N;/\n/s/[^:]*://p'
Stéphane Chazelas
@ StéphaneChazelas - Anda mungkin mendapatkan hasil yang lebih baik jika Anda beralih seds - yang sedsaya gunakan adalah pusaka sed- Anda dapat melihat aliasnilai dalam timehasil. Paket pusaka saya, by the way, secara statis dikompilasi melawan libc musl - implementasi regex yang didasarkan pada TRE . Ketika saya beralih ke GNU sed- dan menjalankannya tanpa cut- itu menambahkan satu detik penuh ke waktu penyelesaian (2,8 detik) - senyawa itu lebih dari sepertiga. Dan itu hanya 0,3 detik lebih cepat daripada milik Anda di sistem saya.
mikeserv
1
sort -mnsebagai lawan sort -nmk1,1mungkin lebih baik karena Anda tidak perlu melakukan pemisahan di sini (tidak diuji)
Stéphane Chazelas
@ StéphaneChazelas - ya, saya pikir hal yang sama dan saya mencoba segala hal. -nadalah spec'd hanya untuk melakukan string numerik pertama pada sebuah baris jadi saya pikir, ok -mnatau -nmdan, untuk alasan apa pun satu-satunya kali itu dicelupkan di bawah 2sec dalam waktu penyelesaian adalah ketika saya menambahkan semua opsi apa adanya. Ini aneh - dan itu alasannya kemarin saya tidak memakainya -m- saya tahu apa yang saya maksud, tetapi sepertinya hanya berhasil sebagai semacam optimasi otomatis. Menariknya, heirloom sortmemiliki -zopsi string-length yang hanya berlaku untuk -[cm]....
mikeserv
-nbukan string numerik pertama di telepon . Itu hanya menganggap garis sebagai angka sehingga abc 123akan menjadi 0. Jadi tidak bisa kurang efisien daripada dengan-t: -k1,1
Stéphane Chazelas
9

Saya akan menggunakan awk:

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

Pembaruan: Saya telah melakukan pengukuran kinerja; tampaknya versi ini menskala lebih baik dengan set data yang sangat besar (seperti halnya dengan persyaratan yang dinyatakan), karena perbandingannya sangat cepat dan mengkompensasi upaya yang diperlukan untuk membangun tabel hash.

Janis
sumber
1
@miku; Ya, ini solusi ringkas yang bagus. Tapi peringatan; tidak semua awks dapat menangani set data sebesar itu. - Saya menggunakan GNU awkdan tidak ada masalah; tes dengan 500 juta baris data diperlukan 7 menit.
Janis
1
Ini agak lambat (dibandingkan) real 16m3.468s- user 15m48.447s- sys 0m10.725s. Ini digunakan 3,3 GB RAM pengujian ukuran 1/10 Ldengan 50.000.000 baris; dan Fdengan 500.000.000 baris - vs waktu untuk Stéphane Chazelas 'awk anser: real 2m11.637s- user 2m2.748s- sys 0m6.424s- Saya tidak menggunakan kotak cepat, tetapi perbandingannya menarik.
Peter.O
@ Peter.O; Terima kasih atas datanya! Kecepatan yang lebih lambat diharapkan, mengingat bahwa (dalam kasus pengujian saya sendiri) setengah miliar baris disimpan dalam array asosiatif. (Itu sebabnya saya berkomentar "(+1)" di atas untuk proposal Stephane.) - Meskipun saya heran bahwa solusi singkat ini masih memproses 1 juta baris per detik! Saya pikir itu membuat pola kode ini (karena kesederhanaannya!) Pilihan yang layak, dan khususnya dalam kasus dengan ukuran data yang kurang ekstrim.
Janis
Ini jelas merupakan solusi yang layak. Pada data uji yang saya gunakan (5mil baris / 1.5mil L) milik Anda selesai dalam waktu lebih dari 4 detik - hanya satu detik di belakang jawaban Stephane. Kode yang digunakan untuk gen tes set dalam jawaban saya, tapi itu kebanyakan hanya seqoutput dan kemudian yang lebih kecil, bagian yang dipilih secara acak dari yang sama di L .
mikeserv
1
Saya baru saja membuat beberapa ukuran kinerja dengan ukuran file data 500 juta baris dan ukuran file kunci 50 juta dan resp. 500 juta baris, dengan pengamatan yang patut diperhatikan. Dengan file kunci yang lebih kecil waktunya adalah 4 menit (Stephane) vs 8 menit (Janis), sedangkan dengan file kunci yang lebih besar 19 menit (Stephane) vs. 12 menit (Janis).
Janis
3

Hanya untuk kelengkapan: kita dapat menggabungkan skrip awk yang sangat baik dalam jawaban oleh Stéphane Chazelas, dan skrip perl dalam jawaban dengan kos tetapi tanpa menyimpan seluruh daftar dalam memori, dengan harapan perl mungkin lebih cepat daripada awk. (Saya telah mengubah urutan argumen agar sesuai dengan pertanyaan awal).

#!/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;
        }
    }
}
meuh
sumber
Ini jauh lebih cepat daripada awk. Ini hampir secepat milik saya - Saya menguji keduanya tiga kali sekarang dan setiap kali saya menangani testset garis 5mil saya dalam 1,8 ... detik dan Anda 1,9 ... detik setiap kali. Kode gen testset ada dalam jawaban saya jika Anda peduli, tetapi intinya sangat bagus. Terlebih lagi, hasilnya benar - saya masih tidak bisa awkbekerja ... Meski begitu, kedua jawaban kami dipermalukan oleh FloHimself .
mikeserv
@ mikeserv, kita harus punya awks yang berbeda . Pada sampel Anda, saya mendapatkan 1.4s dengan gawk (4s untuk Janis '), 0.9s dengan mawk, 1.7s dengan solusi perl ini, 2.3s dengan kos', 4.5s dengan milik Anda (sed GNU), dan 1.4s dengan milik Anda ( GNU sed) dan perbaikan yang saya sarankan (dan 0,5s untuk solusi C).
Stéphane Chazelas
@ mikeserv, ah! tentu saja dengan pendekatan Anda, lokal membuat perbedaan. Turun dari 4,5 hingga 2,3 di sini saat beralih dari UFT-8 ke C.
Stéphane Chazelas
3

Saya menulis skrip Perl sederhana untuk melakukan itu:

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 ++;
    }
}
  • Banyak F.txt
  • Banyak L.txt
  • Menyimpan setiap baris L.txtdalam sebuah array
  • Membaca F.txtbaris demi baris, melacak nomor baris saat ini dan indeks array saat ini; meningkatkan F.txtnomor baris saat ini; jika F.txtnomor baris saat ini cocok dengan konten array di indeks array saat ini, itu mencetak baris saat ini dan meningkatkan indeks

Pertimbangan biaya dan kompleksitas :

Mempertimbangkan biaya untuk membuat penugasan, biaya untuk membuat perbandingan dan biaya untuk mencetak garis, diberikan N 1 sebagai jumlah baris F.txtdan N 2 sebagai jumlah baris L.txt, whileloop berjalan paling banyak N 1 kali, mengarah ke penugasan 2N 1 + N 2 (jelas dengan asumsi N 1 > N 2 ), untuk perbandingan 2N 1 dan ke cetakan N 2 ; diberikan sebagai sama dengan biaya setiap operasi, total biaya untuk menjalankan whileloop adalah 4N 1 + 2N 2 , yang mengarah pada kompleksitas skrip O (N).

Tes pada file input 10 juta baris :

Menggunakan file 10-juta-baris yang F.txtberisi 50-karakter-panjang baris acak dan 10-juta-baris L.txtfile yang berisi angka 1 hingga 10.000000 (skenario terburuk):

~/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
kos
sumber
2

Solusi perl ini lebih cepat daripada solusi awk atau perl lainnya sebesar 20% atau lebih, tetapi sebelumnya tidak secepat solusi dalam C.

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
jrw32982 mendukung Monica
sumber
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

Karena L.txt diurutkan Anda dapat menggunakan gabung. Cukup beri nomor pada setiap baris di F.txt, gabungkan dua file, lalu hapus nomor baris. Tidak diperlukan file perantara yang besar.

Sebenarnya, di atas akan memotong-motong garis data Anda dengan mengganti semua ruang putih dengan satu ruang. Agar garis tetap utuh, Anda harus memilih sebagai pembatas beberapa karakter yang tidak muncul dalam data Anda, mis. "|". Cmd kemudian

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

Sed pertama menghapus spasi utama dari output "cat -n" dan menggantikan tab. Sed kedua menghapus nomor baris dan "|".

meuh
sumber
Saya khawatir ini tidak akan berfungsi pada file yang lebih besar. Perlu <10 baris. Saya punya ide yang sama dan mencoba join L.txt <(nl F.txt )tetapi tidak akan bekerja pada file besar. Selamat datang di situs ini, omong-omong, jarang kita mendapatkan jawaban yang jelas dan terformat dengan baik dari pengguna baru!
terdon
@terdon, Ya, memalukan bahwa join/ commtidak bisa bekerja dengan masukan numerik diurutkan.
Stéphane Chazelas
@terdon: Saya mengikuti petunjuk Anda (sekarang dihapus), dan mencoba join -t' ' <(<L.txt awk '{printf("%010s\n",$0)}') <(<F.txt awk '{printf("%010s %s\n",NR,$0)}') | cut -d' ' -f2-- Itu lambat! - dan bahkan ketika saya join -t' ' L.txt F.txt | cut -d' ' -f2- memasukkan file yang sudah disiapkan dengan kunci 0-padded yang cocok , masih lambat (tidak termasuk waktu persiapan) - lebih lambat dari awkjawaban oleh @Janis (di mana saya telah mengirim komentar tentang waktu aktual yang diambil untuk kedua jawabannya dan @ StéphaneChazelas '
Peter.O
@ Peter.O ya. Saya mencoba pendekatan yang sama yang menghindari salah satu dari masalah, tetapi saya tidak dapat menemukan cara untuk membuatnya bekerja dan layak.
terdon
@terdon dan lain-lain: Waktu aktual untuk substitusi prosesjoin + adalah vs Stéphane Chazelas ' menggunakan 50 juta baris, 500 juta baris. awk printf real 20m11.663s user 19m35.093s sys 0m10.513sreal 2m11.637s user 2m2.748s sys 0m6.424sLF
Peter.O
0

Untuk kelengkapan, upaya lain untuk joinsolusi:

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

Ini berfungsi dengan memformat kolom nomor baris yang bergabung bekerja sebagai panjang tetap dengan nol di depan, sehingga angka-angka selalu panjang 15 digit. Ini menghindari masalah bergabung tidak menyukai urutan urutan numerik yang normal, karena kolom sekarang secara efektif telah dipaksa menjadi semacam kamus. nldigunakan untuk menambahkan nomor baris dalam format ini ke F.txt. Sayangnya sedperlu digunakan untuk memformat ulang penomoran dalam L.txt.

Pendekatan ini tampaknya berfungsi baik pada data uji yang dihasilkan menggunakan metode @ mikeserv. Tetapi masih sangat lambat - solusi c 60x lebih cepat pada mesin saya. sekitar 2/3 dari waktu dihabiskan di seddan 1/3 di join. Mungkin ada ekspresi sed yang lebih baik ...

Trauma Digital
sumber
Oke - tapi mengapa kita menambahkan semua nol? Saya mencoba merasakan ini. Juga, nlsangat keren, tetapi Anda tidak dapat menggunakannya dengan kuat pada input yang belum diuji. Salah satu hal yang membuatnya sangat keren adalah penghapus halaman -d logisnya. Secara default jika ada baris dalam input yang hanya terdiri dari string :\` (tapi tanpa jejak trailing) 1, 2, 3 atau tiga kali berturut-turut, hitungan Anda akan menjadi sedikit gila. Eksperimen dengan itu - cukup rapi. Terutama melihat apa yang terjadi ketika nl` membaca baris dengan 1 pembatas string dan kemudian lain w / 3 atau 2
mikeserv
0

Karena jawaban yang diterima adalah dalam C, saya pikir tidak masalah untuk melempar solusi python di sini:

# 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)

Jika menggunakan perpustakaan eksternal seperti numpy, solusi akan terlihat lebih elegan:

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)
AlexG
sumber