Làm thế nào để nối tất cả các dòng với nhau phù hợp với mô hình?


11

Tôi chỉ muốn nối các dòng với nhau cho các dòng có mẫu nhất định (chẳng hạn như ;), tuy nhiên khi sử dụng g/;/jnó không hoạt động như mong đợi trừ khi được gọi là vài lần.

Ví dụ nội dung sau:

a
1;
2;
3;
4;
5;
b
6;
7;
8;
9;
c

khi sử dụng: :g/;/jđầu ra là:

a
1; 2;
3; 4;
5; b
6; 7;
8; 9;
c

hoặc :g/;/-jcho:

a 1; 2; 3; 4; 5;
b 6; 7; 8; 9;
c

tương tự với : :g/;\_.\{-};/j.

Sản lượng dự kiến ​​của tôi là:

a 
1; 2; 3; 4; 5;
b
6; 7; 8; 9;
c

hoặc một cái gì đó tương tự, vì vậy tất cả các dòng chứa mẫu được nối với nhau.

Làm thế nào điều này có thể đạt được?


3
FWIW, :g/;/jkhông hoạt động vì nó được thực hiện trong hai lần: đầu tiên bộ đệm được quét, sau đó lệnh được áp dụng cho các dòng khớp.
romainl

Câu trả lời:


12

Có thể giải thích vấn đề

Tôi nghĩ lý do tại sao :g/;/jkhông hoạt động là vì :glệnh hoạt động với thuật toán 2-pass:

  • trong lần đầu tiên, nó đánh dấu các dòng chứa mẫu ;
  • trong lần chuyển thứ hai, nó hoạt động trên các đường được đánh dấu

Trong lần vượt qua thứ hai, :gnối dòng 1;với dòng 2;1;được đánh dấu trong lần đầu tiên. Tuy nhiên tôi nghi ngờ (không chắc chắn) mà nó không tham gia 1; 2;với 3;vì dòng 2;không tồn tại nữa, nội dung của nó đã được sáp nhập với dòng 1;mà đã được xử lý.

Vì vậy, :gtìm kiếm dòng tiếp theo được đánh dấu trong lần đầu tiên vượt qua ( 3;) và nối nó với dòng sau ( 4;). Sau đó lặp đi lặp lại vấn đề, nó không thể tham gia 3; 4;với 5;vì dòng 4;không tồn tại nữa.

Giải pháp 1 (có vimscript)

Có lẽ bạn có thể gọi một hàm bất cứ khi nào ;tìm thấy một dòng chứa để kiểm tra xem dòng trước đó có chứa dấu chấm phẩy hay không:

function! JoinLines()
    if getline(line('.')-1) =~ ';'
        .-1join
    endif
endfunction

Sau đó sử dụng lệnh toàn cầu sau:

:g/;/call JoinLines()

Hoặc không có chức năng:

:g/;/if getline(line('.')-1) =~ ';' | -j | endif

Giải pháp 2 (không có vimscript)

:g/;/.,/^[^;]*$/-1j

Bất cứ khi nào lệnh toàn cầu :gtìm thấy mẫu, ;nó sẽ thực thi lệnh: .,/^[^;]*$/-1j

Nó có thể được chia nhỏ như thế này:

:g/pattern/a,bj

Ở đâu :

pattern = ;
a       = .           = number of current line
b       = /^[^;]*$/-1 = number of next line without any semicolon minus one

b có thể được chia nhỏ hơn nữa như thế này:

/    = look for the number of the next line matching the following pattern
^    = a beginning of line
[^;] = then any character except a semicolon
 *   = the last character can be repeated 0 or more times
 $   = an end of line
 /   = end of pattern
 -1  = removes one to the number you just got

jlà dạng viết tắt của lệnh Ex :join, giống như hầu hết các lệnh Ex khác có thể được bắt đầu bằng một phạm vi.
Ở đây, nó đi trước phạm vi: .,/^[^;]*$/-1( a,b)
Một phạm vi theo mẫu a,btrong đó abnói chung là 2 số dòng và cho phép bạn hoạt động trên một nhóm các dòng có số nằm giữa ab, thay vì chỉ một.

Vì vậy, jlệnh kết hợp tất cả các dòng giữa dòng hiện tại ( a) và dòng tiếp theo không chứa bất kỳ dấu chấm phẩy trừ một ( b).

Để biết thêm thông tin, xem:

:help :global
:help :join
:help :range

1

Tôi cũng tham gia tương tự mọi lúc với tìm kiếm toàn cầu và thay thế:

s /; \ n /; /

\n phù hợp với dòng mới.

Để tìm và xóa các dòng trống:

s / ^ $ \ n //

Tôi không chắc tại sao, nhưng nếu muốn chèn một dòng mới, bạn phải sử dụng \r


smột mình sẽ chỉ hoạt động cho một dòng, để làm cho nó toàn cầu, bạn cần sử dụng %s, nhưng sau đó nó sẽ tham gia hầu hết tất cả các dòng, bao gồm cả các ;dòng
kenorb

2
@kenorb Ehm không, tôi nghĩ bạn có thể sử dụng :slệnh chính xác cho những gì bạn muốn. Tôi nghĩ rằng điều này %s/;\n\(.*;\)\@=/;/làm những gì bạn cần.
Christian Brabandt
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.