Chọn dòng từ tệp văn bản có id được liệt kê trong tệp khác


13

Tôi sử dụng rất nhiều grep awk sort trong shell unix của mình để làm việc với các tệp văn bản cột được phân tách bằng thẻ cỡ trung bình (khoảng 10M-100M). Về mặt này, vỏ unix là bảng tính của tôi.

Nhưng tôi có một vấn đề lớn, đó là chọn các bản ghi được cung cấp danh sách ID.

table.csvtệp có định dạng id\tfoo\tbar...ids.csvtệp có danh sách id, chỉ chọn các bản ghi từ table.csvid có trong ids.csv.

loại /programming/13732295/extract-all-lines-from-text-file-basing-on-a-given-list-of-ids nhưng có vỏ, không perl.

grep -Frõ ràng tạo ra dương tính giả nếu id có chiều rộng thay đổi. joinlà một tiện ích tôi không bao giờ có thể tìm ra. Trước hết, nó yêu cầu sắp xếp chữ cái (các tệp của tôi thường được sắp xếp theo số), nhưng ngay cả sau đó tôi không thể làm cho nó hoạt động mà không phàn nàn về thứ tự không chính xác và bỏ qua một số hồ sơ. Vì vậy, tôi không thích nó. grep -f đối với tệp có ^id\t-s rất chậm khi số lượng id lớn. awklà cồng kềnh.

Có giải pháp nào tốt cho việc này không? Bất kỳ công cụ cụ thể cho các tập tin tách tab? Chức năng bổ sung sẽ được chào đón nhất.

CẬP NHẬT: Đã sửa sort->join


Nếu grep -fquá chậm, việc duy trì chiến lược này nghe có vẻ rắc rối hơn giá trị - các biến thể có thể sẽ trở thành con mồi cho cùng các vấn đề về hiệu suất O (N * M). Có lẽ thời gian của bạn sẽ tốt hơn dành cho việc học cách sử dụng SQL DB được chuẩn hóa ...
goldilocks

1
Tại sao không sử dụng tập lệnh Perl từ câu hỏi bạn đã liên kết? Ngoài ra, có thể viết một kịch bản tương tự awk.
cjm

Bash 4 có các mảng kết hợp, đó là những gì bạn cần để phá vỡ các vòng lặp lồng nhau là một ví dụ perl.
goldilocks

1
sortcó thể làm tất cả các loại sắp xếp, số, bảng chữ cái và những người khác. Xem man sort.
terdon

Tôi có một truy vấn ở đây, làm thế nào để chúng tôi làm tương tự nếu tệp nguồn từ nơi chúng tôi muốn trích xuất dữ liệu là một tệp không được phân tách

Câu trả lời:


19

Tôi đoán bạn có nghĩa là grep -fkhông grep -Fnhưng bạn thực sự cần một sự kết hợp của cả hai và -w:

grep -Fwf ids.csv table.csv

Lý do bạn nhận được thông báo sai là (tôi đoán, bạn đã không giải thích) bởi vì nếu một id có thể được chứa trong một cái khác, thì cả hai sẽ được in. -wloại bỏ vấn đề này và -Fđảm bảo các mẫu của bạn được coi là chuỗi, không phải là biểu thức chính quy. Từ man grep:

   -F, --fixed-strings
          Interpret PATTERN as a  list  of  fixed  strings,  separated  by
          newlines,  any  of  which is to be matched.  (-F is specified by
          POSIX.)
   -w, --word-regexp
          Select  only  those  lines  containing  matches  that form whole
          words.  The test is that the matching substring must  either  be
          at  the  beginning  of  the  line,  or  preceded  by  a non-word
          constituent character.  Similarly, it must be either at the  end
          of  the  line  or  followed by a non-word constituent character.
          Word-constituent  characters  are  letters,  digits,   and   the
          underscore.

   -f FILE, --file=FILE
          Obtain  patterns  from  FILE,  one  per  line.   The  empty file
          contains zero patterns, and therefore matches nothing.   (-f  is
          specified by POSIX.)

Nếu dương tính giả của bạn là do ID có thể xuất hiện trong trường không phải ID, hãy lặp qua tệp của bạn:

while read pat; do grep -w "^$pat" table.csv; done < ids.csv

hoặc, nhanh hơn:

xargs -I {} grep "^{}" table.csv < ids.csv

Cá nhân, tôi sẽ làm điều này perlmặc dù:

perl -lane 'BEGIN{open(A,"ids.csv"); while(<A>){chomp; $k{$_}++}} 
            print $_ if defined($k{$F[0]}); ' table.csv

1
+1 Nhưng: Điều gì xảy ra nếu có các lỗi dương tiềm ẩn khớp với id chính xác từ thông minh, chỉ là không có trong cột id? Nếu bạn không thể sử dụng ^với -F, bạn không thể nhắm mục tiêu cột đầu tiên một cách cụ thể.
goldilocks

@goldilocks nếu chúng khớp chính xác, chúng không phải là dương tính giả. Tôi hiểu ý của bạn, nhưng trong trường hợp đó, OP sẽ hiển thị các tệp đầu vào của họ.
terdon

Các ^id\tbit từ OP ngụ ý idcó thể xảy ra trong một cột khác. Nếu không, điều này không thành vấn đề.
goldilocks

@goldilocks điểm công bằng, trả lời chỉnh sửa.
terdon

Cách chúng ta thường làm là tạo các tệp tạm thời (sử dụng awk hoặc sed) đã thêm một ký tự duy nhất (giả sử, điều khiển-A) phân định trường mà chúng ta muốn tìm kiếm, sau đó sử dụng temp mô-tơ-tempotypefile tr -d '\ 001'
Đánh dấu Plotnick

7

Các jointiện ích là những gì bạn muốn. Nó đòi hỏi các tập tin đầu vào phải được sắp xếp theo từ vựng.

Giả sử vỏ của bạn là bash hoặc ksh:

join -t $'\t' <(sort ids.csv) <(sort table.csv)

Không cần sắp xếp, giải pháp awk thông thường là

awk -F '\t' 'NR==FNR {id[$1]; next} $1 in id' ids.csv table.csv

Như tôi đã cố gắng nhưng cuối cùng không thể truyền đạt, tham gia là một loại bùn. Không làm việc cho tôi rất tốt.
alamar

1
joinkhông phải là một loại bùn: lời nói của bạn là bạn không thể hiểu được. Hãy mở mang đầu óc và học hỏi. Bạn đã nhận được kết quả đầu ra nào, và nó khác với những gì bạn mong đợi như thế nào?
glenn jackman

+1, đây là một công việc cho join.
don_crissti

Các awkgiải pháp ở đây là rất nhanh chóng và hiệu quả cho các mục đích của tôi (tôi trích xuất các tập con của một vài trăm từ các tập tin với dòng ~ 100M)
Luke

2

Các câu trả lời cho câu hỏi SO này đã giúp tôi vượt qua những khúc mắc với sự tham gia. Về cơ bản, khi bạn sắp xếp tệp chuẩn bị gửi nó để tham gia, bạn cần đảm bảo rằng bạn đang sắp xếp dựa trên cột bạn đang tham gia. Vì vậy, nếu đó là cái đầu tiên, bạn cần cho nó biết ký tự dấu tách trong tệp và bạn muốn nó sắp xếp trên trường đầu tiên (và chỉ trường đầu tiên). Mặt khác, nếu trường đầu tiên có độ rộng thay đổi (ví dụ), dấu phân cách của bạn và có thể các trường khác có thể bắt đầu ảnh hưởng đến thứ tự sắp xếp.

Vì vậy, hãy sử dụng tùy chọn -t of sort để chỉ định ký tự phân tách của bạn và sử dụng tùy chọn -k để chỉ định trường (nhớ rằng bạn cần trường bắt đầu và kết thúc - ngay cả khi nó giống nhau - hoặc sẽ sắp xếp từ ký tự đó đến cuối dòng).

Vì vậy, đối với một tệp được phân tách bằng tab như trong câu hỏi này, phần sau sẽ hoạt động (với câu trả lời của cấu trúc glenn ):

join -t$'\t' <(sort -d ids.csv) <(sort -d -t$'\t' -k1,1 table.csv) > output.csv

(Để tham khảo, cờ -d có nghĩa là sắp xếp từ điển. Bạn cũng có thể muốn sử dụng cờ -b để bỏ qua khoảng trắng hàng đầu, xem man sortman join).

Ví dụ tổng quát hơn, giả sử bạn đang tham gia hai tệp được phân tách bằng dấu phẩy - input1.csvtrên cột thứ ba và input2.csvtrên cột thứ tư. Bạn đã có thể sử dụng

join -t, -1 3 -2 4 <(sort -d -t, -k3,3 input2.csv) <(sort -d -t, -k4,4 input2.csv) > output.csv

Ở đây -1-2các tùy chọn chỉ định các trường sẽ tham gia vào các tệp đầu vào thứ nhất và thứ hai tương ứng.


0

Bạn cũng có thể sử dụng ruby ​​để làm một cái gì đó tương tự:

ruby -pe 'File.open("id.csv").each { |i| puts i if i =~ /\$\_/ }' table.csv
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.