Lọc tệp theo số dòng


17

Cho một tệp L có một số nguyên không âm trên mỗi dòng và tệp văn bản F, cách nhanh nhất để chỉ giữ các dòng đó trong F, có số dòng xuất hiện trong tệp L là gì?

Thí dụ:

$ 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

Tôi đang tìm kiếm một lệnh có thể xử lý tệp L có 500 triệu mục trở lên; tập tin L được sắp xếp theo số lượng.

Lưu ý: Tôi mới thực hiện được một nửa command-in-questionnhưng tôi chỉ tự hỏi, liệu người ta có thể sử dụng một số công cụ Unix ở đây không.


Cập nhật: Cảm ơn tất cả các câu trả lời, tôi đã học được rất nhiều ngày hôm nay! Tôi muốn chấp nhận nhiều hơn một câu trả lời, nhưng điều đó là không thể.


Có bao nhiêu dòng trong tập tin F?
Peter.O

1
Có ít nhất nhiều dòng trong F như trong L.
miku

Câu trả lời:


8

Với việc Cbỏ qua các thông báo lỗi có ý nghĩa:

#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
Đây là câu trả lời hiệu quả nhất ở đây. Ít nhất, nó là như vậy bởi các bài kiểm tra của tôi. Trong trường hợp bất cứ ai quan tâm, tôi biên dịch nó như : xsel -bo | cc -xc - -o cselect. Và nó chỉ hoạt động - nó chỉ cần hai lib.
mikeerv

1
Cảm ơn, điều này thật tuyệt! Tôi hy vọng bạn không phiền, nhưng tôi gói mã của bạn vào một công cụ nhỏ .
miku

1
@miku Đi tiếp, tôi rất vui vì tôi có thể giúp. Tôi nhận thấy bạn đã tăng LINE_MAXtrong phiên bản của mình, vì vậy bạn có thể làm việc với các dòng rất lớn trong các tệp của mình. Tôi đã cập nhật A với một phiên bản bằng cách sử dụng getline()để loại bỏ giới hạn kích thước dòng.
FloHimelf

@FloHimelf, tốt, cảm ơn một lần nữa :) Thật vậy, một số dòng đầu vào có thể vượt quá LINE_MAX, vì vậy getlinecó vẻ đúng.
miku

10

Tôi sẽ sử dụng awk, nhưng không lưu trữ toàn bộ nội dung L.txttrong bộ nhớ và thực hiện tra cứu băm không cần thiết ;-).

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"

Chính xác, tôi đã thử bản đồ băm và chúng sẽ vượt quá bộ nhớ; bitcoin sẽ mua cho bạn nhiều khoảng không hơn; nhưng bằng cách sử dụng thực tế, rằng đầu vào được sắp xếp, bạn có thể loại bỏ hoàn toàn vấn đề (không gian) này.
miku

1
@Janis; không phải chỉ là một trường hợp thực hành mã hóa tốt tiêu chuẩn: đừng cứng mã chữ - thay vào đó hãy sử dụng các biến ... (linh hoạt hơn và ít lỗi hơn và dễ bảo trì hơn)
Peter.O

1
@ StéphaneChazelas: Nó cần pre-loop khởi động của n, nếu không (như-là) nó nhớ 1trongL.txt
Peter.O

1
@ Peter.O, rất tiếc, đó là những gì tôi đã cố gắng giải quyết bằng NR> = n, nhưng điều đó đã sai. Nên tốt hơn bây giờ.
Stéphane Chazelas

1
@Janis, ý tưởng là nếu mã đó được nhúng vào một command-in-questiontập lệnh, thì bạn không thể có tên tệp được nhúng trong mã. -v list="$opt_x"cũng không hoạt động vì quá trình xử lý dấu gạch chéo ngược được thực hiện bởi awk trên nó. Đó là lý do tại sao tôi sử dụng ENVIRON thay vì ở đây.
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

Điều đó sẽ hoạt động khá nhanh (một số bài kiểm tra thời gian được bao gồm bên dưới) với đầu vào có kích thước bất kỳ. Một số lưu ý về cách:

  • export LC_ALL=C
    • Bởi vì mục đích của thao tác sau là lấy toàn bộ tệp ./Fxếp chồng lên nhau với ./Ltệp lineno của nó, các ký tự duy nhất chúng ta thực sự cần phải lo lắng là các [0-9]chữ số ASCII và :dấu hai chấm.
    • Vì lý do đó, việc lo lắng về việc tìm kiếm 11 ký tự đó trong một bộ 128 sở hữu sẽ đơn giản hơn so với nếu UTF-8 có liên quan.
  • grep -n ''
    • Điều này chèn chuỗi LINENO:vào đầu của mỗi dòng trong stdin - hoặc <./F.
  • sort -t: -nmk1,1 ./L -
    • sortbỏ qua việc sắp xếp các tập tin đầu vào của nó, và thay vào đó (chính xác) cho rằng chúng được sắp đặt trước và sắp xếp -mchúng -numericallytheo thứ tự được sắp xếp, về cơ bản bỏ qua mọi thứ ngoài bất kỳ ký tự đại tràng nào có thể -k1,1xảy ra -t:.
    • Mặc dù điều này có thể yêu cầu một số không gian tạm thời để thực hiện (tùy thuộc vào việc một số trình tự có thể xảy ra cách nhau bao xa) , nhưng nó sẽ không đòi hỏi nhiều so với một loại thích hợp, và nó sẽ rất nhanh vì nó liên quan đến việc quay lui bằng không.
    • sortsẽ tạo ra một luồng đơn trong đó bất kỳ dòng lineno nào ./Lsẽ ngay lập tức đi trước các dòng tương ứng ./F. ./LCác dòng luôn luôn đến đầu tiên vì chúng ngắn hơn.
  • sed /:/d\;n
    • Nếu dòng hiện tại khớp với /:/dấu hai chấm dtừ đầu ra. Khác, tự động in dòng hiện tại và next.
    • Và do đó , đầu ra của sedprunes chỉ cho các cặp dòng liên tiếp không khớp với dấu hai chấm và dòng sau - hoặc, chỉ một dòng từ và sau đó đến dòng tiếp theo.sort./L
  • cut -sd: -f2-
    • cut -snhấn mạnh từ đầu ra của những dòng đầu vào không chứa ít nhất một trong các -d:chuỗi loại bỏ của nó - và vì vậy ./Lcác dòng của nó được cắt tỉa hoàn toàn.
    • Đối với những dòng này, ield :được phân định bằng dấu hai chấm đầu tiên của chúng -fsẽ cutbiến mất - và tất cả các greplineno được chèn vào.

kiểm tra đầu vào nhỏ

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

... tạo ra 5 dòng đầu vào mẫu. Sau đó...

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

... bản in ...

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

kiểm tra thời gian lớn hơn

Tôi đã tạo ra một vài tệp khá lớn:

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

... trong đó đặt 5 triệu dòng /tmp/Fvà 1,5 triệu dòng được chọn ngẫu nhiên vào đó /tmp/L. Sau đó tôi đã làm:

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

Nó được in:

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

(Tôi đã thêm dấu gạch chéo ngược ở đó)

Trong số các giải pháp hiện đang được cung cấp ở đây, đây là giải pháp nhanh nhất trong số chúng nhưng một giải pháp khi đọ sức với bộ dữ liệu được tạo ở trên trên máy của tôi. Trong số những người khác chỉ có một người đến gần để tranh giành vị trí thứ hai, và đó là meuh perl ở đây .

Đây không phải là giải pháp ban đầu được đưa ra - nó đã giảm một phần ba thời gian thực hiện nhờ lời khuyên / cảm hứng được cung cấp bởi người khác. Xem lịch sử bài viết cho các giải pháp chậm hơn (nhưng tại sao?) .

Ngoài ra, điều đáng chú ý là một số câu trả lời khác rất có thể tranh luận tốt hơn nếu không phải là kiến ​​trúc đa cpu của hệ thống của tôi và việc thực hiện đồng thời từng quy trình trong đường ống đó. Tất cả đều hoạt động cùng một lúc - mỗi lõi trên bộ xử lý riêng - truyền xung quanh dữ liệu và thực hiện phần nhỏ của chúng. Nó thật tuyệt

nhưng giải pháp nhanh nhất là ...

Nhưng nó không phải là giải pháp nhanh nhất. Giải pháp nhanh nhất được cung cấp ở đây, bàn tay xuống, là chương trình C . Tôi gọi nó cselect. Sau khi sao chép nó vào clipboard X của tôi, tôi đã biên dịch nó như sau:

xsel -bo | cc -xc - -o cselect

Sau đó tôi đã làm:

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

... và kết quả 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
Bạn có thể làm cho nó nhanh hơn đáng kể (gần như nhanh như của tôi trên các hệ thống đa lõi) sed -ne'/:/!{n;p;}' | cut -d: -f2-thay vìsed -ne'/:/!N;/\n/s/[^:]*://p'
Stéphane Chazelas

@ StéphaneChazelas - bạn có thể nhận được kết quả tốt hơn nếu bạn chuyển đổi sed- sedtôi đang sử dụng là gia truyền sed- bạn có thể thấy aliasgiá trị trong timekết quả. Nhân tiện, gói gia truyền của tôi, được biên dịch tĩnh đối với một libc musl - việc thực hiện regex dựa trên TRE . Khi tôi chuyển nó sang GNU sed- và chạy nó mà không cần cut- nó sẽ thêm một giây đầy đủ vào thời gian hoàn thành (2,8 giây) - kết hợp nó với hơn một phần ba. Và đó chỉ nhanh hơn 3 giây so với của bạn trên hệ thống của tôi.
mikeerv

1
sort -mntrái ngược với sort -nmk1,1có thể tốt hơn khi bạn không cần thực hiện việc chia tách ở đây (chưa được thử nghiệm)
Stéphane Chazelas

@ StéphaneChazelas - vâng, tôi cũng nghĩ như vậy và tôi đã thử mọi cách. -nlà chỉ để thực hiện chuỗi số đầu tiên trên một dòng vì vậy tôi đã tìm ra, ok -mnhoặc -nm, vì bất kỳ lý do gì, lần duy nhất nó giảm xuống dưới 2 giây trong thời gian hoàn thành là khi tôi thêm vào tất cả các tùy chọn. Điều đó thật kỳ lạ - và đó là lý do ngày hôm qua tôi đã không giải quyết vấn đề -mngay từ đầu - tôi biết những gì tôi đang nói, nhưng dường như nó chỉ là một thứ tối ưu hóa tự động. Điều thú vị là, gia truyền sort-ztùy chọn độ dài chuỗi chỉ áp dụng cho -[cm]....
mikeerv

-nkhông phải là chuỗi số đầu tiên trên dòng . Nó chỉ coi dòng là một số nên abc 123sẽ là 0. Vì vậy, nó không thể kém hiệu quả hơn với-t: -k1,1
Stéphane Chazelas

9

Tôi sẽ sử dụng awk:

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

Cập nhật: Tôi đã thực hiện các biện pháp hiệu suất; có vẻ như phiên bản này có quy mô tốt hơn với các tập dữ liệu rất lớn (như trường hợp với các yêu cầu đã nêu), vì việc so sánh rất nhanh và bù đắp cho nỗ lực cần thiết để xây dựng bảng băm.


1
@miku; Vâng, đó là một giải pháp nhỏ gọn tốt đẹp. Nhưng một lời cảnh báo; không phải tất cả các awks có thể xử lý các tập dữ liệu lớn như vậy. - Tôi đang sử dụng GNU awkvà không có vấn đề gì; bài kiểm tra với 500 triệu dòng dữ liệu cần 7 phút.
Janis

1
Điều này khá chậm (bằng cách so sánh) real 16m3.468s- user 15m48.447s- sys 0m10.725s. Nó đã sử dụng 3,3 GB RAM để kiểm tra kích thước 1/10 Lvới 50.000.000 dòng; và Fvới 500.000.000 dòng - so với thời gian dành cho người phản ứng tuyệt vời của Stéphane Chazelas: real 2m11.637s- user 2m2.748s- sys 0m6.424s- Tôi không sử dụng hộp nhanh, nhưng so sánh rất thú vị.
Peter.O

@ Peter.O; Cảm ơn dữ liệu! Một tốc độ chậm hơn là mong đợi, với điều kiện (trong trường hợp thử nghiệm của riêng tôi), nửa tỷ dòng được lưu trữ trong một mảng kết hợp. (Đó là lý do tại sao tôi nhận xét "(+1)" ở trên cho đề xuất của Stephane.) - Mặc dù tôi rất ngạc nhiên khi giải pháp ngắn gọn này vẫn xử lý 1 triệu dòng mỗi giây! Tôi nghĩ rằng nó làm cho mẫu mã này (vì nó đơn giản!) Là một tùy chọn khả thi và đặc biệt trong các trường hợp có kích thước dữ liệu cực ít.
Janis

Nó chắc chắn là một giải pháp khả thi. Trên dữ liệu thử nghiệm tôi đã sử dụng (5 triệu dòng / 1,5 triệu L), dữ liệu của bạn đã hoàn thành sau hơn 4 giây - chỉ một giây sau câu trả lời của Stephane. Các mã được sử dụng để gen tập kiểm tra là trong câu trả lời của tôi, nhưng nó chủ yếu chỉ seqđầu ra và sau đó một nhỏ hơn, lựa chọn ngẫu nhiên tập hợp con của cùng một trong L .
mikeerv

1
Tôi vừa thực hiện thêm một số biện pháp hiệu suất với kích thước tệp dữ liệu là 500 triệu dòng và kích thước tệp chính là 50 triệu và tương ứng. 500 triệu dòng, với một quan sát đáng chú ý. Với tệp khóa nhỏ hơn, thời gian là 4 phút (Stephane) so với 8 phút (Janis), trong khi với tệp khóa lớn hơn, 19 phút (Stephane) so với 12 phút (Janis).
Janis

3

Chỉ để hoàn chỉnh: chúng ta có thể hợp nhất tập lệnh awk xuất sắc trong câu trả lời của Stéphane Chazelas và tập lệnh perl trong câu trả lời của kos nhưng không giữ toàn bộ danh sách trong bộ nhớ, với hy vọng rằng perl có thể nhanh hơn awk. (Tôi đã thay đổi thứ tự các đối số để phù hợp với câu hỏi ban đầu).

#!/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;
        }
    }
}

Đây là cách nhanh hơn awk. Nó nhanh như của tôi - Tôi đã thử nghiệm cả ba lần vừa rồi và mỗi lần tôi xử lý thử nghiệm dòng 5 triệu của tôi trong 1,8 ... giây và mỗi lần 1,9 ... giây của bạn. Mã gen testset nằm trong câu trả lời của tôi nếu bạn quan tâm, nhưng vấn đề là nó rất tốt. Hơn nữa, đầu ra là chính xác - Tôi vẫn không thể thực hiện được awkcông việc ... Tuy nhiên, cả hai câu trả lời của chúng tôi đều bị xấu hổ bởi FloHimelf .
mikeerv

@mikeerv, chúng ta phải có awks khác nhau . Trên mẫu của bạn, tôi nhận được 1,4 giây với gawk (4 giây cho Janis '), 0,9 với mawk, 1,7 giây với giải pháp perl này, 2,3 giây với kos', 4,5 giây với của bạn (GNU sed) và 1,4 giây với của bạn ( GNU sed) và cải tiến đề xuất của tôi (và 0,5 giây cho giải pháp C).
Stéphane Chazelas

@mikeerv, ah! Tất nhiên với cách tiếp cận của bạn, miền địa phương làm cho một sự khác biệt. Giảm từ 4,5 xuống 2,3 giây tại đây khi chuyển từ UFT-8 sang C.
Stéphane Chazelas

3

Tôi đã viết một kịch bản Perl đơn giản để làm điều đó:

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 ++;
    }
}
  • Tải F.txt
  • Tải L.txt
  • Lưu trữ mỗi dòng L.txtthành một mảng
  • Đọc F.txttừng dòng, theo dõi số dòng hiện tại của nó và chỉ số mảng hiện tại; tăng F.txtsố dòng hiện tại; nếu F.txtsố dòng hiện tại khớp với nội dung của mảng ở chỉ mục mảng hiện tại, nó sẽ in dòng hiện tại và tăng chỉ mục

Chi phí và cân nhắc phức tạp :

Xem xét chi phí để thực hiện các bài tập, chi phí để so sánh và chi phí để in các dòng, được N 1 là số dòng trong F.txtvà N 2 là số dòng trong L.txt, whilevòng lặp chạy nhiều nhất N 1 lần, dẫn đến các bài tập 2N 1 + N 2 (rõ ràng giả sử N 1 > N 2 ), so sánh 2N 1 và in N 2 ; được tính bằng chi phí của mỗi thao tác, tổng chi phí để chạy whilevòng lặp là 4N 1 + 2N 2 , dẫn đến sự phức tạp của tập lệnh O (N).

Kiểm tra tệp đầu vào 10 triệu dòng :

Sử dụng F.txttệp 10 triệu dòng chứa các dòng dài 50 ký tự ngẫu nhiên và tệp 10 triệu dòng L.txtchứa các số từ 1 đến 10000000 (trường hợp xấu nhất):

~/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

Giải pháp perl này nhanh hơn các giải pháp awk hoặc perl khác khoảng 20% ​​hoặc hơn, nhưng không nhanh như giải pháp trong 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

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

Vì L.txt được sắp xếp, bạn có thể sử dụng tham gia. Chỉ cần đánh số từng dòng trong F.txt, tham gia hai tệp, sau đó xóa số dòng. Không có tập tin trung gian lớn là cần thiết.

Trên thực tế, ở trên sẽ xử lý các dòng dữ liệu của bạn bằng cách thay thế tất cả khoảng trắng bằng một khoảng trắng. Để giữ nguyên dòng, bạn cần chọn làm dấu phân cách một số ký tự không xuất hiện trong dữ liệu của bạn, ví dụ: "|". Các cmd là sau đó

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

Sed đầu tiên loại bỏ khoảng trắng hàng đầu khỏi đầu ra "cat -n" và thay thế tab. Sed thứ hai loại bỏ số dòng và "|".


Tôi sợ điều này sẽ không hoạt động trên các tập tin lớn hơn. Nó cần <10 dòng. Tôi có cùng ý tưởng và đã thử join L.txt <(nl F.txt )nhưng nó sẽ không hoạt động trên các tệp lớn. Nhân tiện, chào mừng bạn đến với trang web, chúng tôi không thường xuyên nhận được câu trả lời rõ ràng và được định dạng tốt như vậy từ người dùng mới!
terdon

@terdon, Có, thật xấu hổ khi join/ commkhông thể làm việc với đầu vào được sắp xếp bằng số.
Stéphane Chazelas

@terdon: Tôi đã theo dõi sự dẫn dắt của bạn (hiện đã bị xóa) và đã thử join -t' ' <(<L.txt awk '{printf("%010s\n",$0)}') <(<F.txt awk '{printf("%010s %s\n",NR,$0)}') | cut -d' ' -f2-- Thật chậm! - và ngay cả khi tôi cho ăn các tệp được chuẩn bị với các phím 0 đệm phù hợp join -t' ' L.txt F.txt | cut -d' ' -f2- , nó vẫn chậm (không bao gồm thời gian chuẩn bị) - chậm hơn so với awkcâu trả lời của @Janis (nơi tôi đã đăng nhận xét về thời gian thực tế cho cả hai câu trả lời của anh ấy và @ StéphaneChazelas
Peter.O

@ Peter.O vâng. Tôi đã thử một cách tiếp cận tương tự để tránh một trong những điều tồi tệ nhưng tôi không thể tìm ra cách để làm cho nó vừa hoạt động vừa có giá trị.
terdon

@terdon và những người khác: Thời gian thực tế cho sự thay thế quá trìnhjoin + là so với Stéphane Chazelas ' bằng cách sử dụng 50 triệu dòng,awk printf real 20m11.663s user 19m35.093s sys 0m10.513sreal 2m11.637s user 2m2.748s sys 0m6.424sLF 500 triệu dòng.
Peter.O

0

Để hoàn thiện, một nỗ lực khác tại joingiải pháp:

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

Điều này hoạt động bằng cách định dạng cột số dòng tham gia hoạt động với độ dài cố định với các số 0 đứng đầu, sao cho các số luôn dài 15 chữ số. Điều này giải quyết vấn đề tham gia không thích thứ tự sắp xếp số thông thường, vì cột hiện đã bị buộc phải sắp xếp từ điển. nlđược sử dụng để thêm số dòng ở định dạng này vào F.txt. Thật không may sedcần phải được sử dụng để định dạng lại việc đánh số trong L.txt.

Cách tiếp cận này có vẻ hoạt động tốt trên dữ liệu thử nghiệm được tạo bằng phương pháp @ mikeerv. Nhưng nó vẫn rất chậm - giải pháp c nhanh hơn 60 lần trên máy của tôi. khoảng 2/3 thời gian được dành cho sedvà 1/3 trong join. Có lẽ có một biểu hiện sed tốt hơn ...


Ok - nhưng tại sao chúng ta lại chuẩn bị tất cả các số không? Tôi đang cố gắng để có được cảm giác về điều này. Ngoài ra, nlnó rất tuyệt, nhưng bạn không thể sử dụng nó một cách mạnh mẽ cho đầu vào chưa được kiểm tra. Một trong những điều làm cho nó rất tuyệt là trình loại bỏ trang logic của nó -d. Theo mặc định, nếu có bất kỳ dòng nào trong đầu vào chỉ bao gồm các chuỗi :\` (nhưng không có dấu vết) 1, 2, 3 hoặc ba lần liên tiếp, số đếm của bạn sẽ hơi điên. Thử nghiệm với nó - nó khá gọn gàng. Đặc biệt hãy xem điều gì xảy ra khi nl` đọc một dòng có 1 chuỗi phân cách và sau đó là một w / 3 hoặc 2
mikeerv

0

Vì câu trả lời được chấp nhận là bằng C, tôi cho rằng không nên ném giải pháp trăn lên đây:

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

Nếu sử dụng một thư viện bên ngoài như numpy, một giải pháp sẽ trông thậm chí thanh lịch hơn:

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.