thứ tự ngược của đoạn văn trong tập tin


8

Tôi có một tệp chứa văn bản trong đoạn văn (các dòng có văn bản được phân tách bằng một hoặc nhiều dòng trống). Tôi muốn đảo ngược thứ tự các đoạn văn (tức là đoạn cuối sẽ trở thành đoạn đầu tiên, ...), tốt nhất là bằng cách sử dụng sed.

Tôi đang tìm kiếm một lệnh sed sẽ làm một tập tin các đoạn văn, những gì tacsẽ làm với một tập tin các dòng.

Câu trả lời:


6

Việc sử dụng sedkhông hoàn toàn đơn giản như được đề cập bởi Joseph R .. Tuy nhiên, bạn có thể nói:

sed '/./{H;d;};x;s/\n/={NL}=/g' inputfile | \
sed -e 's/^={NL}=//' -e '1!G;h;$!d' | \
sed G | sed 's/={NL}=/\'$'\n/g'

Cho một đầu vào mẫu:

Para 1 line 1
Para 1 line 2
Para 1 line 3

Para 2 line 1
Para 2 line 2
Para 2 line 3

Para 3 line 1
Para 3 line 2
Para 3 line 3

điều này sẽ tạo ra:

Para 3 line 1
Para 3 line 2
Para 3 line 3

Para 2 line 1
Para 2 line 2
Para 2 line 3

Para 1 line 1
Para 1 line 2
Para 1 line 3

Điều đáng nói là giải pháp này (cũng như giải pháp thay thế Perl) yêu cầu một dòng trống ở cuối tệp đầu vào để hoạt động như mong đợi.


6

Giải pháp này sử dụng cả hai tacperlđể đọc một đoạn văn tại một thời điểm. Nó không yêu cầu đọc toàn bộ tập tin vào bộ nhớ.

tac file | perl -00 -lpe '$_ = join "\n", reverse split /\n/'

Đảo ngược tất cả các dòng của tệp, sau đó cho mỗi đoạn đảo ngược, đảo ngược các dòng.


Điều này trông rất thanh lịch và hiệu quả. Tuy nhiên, giải pháp này cũng ngưng tụ nhiều dòng trống (tức là tách) thành một
Martin Vegter 16/214

3

Có thể có một cách để làm điều này với sed, nhưng tôi nghi ngờ nó sẽ đơn giản. Đây là cách tôi sẽ làm điều đó trong Perl:

perl -n00e 'push @paragraphs,$_; END{print for reverse @paragraphs}' your_file

Điều này hoạt động vì xác định dấu tách bản ghi đầu vào là ký tự null ( -00) cho Perl hoạt động ở chế độ đoạn. Định nghĩa của Perl về đoạn 1 khớp chính xác với định nghĩa của bạn.


1 Nhìn dưới tiêu đềOther values for $/


điều này thực sự hoạt động. Vấn đề nhỏ duy nhất là, nó không bảo tồn nhiều dòng trống ngăn cách các đoạn văn. Thay vào đó, tất cả các đoạn được phân tách bằng chính xác một dòng trống.
Martin Vegter

1

Nếu các đoạn văn của bạn luôn được phân tách bằng một dòng trống duy nhất:

sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\n | \
sed 's/^\x03//;1s/\x03$//;1!G;h;$!d;$a\' | tr $'\003' \\n

Thật dễ dàng để xem nó hoạt động như thế nào nếu bạn chia nó thành từng mảnh và chạy sed '/^$/s/^/\x02/' infilesau đó sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\n...


Nếu các đoạn văn của bạn được phân tách bằng một hoặc nhiều dòng trống, vd

Para 1 line 1
Para 1 line 2

Para 2 line 1


Para 3 line 1
Para 3 line 2

Para 4 line 1
Para 4 line 2



Para 5 line 1

và bạn muốn đảo ngược thứ tự các đoạn văn nhưng giữ nguyên thứ tự của "các khối trống", bạn có thể đọc tệp hai lần: Thứ
nhất: biến các đoạn văn thành các dòng đơn (loại bỏ các khối trống ở giữa) và đảo ngược chúng và
thứ 2: biến các khối trống thành các dòng đơn, "lập chỉ mục" số lượng dòng trống trong mỗi khối (và loại bỏ các dòng không trống)
sau đó pastekết quả và xử lý đầu ra để khôi phục dòng mới:

paste -d $'\004' <(sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\n | \
sed -e '/^\x03$/d;s/^\x03//;s/\x03$//;1!G;h;$!d;$a\') \
<(sed -E '/^$/!d;//{:a;N;/^(\n){1,}$/ba;s/\n/\x02/g;s/(.*)\x02.*/\1/}' infile) \
| sed '$!s/\x04/\n/;$s/\x04$//' | tr $'\003\002' \\n\\n

đầu ra nào:

Para 5 line 1

Para 4 line 1
Para 4 line 2


Para 3 line 1
Para 3 line 2

Para 2 line 1



Para 1 line 1
Para 1 line 2

Nếu bạn không nhớ một dòng dấu phụ trong đầu ra, bạn có thể bỏ dòng cuối cùng sed:

paste -d $'\n' <(sed '/^$/s/^/\x02/' infile | tr \\n$'\002' $'\003'\\n | \
sed -e '/^\x03$/d;s/^\x03//;s/\x03$//;1!G;h;$!d;$a\') \
<(sed -E '/^$/!d;//{:a;N;/^(\n){1,}$/ba;s/\n/\x02/g;s/(.*)\x02.*/\1/}' infile) | \
tr $'\003\002' \\n\\n

Chúng giả định rằng dòng đầu tiên và dòng cuối cùng không trống (và không \x02, \x03hoặc \x04trong đầu vào).


1

Bạn CÓ THỂ làm điều đó với một ví dụ duy nhất sed; không có đường ống cần thiết. Vì sedchỉ thực hiện một lần đi qua tài liệu và vì phần của tệp được yêu cầu khi bắt đầu xuất là ở cuối tệp, nên nó sẽ yêu cầu giữ toàn bộ tệp trong bộ nhớ trong sed(trong không gian giữ). không có quy mô tốt. Nhưng nó trả lời chính xác câu hỏi:

:getpara
   ${
      s/$/\
/
      G
      s/\n\n$//
      q
   }
   N
   /\n$/!bgetpara
G
h
$!d
s/\n\n$//
q

Nếu không có dòng mới, điều này vẫn hoạt động tốt. Nếu có một dòng mới duy nhất, nó bị chặn trong đầu ra (nghĩa là sẽ không có một dòng mới hàng đầu trong đầu ra). Nếu có (ví dụ) 5 dòng mới ở đầu vào, sẽ có 4 dòng mới hàng đầu trong đầu ra.

Khoảng cách giữa các đoạn được bảo tồn.

Khoảng trắng trên một dòng trống khác KHÔNG được coi là ngắt đoạn, nhưng đó là một tính năng, không phải là lỗi. :)

Bạn cũng có thể làm điều này như một lớp lót ít đọc hơn nhiều:

sed ':k;${;s/\(\(\n\).*\)$/\1\2/;G;s/\n\n$//;q;};N;/\n$/!bk;G;h;$!d;s/\n\n$//;q' inputfile

Mặc dù điều này chỉ hoạt động với GNU sed. (Lưu ý việc sử dụng khéo léo của backreferences để thực hiện s/$/\n/. Nếu không có này nó sẽ không là một chữ một lót vì nó sẽ chứa một dấu chéo ngược-xuống dòng.)


Vì vậy, bạn slurp các tập tin, phải không? có vẻ như bạn đặt toàn bộ trong không gian giữ. w / G;h. bạn có thể đề cập một vài điều về các hạn chế đầu vào hoặc tương tự.
mikeerv

Tôi đã không kiểm tra một lớp vì tôi đang làm việc từ máy Mac của mình và không có GNU sedtiện dụng, nhưng phiên bản tập lệnh chắc chắn giữ được các khoảng trống giữa các đoạn. Tôi chỉ thử nó trên đầu vào của bạn. Bạn đã kiểm tra phiên bản kịch bản chưa?
tự đại diện

@mikeerv: Chắc chắn là đúng. (Sẽ cập nhật vào tối nay.)
Wildcard

0
gem install facets

ruby -r facets/string \
     -e 'puts $stdin.read.strip.shatter(/\n\n+/).reverse.join("")' < file

Điều này sẽ duy trì khoảng cách đoạn văn của bạn (trong khi dễ đọc hơn sed:)) Mặc dù vậy, các đạo cụ để giảm bớt cho một câu trả lời tuyệt vời.

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.