Eşleşen tüm desenleri nasıl birleştirebilirim?


12

Çizgileri sadece belirli desen (örneğin ;) olan çizgiler için birleştirmek istiyorum , ancak kullanırken g/;/jbirkaç kez çağrılmadığı sürece beklendiği gibi çalışmaz.

Örneğin, aşağıdaki içerik:

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

kullanırken: :g/;/jçıktı:

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

veya :g/;/-jverir:

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

ile benzer: :g/;\_.\{-};/j.

Beklediğim çıktı:

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

veya benzer bir şey, bu nedenle deseni içeren tüm çizgiler birleştirilir.

Bu nasıl başarılabilir?


3
FWIW, :g/;/jiki geçişte yapıldığından çalışmaz: önce arabellek taranır, daha sonra komut eşleşen satırlara uygulanır.
romainl

Yanıtlar:


12

Sorunun olası açıklaması

Bence neden :g/;/jçalışmıyor çünkü :gkomut 2-pass algoritması ile çalışıyor:

  • ilk geçişte deseni içeren çizgileri işaretler ;
  • ikinci geçişte işaretli çizgiler üzerinde çalışır

İkinci geçiş sırasında :gçizgiye çizgi 1;ile katılır, 2;çünkü 1;ilk geçişte işaretlenmiştir. Ancak (değil emin) o katılmazsa şüpheli 1; 2;ile 3;hat çünkü 2;artık var olmayan, içeriği hattı ile birleşti olmuştur 1;işleme alındı.

Bu nedenle :g, ilk geçiş ( 3;) sırasında işaretlenen ve bir sonraki ( ) ile birleştirilen bir sonraki satırı arar 4;. Sorun tekrarlar, bu katılamaz Bundan sonra 3; 4;birlikte 5;hat çünkü 4;artık yok.

Çözüm 1 (vimscript ile)

Belki ;de önceki satırın noktalı virgül içerip içermediğini kontrol etmek için bir satır bulunduğunda bir işlevi çağırabilirsiniz :

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

Ardından aşağıdaki global komutu kullanın:

:g/;/call JoinLines()

Veya bir işlev olmadan:

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

Çözüm 2 (vimscript olmadan)

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

Global komut :gkalıbı bulduğunda komutu ;yürütür: .,/^[^;]*$/-1j

Bu şekilde parçalanabilir:

:g/pattern/a,bj

Nerede :

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

b bunun gibi daha fazla parçalanabilir:

/    = 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

jEx komutunun :joinçoğunda olduğu gibi Ex komutlarının çoğunun önünde bir aralık bulunabilecek kısaltılmış biçimidir .
Burada, aralığın önünde şunlar bulunur : .,/^[^;]*$/-1( a,b)
Bir aralık , genellikle 2 satır numarasının a,bbulunduğu formdan sonra gelir ave bnumarası yalnızca bir yerine ave arasında olan bir satır grubunda çalışmanıza olanak tanır b.

Böylece jkomut, geçerli olan ( a) ile sonraki noktalı virgül eksi bir ( b) içermeyen tüm satırları birleştirir .

Daha fazla bilgi için, bkz:

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

2

Ben her zaman küresel bir arama ve değiştirme ile katılmadan yapmak:

s /; \ N /; /

\n satırsonu ile eşleşir.

Boş satırları bulmak ve silmek için:

s / ^ $ \ n //

Neden olduğundan emin değilim, ancak yeni bir satır eklemek istiyorsanız kullanmanız gerekir \r


syalnız küresel hale getirmek için, sadece tek bir hat için çalışacak, kullanılan gereken %solmayan dahil, ama sonra hemen hemen tüm satırları katılmak olacak ;hatları
kenorb

2
@kenorb Ehm hayır, :skomutu istediğiniz gibi kullanabilirsiniz . Bence bu %s/;\n\(.*;\)\@=/;/ihtiyacınız olanı yapıyor.
Christian Brabandt
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.