Loại bỏ trùng lặp trong mỗi dòng của một tập tin


7

Làm thế nào tôi có thể loại bỏ trùng lặp trong mỗi dòng, ví dụ ở đây?

1 1 1 2 1 2 3
5 5 4 1 2 3 3

Tôi muốn có được đầu ra này:

1 2 3 
5 4 1 2 3

Có rất nhiều dòng (100.000) và trong mỗi dòng tôi muốn các giá trị duy nhất. Perl có thể là nhanh nhất, nhưng làm thế nào tôi có thể làm điều đó trong Perl hoặc Bash?

Câu trả lời:


12

Đây là một tùy chọn sử dụng awk:

awk '{ while(++i<=NF) printf (!a[$i]++) ? $i FS : ""; i=split("",a); print ""}' infile > outfile

Chỉnh sửa Cập nhật với ý kiến:

  1. while (++i<=NF)

    Khởi tạo vòng lặp while, trước "i" vì $ 0 là dòng đầy đủ trong awk.

    Vì vậy, nó bắt đầu từ $ 1 (trường đầu tiên). Vòng lặp qua dòng cho đến hết (nhỏ hơn hoặc bằng 'NF' được tích hợp vào awk cho "Số lượng trường"). Dấu tách trường mặc định là khoảng trắng, bạn có thể dễ dàng thay đổi dấu tách mặc định.

  2. printf (!a[$i]++) ? $i FS : ""

    Đây là một hoạt động ternary .

    Vì vậy, nếu đầu vào không nằm trong mảng !a[$i]++, thì nó sẽ in $ i, nếu có, nó sẽ in "". (Bạn có thể xóa !và đảo ngược $i FS : ""nếu bạn không thích nó theo cách này).

  3. i=split("",a)

    Thông thường, đó là một sự chia tách null. Trong trường hợp này, nó đặt lại tôi cho dòng tiếp theo.

  4. print ""

    kết thúc dòng cho đầu ra (thực tế không phải 100% tại sao), nếu không, bạn sẽ có đầu ra là:

    1 2 3 5 4 1 2 3 thay vì
    1 2 3
    5 4 1 2 3


5
Để giúp người đọc hiện tại và tương lai, xin vui lòng cố gắng ghi lại câu trả lời ở một mức độ nào đó. Điều này nhỏ gọn và hiệu quả, nhưng nó khá khó đọc đối với một người không quen sử dụng awkvì nó phụ thuộc vào thứ tự kiểm tra và vận hành, toán tử ternary, split("",a)quirk để đặt lại một mảng (và giá trị trả về của nó để đặt lại i) và các biến đặc biệt NFFS. Một lời giải thích như vậy làm cho một câu trả lời thậm chí còn tốt hơn!
Daniel Andersson

@DanielAndersson Lời xin lỗi của tôi vì lười biếng, cập nhật. Cảm ơn!
nerdwaller

1
nerdwaller: lý do bạn nhận được 1 2 3 5 4 1 2 3 w / o bước 4 là tất cả đầu ra của bạn được thực hiện thông qua printf, w / no \ n từng bị ném vào ...
tink

Bước 2 hoạt động vì nó tăng giá trị mảng với chỉ số của số hiện tại. Nếu chỉ số này trống, kiểm tra trả về !falsevà gia tăng được thực hiện sau khi so sánh. Lần sau khi vòng lặp tìm thấy cùng một số, phép so sánh sẽ trả về !truedo giá trị tương ứng với chỉ mục được đặt thành giá trị lần trước. Trường được tăng trở lại, nhưng "tổng số" này không được sử dụng sau này (mặc dù nó không bị tổn thương).
Daniel Andersson

Trong bước 3, mảng abị xóa cho lần lặp dòng tiếp theo. split("",a)là một tốc ký để xóa một mảng a(xem tài liệu để biết thông báo). Là một tác dụng phụ, thao tác này cũng trả về 0và do iđược đặt thành 0lần lặp tiếp theo, nên split()cuộc gọi được sử dụng thay cho chuyển nhượng thay vì một i=0cuộc gọi riêng biệt , có thể tiết kiệm một số ký tự (có thể dễ đọc).
Daniel Andersson

5

Kể từ khi rubyđi kèm với bất kỳ bản phân phối Linux nào tôi biết:

ruby -e 'STDIN.readlines.each { |l| l.split(" ").uniq.each { |e| print "#{e} " }; print "\n" }' < test

Ở đây, testlà tập tin có chứa các yếu tố.

Để giải thích lệnh này làm gì, mặc dù Ruby gần như có thể được đọc từ trái sang phải:

  • Đọc đầu vào (xuất phát từ < testvỏ của bạn)
  • Đi qua từng dòng của đầu vào
  • Tách dòng dựa trên một không gian ngăn cách các mục, thành một mảng ( split(" "))
  • Lấy các phần tử duy nhất từ ​​mảng này (theo thứ tự)
  • Đối với mỗi thành phần duy nhất, hãy in nó, bao gồm dấu cách ( print "#{e} ")
  • In một dòng mới một khi chúng ta đã hoàn thành với các yếu tố độc đáo

2

Không phải bash thuần túy, nhưng ...:

while read line; do
    printf "%s\n" $line | sort -u | tr '\n' ' '
    echo ''
done < file

Các dòng sẽ được sắp xếp như một sản phẩm phụ.

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.