Phân loại nhóm các đoạn liên tục (cách nhau bởi dòng trống)?


8

Tôi nghĩ bây giờ tôi khá có kinh nghiệm trong việc sắp xếp theo các cột ; tuy nhiên, cho đến nay tôi vẫn chưa tìm thấy cách sắp xếp các hàng liên tục .

Giả sử chúng ta có một tệp văn bản trông như thế này: (tất nhiên là rất đơn giản)

Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

Bây giờ, có thể sắp xếp các dòng chữ và số theo từng khối riêng biệt không? Ý tôi là, để kết quả trông như thế này:

Alpha
Charlie
Delta
Echo

Bravo
Foxtrot
Golf
Hotel

Kể từ những gì tôi tìm thấy trong sorttrang man, điều này có thể không thực hiện được với sortlệnh UNIX tích hợp . Hoặc thậm chí có thể được thực hiện mà không cần phải dùng đến các công cụ bên ngoài / bên thứ ba?

Câu trả lời:


9

awkGiải pháp của Drav là tốt, nhưng điều đó có nghĩa là chạy một sortlệnh trên mỗi đoạn. Để tránh điều đó, bạn có thể làm:

< file awk -v n=0 '!NF{n++};{print n,$0}' | sort -k1n -k2 | cut -d' ' -f2-

Hoặc bạn có thể làm toàn bộ trong perl:

perl -ne 'if (/\S/){push@l,$_}else{print sort@l if@l;@l=();print}
          END{print sort @l if @l}' < file

Lưu ý rằng ở trên, dấu phân cách là các dòng trống (đối với awkmột, các dòng chỉ có perlký tự khoảng trắng hoặc tab, cho một, bất kỳ ký tự khoảng cách ngang hoặc dọc) thay vì các dòng trống. Nếu bạn muốn dòng trống, bạn có thể thay thế !NFbằng !lengthhoặc $0=="", và /\S/bằng /./.


Cảm ơn bạn quá, đặc biệt là awkgiải pháp tránh sortchi phí! Lén lút!
cú pháp

9
awk -v RS= -v cmd=sort '{print | cmd; close(cmd); print ""}' file

Đặt dấu tách bản ghi RSthành một chuỗi trống làm cho bước awk trong đoạn văn bản tại một thời điểm. Đối với mỗi đoạn, hãy đặt đoạn (in $0) thành cmd (được đặt thành sort) và in đầu ra. In ra một dòng trống để phân tách các đoạn đầu ra với a print "".

Nếu chúng tôi đưa ra các ví dụ perl, thì tôi sẽ trình bày một cách tiếp cận khác so với của Stephane:

perl -e 'undef $/; print join "\n", sort (split /\n/), "\n" 
    foreach(split(/\n\n/, <>))' < file

Bỏ đặt dấu tách trường ( undef $/), điều này cho phép chúng ta sử dụng <>và lấy toàn bộ STDIN. Chúng tôi sau splitđó xung quanh \n\n(đoạn văn). foreach"Đoạn", sortcác dòng bằng cách splitxoay quanh các dòng mới, sorting và sau đó joinđưa chúng trở lại với nhau và giải quyết một dấu vết \n.

Tuy nhiên, điều này có một tác dụng phụ là thêm dấu phân cách "đoạn cuối" vào đoạn cuối (nếu trước đó không có đoạn nào). Bạn có thể vượt qua điều đó với một chút ít xinh đẹp:

perl -e 'undef $/; print join "\n", sort (split /\n/) , (\$_ == \$list[-1] ? "" : "\n")
    foreach(@list = split(/\n\n/, <>))' < file

Điều này gán các đoạn cho @list, và sau đó có một "hoạt động ternary" để kiểm tra xem đó có phải là phần tử cuối cùng của foreach( \$_ == \$list[-1]kiểm tra) không. in ""nếu nó là ( ? ...), other ( : ...) in "\n"cho tất cả các "đoạn" khác (các phần tử của @list).


Đây là gọn gàng! Cảm ơn bạn. Bạn có thực sự gọi /usr/bin/sortvới dòng đó hay nó là một awklệnh "sort" tích hợp?
cú pháp

Gọi lệnh sort, do đó yêu cầu đóng (cmd) trên mỗi vòng lặp :)
Drav Sloan

5

Tôi đã viết một công cụ trong haskell cho phép bạn sử dụng sort, shuf, tac hoặc bất kỳ lệnh nào khác trên các đoạn văn bản.

https://gist.github.com/siers/01306a361c22f2de0122
EDIT: công cụ cũng được bao gồm trong repo này: https://github.com/siers/haskell-import-sort

Nó chia văn bản thành các khối, nối các chuỗi con với \0char, chuyển qua lệnh và cuối cùng thực hiện điều tương tự ngược lại.

28-08-2015 : Tôi tìm thấy một cách sử dụng cá nhân khác cho công cụ này - chọn N đoạn văn sau một dòng.

paramap grep -aA2 '^reddit usernames' < ~/my-username-file
reddit usernames

foo
bar
baz

a couple
more of these

4

Nếu bạn có sẵn GNU awk, bạn có thể sắp xếp từng khối bằng hàm dựng sẵn asort(). Một cái gì đó như thế này:

blockort.awk

function sort_n_print(array) {
  asort(array)
  for(i=1; i<=length(array); i++)
    print array[i]
  delete array
}

NF { a[++x] = $0 }

!NF { sort_n_print(a); print }

END { sort_n_print(a) }

Chạy nó như thế này:

awk -f blocksort.awk infile

1

TXR Lisp từng bước:

$ cat data
Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

$ txr -p '(get-lines)' < data
("Echo" "Alpha" "Delta" "Charlie" "" "Golf" "Bravo" "Hotel" "Foxtrot")

$ txr -t '(get-lines)' < data
Echo
Alpha
Delta
Charlie

Golf
Bravo
Hotel
Foxtrot

$ txr -p '(partition* (get-lines) (op where [chain length zerop]))' < data
(("Echo" "Alpha" "Delta" "Charlie") ("Golf" "Bravo" "Hotel" "Foxtrot"))

$ txr -p '[mapcar sort (partition* (get-lines) (op where [chain length zerop]))]' < data
(("Alpha" "Charlie" "Delta" "Echo") ("Bravo" "Foxtrot" "Golf" "Hotel"))

$ txr -p '(interpose (list "") [mapcar sort (partition* (get-lines) (op where [chain length zerop]))])' < data
(("Alpha" "Charlie" "Delta" "Echo") ("") ("Bravo" "Foxtrot" "Golf" "Hotel"))

$ txr -t '(interpose (list "") [mapcar sort (partition* (get-lines) (op where [chain length zerop]))])' < data
Alpha
Charlie
Delta
Echo

Bravo
Foxtrot
Golf
Hotel

Tài liệu tham khảo: get-lines , phân vùng * , op , where , chain , length , zerop , mapcar , interpose .


Lưu ý rằng trong phần [mapcar sort ...]chúng ta có thể thay thế sortbằng một hàm dẫn các chuỗi thông qua một quy trình bên ngoài. Sau đó chúng ta có thể kết thúc với một công cụ phân phối lệnh bên ngoài xử lý văn bản trên các đoạn văn.
Kaz
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.