Filtruj plik według numeru linii

17

Biorąc pod uwagę plik L z jedną nieujemną liczbą całkowitą na wiersz i plik tekstowy F, jaki byłby szybki sposób na zachowanie tylko tych wierszy w F, których numer pojawia się w pliku L?

Przykład:

$ 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

Szukam polecenia, które może obsłużyć plik L zawierający 500 milionów lub więcej wpisów; plik L jest sortowany numerycznie.

Uwaga: Jestem w połowie wdrażania, command-in-questionale zastanawiałem się, czy można tu również użyć niektórych narzędzi uniksowych.


Aktualizacja: Dziękuję za wszystkie odpowiedzi, dzisiaj wiele się nauczyłem! Chciałbym zaakceptować więcej niż jedną odpowiedź, ale nie jest to możliwe.

miku
źródło
Ile linii jest w pliku F?
Peter.O
1
Jest co najmniej tyle linii w F, co w L.
miku

Odpowiedzi:

8

Z Cpominięciem istotnych komunikatów o błędach:

#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
źródło
2
To jest najbardziej wydajna odpowiedź tutaj. Przynajmniej tak wynika z moich testów. W przypadku gdy ktoś jest zainteresowany, mam skompilowane go tak: xsel -bo | cc -xc - -o cselect. I to po prostu działało - potrzebuje tylko dwóch bibliotek.
mikeserv
1
Dzięki, to świetnie! Mam nadzieję, że nie masz nic przeciwko, ale zawarłem twój kod w małe narzędzie .
miku
1
@miku Śmiało, cieszę się, że mogłem pomóc. Zauważyłem, że zwiększyłeś LINE_MAXswoją wersję, więc prawdopodobnie pracujesz z bardzo dużymi liniami w swoich plikach. Zaktualizowałem A z wersją używaną getline()do usunięcia limitu rozmiaru linii.
FloHimself
@ FloHimself, cóż, jeszcze raz dziękuję:) Rzeczywiście, niektóre linie wejściowe mogą przekraczać LINE_MAX, więc getlinewydaje się być w porządku.
miku
10

Używałbym awk, ale nie zapisywałbym całej zawartości L.txtw pamięci i robiłbym niepotrzebne wyszukiwanie hash ;-).

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
źródło
Dokładnie próbowałem map haszowych, a one przekraczałyby pamięć; zestawy bitowe zapewnią ci więcej miejsca; ale wykorzystując fakt, że dane wejściowe są posortowane, możesz całkowicie pozbyć się tego (kosmicznego) problemu.
miku
1
@Jisis; czy to nie tylko przypadek standardowej dobrej praktyki kodowania: nie używaj literałów kodu na sztywno - zamiast tego używaj zmiennych ... (bardziej elastyczny, mniej podatny na błędy i łatwiejszy w utrzymaniu)
Peter.O
1
@ StéphaneChazelas: Trzeba wstępnie pętlę inicjalizacji n, inaczej (jak jest) nie trafi 1wL.txt
Peter.O
1
@ Peter.O, oops, z tym próbowałem poradzić sobie przez NR> = n, ale to było złe. Teraz powinno być lepiej.
Stéphane Chazelas,
1
@Janis, pomysł polegał na tym, że jeśli ten kod miałby zostać osadzony w command-in-questionskrypcie, to nie możesz mieć wbudowanej nazwy pliku w kodzie. -v list="$opt_x"nie działa również z powodu wykonywania odwrotnego ukośnika przez awk na nim. Dlatego właśnie tutaj używam ENVIRON.
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

To powinno działać dość szybko (niektóre testy czasowe są zawarte poniżej) z wejściem dowolnego rozmiaru. Kilka uwag na temat:

  • export LC_ALL=C
    • Ponieważ celem następującej operacji jest ./Fułożenie całego pliku w stos z ./Lplikiem lineno, jedynymi znakami, o które naprawdę będziemy musieli się martwić, są [0-9]cyfry ASCII i :dwukropek.
    • Z tego powodu łatwiej jest martwić się znalezieniem tych 11 znaków w zestawie 128 możliwych niż w przypadku, gdy UTF-8 jest zaangażowany w inny sposób.
  • grep -n ''
    • To wstawia ciąg LINENO:do nagłówka każdej linii w stdin - lub <./F.
  • sort -t: -nmk1,1 ./L -
    • sortw ogóle zaniedbuje sortować swoje pliki wejściowe, a zamiast tego (poprawnie) zakłada, że ​​są one wstępnie sortowane i -mustawia je w -numericallyporządku posortowanym, ignorując w zasadzie wszystko poza jakimkolwiek potencjalnie -k1,1występującym -t:znakiem dwukropka.
    • Chociaż może to wymagać trochę przestrzeni tymczasowej (w zależności od tego, jak daleko mogą wystąpić pewne sekwencje) , nie będzie wymagało wiele w porównaniu z odpowiednim rodzajem i będzie bardzo szybkie, ponieważ wymaga zerowego śledzenia.
    • sortwyświetli pojedynczy strumień, w którym dowolne wejście lineno ./Lbędzie bezpośrednio poprzedzać odpowiednie wiersze w ./F. ./LLinie zawsze są na pierwszym miejscu, ponieważ są krótsze.
  • sed /:/d\;n
    • Jeśli bieżący wiersz pasuje do /:/dwukropka, dusuń go z wyjścia. W przeciwnym razie automatycznie wydrukuj bieżącą i nzewnętrzną linię.
    • I tak sedprzycina sortwyniki tylko do sekwencyjnych par linii, które nie pasują do dwukropka i następnej linii - lub, tylko do linii od ./Li do następnej.
  • cut -sd: -f2-
    • cut -swypisuje z wyjścia te z linii wejściowych, które nie zawierają co najmniej jednego z -d:łańcuchów eliminatora - a zatem ./Llinie są całkowicie przycinane.
    • W przypadku linii, które to robią, ich pierwszy :rozdzielony dwukropkami -fobszar jest cutnieobecny - podobnie jak wszystkie grepwstawione linie lineno.

mały test wejściowy

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

... generuje 5 wierszy próbek wejściowych. Następnie...

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

... drukuje ...

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

większe testy czasowe

Stworzyłem kilka całkiem dużych plików:

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

... które /tmp/Fwstawiają 5 mil linii i 1,5 mil linii losowo wybranych linii /tmp/L. Potem zrobiłem:

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

Wydrukowano:

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

(Dodałem tam odwrotne ukośniki)

Spośród obecnie oferowanych rozwiązań jest to najszybsze ze wszystkich, ale jedno w porównaniu z zestawem danych wygenerowanym powyżej na mojej maszynie. Z pozostałych tylko jeden był bliski rywalizacji o drugie miejsce, a to właśnie perl tutaj .

Nie jest to bynajmniej oryginalne oferowane rozwiązanie - skróciło 1/3 czasu realizacji dzięki poradom / inspiracjom oferowanym przez innych. Zobacz historię postów dla wolniejszych rozwiązań (ale dlaczego?) .

Warto również zauważyć, że niektóre inne odpowiedzi mogłyby równie dobrze konkurować, gdyby nie architektura wielu procesorów mojego systemu i jednoczesne wykonanie każdego z procesów w tym potoku. Wszystkie działają w tym samym czasie - każdy na własnym rdzeniu procesora - przekazując dane i wykonując niewielką część całości. To całkiem fajne.

ale najszybszym rozwiązaniem jest ...

Ale to nie jest najszybsze rozwiązanie. Najszybszym rozwiązaniem oferowanym tutaj, ręce w dół, jest program C . Nazwałem to cselect. Po skopiowaniu go do mojego schowka X skompilowałem go w następujący sposób:

xsel -bo | cc -xc - -o cselect

Potem zrobiłem:

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

... a wyniki były ...

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
źródło
1
Możesz znacznie go przyspieszyć (prawie tak szybko jak mój w systemach wielordzeniowych) z sed -ne'/:/!{n;p;}' | cut -d: -f2-zamiastsed -ne'/:/!N;/\n/s/[^:]*://p'
Stéphane Chazelas
@ StéphaneChazelas - możesz uzyskać lepsze wyniki, jeśli przełączysz seds - sedużywam to pamiątka sed- możesz zobaczyć aliaswartość w timewynikach. Nawiasem mówiąc, mój pakiet rodowy jest kompilowany statycznie z musl libc - implementacją wyrażeń regularnych opartą na TRE . Kiedy przestawię go na GNU sed- i uruchomię go bez cut- dodaje on pełną sekundę do czasu zakończenia (2,8 s) - sumuje go o ponad jedną trzecią. A to tylko 0,3 sekundy szybciej niż twoje w moim systemie.
mikeserv
1
sort -mnw przeciwieństwie do sort -nmk1,1może być lepiej, ponieważ nie trzeba tutaj rozdzielać (nie testowano)
Stéphane Chazelas
@ StéphaneChazelas - tak, myślałem tak samo i próbowałem tego na wszystkie sposoby. -njest wyspecyfikowany, aby zrobić pierwszy ciąg liczbowy w wierszu, więc pomyślałem, ok -mnlub -nm, z jakiegokolwiek powodu, jedyny raz, kiedy spadł poniżej 2 sekund w czasie ukończenia, kiedy dodałem wszystkie opcje bez zmian. To dziwne - i to jest powód, dla którego wczoraj nie zajmowałem się tym -m- wiedziałem, o co mi chodzi, ale wydawało się, że po prostu działa jako coś w rodzaju automatycznej optymalizacji. Co ciekawe, pamiątka sortma -zopcję długości sznurka, która dotyczy tylko -[cm]...
mikeserv
-nnie jest pierwszym ciągiem liczbowym w linii . Po prostu uważa, że ​​linia jest liczbą, więc abc 123będzie wynosić 0. Więc nie może być mniej wydajna niż z-t: -k1,1
Stéphane Chazelas
9

Użyłbym awk:

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

Aktualizacja: wykonałem pomiary wydajności; wydaje się, że ta wersja skaluje się jeszcze lepiej przy bardzo dużych zestawach danych (jak w przypadku podanych wymagań), ponieważ porównanie jest bardzo szybkie i nadmiernie rekompensuje wysiłek konieczny do zbudowania tabeli skrótów.

Janis
źródło
1
@miku; Tak, to miłe, kompaktowe rozwiązanie. Ale zastrzeżenie; nie wszystkie awkmogą być w stanie obsłużyć tak ogromne zbiory danych. - Używam GNU awki nie ma problemów; test z 500 milionami linii danych wymagał 7 minut.
Janis,
1
To raczej powolne (dla porównania) real 16m3.468s- user 15m48.447s- sys 0m10.725s. Zużyło 3,3 GB pamięci RAM, testując rozmiar 1/10 Lz 50 000 000 linii; oraz Fz 500 000 000 linii - w porównaniu do czasu dla awk anser Stéphane'a Chazelasa: real 2m11.637s- user 2m2.748s- sys 0m6.424s- Nie używam szybkiego pudełka, ale porównanie jest interesujące.
Peter.O
@ Peter.O; Dzięki za dane! Spodziewano się wolniejszej prędkości, biorąc pod uwagę, że (w moim własnym przypadku testowym) pół miliarda linii było przechowywanych w tablicy asocjacyjnej. (Dlatego skomentowałem powyżej „(+1)” dla propozycji Stephane.) - Byłem zaskoczony, że to zwięzłe rozwiązanie wciąż przetwarzało 1 milion linii na sekundę! Myślę, że sprawia, że ​​ten wzorzec kodu (ze względu na jego prostotę!) Jest realną opcją, a szczególnie w przypadkach o mniejszych rozmiarach danych.
Janis
To zdecydowanie realne rozwiązanie. Na danych testowych, które wykorzystałem (5 mil linii / 1,5 mil L), twoje zakończyły się w nieco ponad 4 sekundy - zaledwie sekundę za odpowiedzią Stephane'a. Kod używany do gen zestawu testowego jest w mojej odpowiedzi, ale najczęściej tylko seqwyjście, a potem mniejsze, losowo wybrany podzbiór samo w L .
mikeserv
1
Właśnie dokonałem kilku pomiarów wydajności przy rozmiarze pliku danych 500 milionów linii i rozmiarze klucza 50 milionów i więcej. 500 milionów linii, z godną uwagi obserwacją. Przy mniejszym pliku klucza czasy wynoszą 4 minuty (Stephane) vs. 8 minut (Janis), podczas gdy przy większym pliku klucza jest to 19 minut (Stephane) vs. 12 minut (Janis).
Janis
3

Dla kompletności: możemy połączyć doskonały skrypt awk w odpowiedzi Stéphane'a Chazelasa i skrypt perla w odpowiedzi kos, ale bez zachowania całej listy w pamięci, w nadziei, że perl może być szybszy niż awk. (Zmieniłem kolejność argumentów, aby pasowała do pierwotnego pytania).

#!/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
źródło
Jest to o wiele szybsze niż awk. Jest prawie tak szybki jak mój - testowałem oba teraz trzy razy i za każdym razem mój mój zestaw testowy linii o długości 5 mil w 1,8 ... sekundach i twój 1,9 ... sekund za każdym razem. Kod genowy zestawu testowego jest w mojej odpowiedzi, jeśli cię to obchodzi, ale chodzi o to, że jest bardzo dobry. Co więcej, wynik jest prawidłowy - nadal nie mogę wykonać awkpracy ... Mimo to obie nasze odpowiedzi są zawstydzone przez FloHimself .
mikeserv
@ Mikeserv, musimy mieć różne awks. Na twojej próbie otrzymuję 1,4s z gawk (4s dla Janis), 0,9s z mawk, 1,7s z tym rozwiązaniem perla, 2,3s z kos ', 4,5s z twoim (GNU sed) i 1,4s z twoim ( GNU sed) i moja sugerowana poprawa (i 0,5 s dla rozwiązania C).
Stéphane Chazelas
@mikeserv, ah! oczywiście z twoim podejściem, lokalizacja robi różnicę. Zmniejszono tutaj z 4,5 do 2,3 s przy zmianie z UFT-8 na C.
Stéphane Chazelas
3

Napisałem prosty skrypt Perla, aby to zrobić:

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 ++;
    }
}
  • Masa F.txt
  • Masa L.txt
  • Przechowuje każdą linię L.txtw tablicy
  • Odczytuje F.txtwiersz po wierszu, śledząc jego bieżący numer wiersza i bieżący indeks tablicy; zwiększa F.txtbieżący numer linii; jeśli F.txtbieżący numer linii pasuje do zawartości tablicy przy bieżącym indeksie tablicy, drukuje bieżącą linię i zwiększa indeks

Uwagi dotyczące kosztów i złożoności :

Biorąc pod uwagę koszt wykonania przypisań, koszt dokonania porównań i koszt wydrukowania linii, biorąc pod uwagę N 1 jako liczbę linii F.txti N 2 jako liczbę linii L.txt, whilepętla działa najwyżej N 1 razy, prowadząc do przypisań 2N 1 + N 2 (oczywiście przy założeniu N 1 > N 2 ), porównań 2N 1 i wydruków N 2 ; podany jako równy koszt każdej operacji, całkowity koszt uruchomienia whilepętli wynosi 4N 1 + 2N 2 , co prowadzi do złożoności skryptu O (N).

Przetestuj plik wejściowy o wielkości 10 milionów wierszy :

Używając pliku 10 milionów linii F.txtzawierającego losowe linie o długości 50 znaków oraz pliku 10 milionów linii L.txtzawierającego liczby od 1 do 10000000 (najgorszy scenariusz):

~/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
źródło
2

Ten roztwór perlowy jest szybszy niż inne roztwory awk lub perl o około 20%, ale owalnie nie tak szybki jak roztwór w 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 obsługuje Monikę
źródło
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

Ponieważ L.txt jest posortowane, możesz użyć join. Po prostu numeruj każdą linię w F.txt, połącz dwa pliki, a następnie usuń numer linii. Nie są potrzebne duże pliki pośrednie.

W rzeczywistości powyższe spowoduje zmieszanie linii danych, zastępując całą białą spację jedną spacją. Aby linia pozostała nienaruszona, musisz wybrać jako separator znak, który nie pojawia się w twoich danych, np. „|”. Cmd jest wtedy

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

Pierwszy sed usuwa początkowe spacje z wyjścia „cat -n” i zastępuje tabulator. Drugi sed usuwa numer linii i „|”.

meuh
źródło
Obawiam się, że to nie zadziała w przypadku większych plików. Potrzebuje <10 linii. Miałem ten sam pomysł i próbowałem, join L.txt <(nl F.txt )ale nie działa na dużych plikach. Nawiasem mówiąc, witamy na stronie, często nie otrzymujemy tak jasnych i dobrze sformatowanych odpowiedzi od nowych użytkowników!
terdon
@terdon, Tak, szkoda, że join/ commnie może działać z sortowaniem numerycznym.
Stéphane Chazelas,
@terdon: Śledziłem twoją szansę (teraz usunięty) i próbowałem join -t' ' <(<L.txt awk '{printf("%010s\n",$0)}') <(<F.txt awk '{printf("%010s %s\n",NR,$0)}') | cut -d' ' -f2-- To było powolne! - i nawet kiedy wprowadziłem przygotowane pliki z odpowiednimi klawiszami 0-padded join -t' ' L.txt F.txt | cut -d' ' -f2- , to wciąż było wolne (nie uwzględniając czasu przygotowania) - wolniej niż awkodpowiedź @Janis (gdzie zamieściłem komentarz na temat faktycznych czasów obu odpowiedź jego i @ StéphaneChazelas
Peter.O
@ Peter.O tak. Wypróbowałem podobne podejście, które pozwala uniknąć jednego z problemów, ale nie mogłem znaleźć sposobu, aby zarówno działał, jak i był tego wart.
terdon
@terdon i inni: Rzeczywisty czas zastąpienia procesujoin + był w porównaniu do Stéphane'a Chazelasa wykorzystującego 50 milionów linii, 500 milionów linii. awk printf real 20m11.663s user 19m35.093s sys 0m10.513sreal 2m11.637s user 2m2.748s sys 0m6.424sLF
Peter.O
0

Dla kompletności kolejna próba joinrozwiązania:

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

Działa to poprzez sformatowanie kolumny z numerem wiersza, która się łączy, jako stałej długości z wiodącymi zerami, dzięki czemu liczby mają zawsze 15 cyfr. To omija problem łączenia, który nie lubi normalnej numerycznej kolejności sortowania, ponieważ kolumna została skutecznie zmuszona do sortowania słownikowego. nlsłuży do dodawania numerów wierszy w tym formacie do F.txt. Niestety sednależy go użyć do sformatowania numeracji w języku L.txt.

To podejście wydaje się działać poprawnie na danych testowych wygenerowanych przy użyciu metody @ mikeserv. Ale wciąż jest bardzo wolny - rozwiązanie c jest 60 razy szybsze na moim komputerze. około 2/3 czasu spędza w, seda 1/3 w join. Być może istnieje lepszy wyraz sed ...

Cyfrowa trauma
źródło
Ok - ale dlaczego przygotowujemy wszystkie zera? Próbuję to wyczuć. Jest też nlsuper fajny, ale nie można solidnie użyć go na niesprawdzonych wejściach. Jedną z rzeczy, które sprawiają, że jest tak fajny, jest jego logiczna -d eliminacja stron . Domyślnie, jeśli na wejściu jest jakikolwiek wiersz składający się tylko z ciągów :\` (ale bez / z końcowego grobu) 1, 2, 3 lub 3 razy z rzędu, twoje liczby będą trochę szalone. Eksperymentuj w / it - jest całkiem fajnie. W szczególności spójrz na to, co się dzieje, gdy nl` czyta linię z 1 ciągiem separatora, a następnie kolejny w / 3 lub 2
mikeserv
0

Ponieważ zaakceptowana odpowiedź jest w języku C, uznałem, że można wrzucić tutaj rozwiązanie python:

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

W przypadku korzystania z zewnętrznej biblioteki, takiej jak numpy, rozwiązanie wyglądałoby jeszcze bardziej elegancko:

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
źródło