Ardışık tüm kopyaları sil


13

Buna benzeyen bir dosyam var.

Move to 230.00
Hold
Hold
Hold
Hold
Hold
Hold
Move to 00.00
Hold 
Hold 
Hold 
Hold 
Hold 
FooBar
Hold 
Spam
Hold

Bunun gibi görünmesini istiyorum:

Move to 230.00
Hold
Move to 00.00
Hold 
FooBar
Hold
Spam
Hold

Eminim vim'in bunu hızlı bir şekilde yapmasının bir yolu olmalı, ama kafamı nasıl yapacağımı tam olarak sayamıyorum. Bu makroların gücünün ötesinde mi ve vimscript'e mi ihtiyaç duyuyor?

Ayrıca, her bir "Muhafaza" bloğuna aynı makroyu uygulamak zorunda kalırsam sorun olmaz. Her ne kadar harika olsa da, tüm dosyayı alan tek bir makro olması gerekmez.

Yanıtlar:


13

Aşağıdaki komutun çalışması gerektiğini düşünüyorum:

 :%s/^\(.*\)\(\n\1\)\+$/\1/

Açıklama:

Biz değişime bütün dosya üzerinde ikame komutunu kullanın patterniçine string:

:%s/pattern/string/

İşte patternolan ^\(.*\)\(\n\1\)\+$ve stringolduğunu \1.

pattern şu şekilde parçalanabilir:

^\(subpattern1\)\(subpattern2\)\+$

^ve $sırasıyla bir satır başı ve bir satır sonu eşleşir.

\(ve daha sonra özel numarayla başvurabilmemiz \)için kuşatmak için kullanılır . Ayrıca , niceleyici ile 1 veya daha fazla kez tekrarlayabilmemiz için kuşatmak için kullanılırlar .subpattern1\1
subpattern2\+

subpattern1olan .*
.yeni satır hariç herhangi bir karakter eşleşen meta karakterdir ve *son karaktere 0, 1 veya daha fazla kez eşleşen bir miktar belirleyici olduğunu.
Dolayısıyla, .*yeni satır içermeyen herhangi bir metni eşleştirir.

subpattern2olan \n\1
\nyeni bir satır eşleşir ve \1ilk iç eşleşti aynı metinle eşleşen \(, \)burada hangi subpattern1.

Bu patternşekilde şu şekilde okunabilir:
satırın başlangıcı ( ^) ve ardından yeni satır içermeyen herhangi bir metin ( .*) ve ardından yeni satır ( \n), sonra aynı metin ( \1), sonuncusu bir veya daha fazla kez tekrarlanır ( \+) ve nihayet satır sonu ( $) .

Her yerde patterneşleşen (aynı çizgiler bir blok) olup, ikame komutu ile değiştirir stringburada hangi \1(bloğun ilk çizgi).

Dosyanızda hiçbir şeyi değiştirmeden hangi satır bloklarının etkileneceğini görmek istiyorsanız, hlsearchseçeneği etkinleştirebilir nve komutun sonuna ikame bayrağını ekleyebilirsiniz :

:%s/^\(.*\)\(\n\1\)\+$/\1/n

Daha ayrıntılı denetim için, cbunun yerine ikame bayrağını ekleyerek her bir satır bloğunu değiştirmeden önce bir onay isteyebilirsiniz :

:%s/^\(.*\)\(\n\1\)\+$/\1/c

İkame komut okuma hakkında daha fazla bilgi için :help :s,
ikame Bayraklar için :help s_flags,
çeşitli meta ve nicelik okumak için :help pattern-atoms,
ve için vim normal ifadeler okumak bu .

Düzenleme: Joker$ , sonuna bir ekleyerek komuttaki bir sorunu düzeltti pattern.

Ayrıca BloodGain aynı komutun daha kısa ve daha okunabilir bir versiyonuna sahiptir.


1
Güzel; $ama emrinizde bir a lazım. Aksi takdirde , bir önceki satıra özdeş metinle başlayan , ancak diğer sondaki karakterleri olan bir satırla beklenmedik şeyler yapar . Ayrıca, verdiğiniz temel komutun cevabımla işlevsel olarak eşdeğer olduğunu :%!uniq, ancak vurgulama ve onay bayraklarının güzel olduğunu unutmayın.
Wildcard

Haklısın, kontrol ettim ve yinelenen satırlardan biri farklı bir sondaki karakter içeriyorsa, komut beklendiği gibi davranmaz. Bunu nasıl düzeltebileceğimi bilmiyorum, atom \nbir satır sonu ile eşleşiyor ve bunu önlemeli ama yapmıyor. Ben $hemen sonra .*başarı ile eklemeyi denedim . Bunu düzeltmeye çalışacağım, ancak yapamıyorsam, belki cevabımı silerim veya sonunda bir uyarı eklerim. Bu sorunu işaret ettiğiniz için teşekkür ederiz.
saginaw

1
Deneyin:%s/^\(.*\)\(\n\1\)\+$/\1/
Wildcard

1
Satır sonu ile değil $, dizenin sonuna uygun olduğunu düşünmelisiniz . Bu teknik olarak doğru değildir - ancak birkaç istisna dışında karakterleri arkasına koyduğunuzda, $özel bir şey yerine bir değişmezle eşleşir . Bu nedenle \n, çok satırlı maçlar için kullanmak daha iyidir. (Bkz. :help /$)
Wildcard

\nRegex içinde herhangi bir yerde kullanılabilecek doğru olduğunu düşünüyorum , oysa $muhtemelen sadece sonunda kullanılmalıdır. Sadece ikisi arasında bir fark yaratmak için, cevabı \nbir satırsonu ile eşleştiğini yazarak (bu da içgüdüsel olarak hala bir metin olduğunu düşündürür) yazarken, satır $sonu ile eşleşir (bu da hiçbir şey olmadığını düşünmenizi sağlar) ayrıldı).
saginaw

10

Takip etmeyi dene:

:%s;\v^(.*)(\n\1)+$;\1;

Olduğu gibi Saginaw cevabı yerine komutunu: Bu Vim'ın kullanır. Bununla birlikte, okunabilirliği artırmak için birkaç ek özellikten yararlanır:

  1. Vim bize ters eğik çizgi dışında herhangi bir alfanümerik olmayan ASCII karakter kullanın (sağlar \ ), çift tırnak ( " () veya boru | (. İşte maç / değiştirme / bayraklar metni bölmek için, ben seçilmiş noktalı virgül) ; ama sen yapabilirsin) Başka birini seç.
  2. Vim, normal ifadeler için "sihirli" ayarlar sağlar, böylece karakterler ters eğik çizgi kaçışı yerine özel anlamları için yorumlanır. Bu, ayrıntı düzeyini azaltmak için yararlıdır ve "anmajik" varsayılandan daha tutarlıdır. İle başlayan \v"çok sihirli" vasıtasıyla veya dışında tüm karakterler alfanümerik ( A-z0-9 ) ve alt çizgi ( _ ) özel bir anlamı vardır.

Bileşenlerin anlamı:

tüm dosya için %

s yerine

; yedek dizeye başla

\ v "çok büyü"

^ satır başı

(. *) Herhangi bir karakterin 0 veya daha fazlası (grup 1)

(\ n \ 1) + yeni satırın ardından (grup 1 eşleşme metni), 1 veya daha fazla kez (grup 2)

$ satır sonu (veya bu durumda, sonraki karakterin yeni satır olması gerektiğini düşünün )

; dizeyi değiştirmeye başla

\ 1 grup 1 eşleşme metni

; komutun sonu veya bayrakların başlatılması


1
Cevabınızı gerçekten çok seviyorum, çünkü daha okunabilir ama aynı zamanda \nve arasındaki farkı daha iyi anlamamı sağladı $. \ndesene bir şey ekler: vim'e aşağıdaki metnin yeni bir satırda olduğunu söyleyen yeni karakter. Örüntüye $herhangi bir şey eklemese de, desenin dışındaki bir sonraki karakter yeni bir çizgi değilse eşleşmenin yasaklanmasını sağlar. En azından cevabını okuyarak anladım ve :help zero-width.
saginaw

Aynı şey doğrudur ^, desene hiçbir şey eklemez, sadece desenin dışındaki bir önceki karakter yeni bir çizgi değilse bir eşleşmenin yapılmasını önler ...
saginaw

@saginaw Tam olarak haklısınız ve bu iyi bir açıklama. Normal ifadelerde, bazı karakterler kontrol karakterleri olarak düşünülebilir . Örneğin, +"önceki ifadeyi (karakter veya grup) 1 veya daha fazla kez tekrarlayın" anlamına gelir, ancak hiçbir şeyle eşleşmez. ^Araçlar "dizesi ortasında başlayamaz" ve $araçlar "dizesi ortasında bitemez." Dikkat "line" demedim ama "string" var. Vim, her satıra varsayılan olarak bir dize olarak davranır ve işte burada devreye \ngirer. Vim'e bu eşleşmeyi yapmaya çalışmak için yeni bir satır kullanmasını söyler.
Bloodgain

8

Sadece TÜM bitişik özdeş çizgileri kaldırmak Holdistiyorsanız, bunu harici bir filtreyle içinden kolayca yapabilirsiniz vim:

:%!uniq (bir Unix ortamında).

Doğrudan yapmak istiyorsanız vim, aslında çok zor. Bir yol olduğunu düşünüyorum, ancak genel durum için% 100 işlevsel hale getirmek çok zor ve henüz tüm hataları çözmedim.

Ancak, bu özel durum için, yinelenmeyen bir sonraki satırın aynı karakterle başlamadığını görsel olarak görebildiğiniz için şunları kullanabilirsiniz:

:+,./^[^H]/-d

+Mevcut hat sonra çizgi anlamına gelmektedir. . mevcut satırı belirtir. /^[^H]/-(Daha önce çizgi anlamına -H. ile başlamaz sonraki satıra)

Sonra d silinir.


3
Yedek ve global Vim komutları iyi alıştırmalar olsa da uniq(vim içinden veya kabuğu kullanarak) çağırmak bunu nasıl çözeceğimdir. Birincisi uniq, boş / tüm boşlukları eşdeğer olarak (test etmedi) satırları işleyeceğinden eminim , ancak bir regex ile yakalamak çok daha zor olurdu. Ayrıca iş yapmaya çalışırken "tekerleği yeniden icat etmek" anlamına da geliyor.
Bloodgain

2
Harici araçlarla metin besleyebilme özelliği, genellikle Windows'ta Vim ve Cygwin'i önermemdir. Vim ve kabuk basitçe birbirlerine aittir .
DevSolar

2

Vim tabanlı bir cevap:

:%s/\(^.*\n\)\1\{1,}/\1

= Her satırı ve ardından en az bir kez aynı satırla değiştirin.


2

Bir tane daha, Vim 7.4.218 veya üstü varsayıldığında:

function! s:Uniq(line1, line2)
    let cursor = getcurpos()
    let lines = uniq(getline(a:line1, a:line2))
    if setline(a:line1, lines) == 0 && len(lines) <= a:line2 - a:line1
        silent execute (a:line1 + len(lines)) . ',' . a:line2 . 'd _'
    endif
    call setpos('.', cursor)
endfunction

command! -range=% Uniq call <SID>Uniq(<line1>, <line2>)

Yine de bu, diğer çözümlerden daha iyi değildir.


2

İşte Preben Gulberg ve Piet Delport'un eski bir (2003) vimine (golf) dayanan bir çözüm .

  • Kökleri yatıyor %g/^\v(.*)\n\1$/d
  • Diğer çözümlerden farklı olarak , bir işlev içine kapsüllenmiştir , bu nedenle arama kaydını veya adsız kaydı değiştirmez.
  • Ayrıca, kullanımını basitleştirmek için bir komuta dahil edilmiştir:
    • :Uniq(eşdeğer :%Uniq),
    • :1,Uniq (arabelleğin başlangıcından geçerli satıra kadar),
    • çizgileri görsel olarak seç + vur :Uniq<cr>(vim tarafından genişletildi :'<,'>Uniq)
    • vb ( :h range)

İşte kod:

command! -range=% -nargs=0 Uniq <line1>,<line2>call s:EmuleUniq()

function! s:EmuleUniq() range
  let l1 = a:firstline
  let l2 = a:lastline
  if l1 < l2
    " Note the "-" to avoid spilling over the end of the range
    " Note also the use of ":delete", along with the black hole register "_"
    silent exe l1.','l2.'-g/^\(.*\)\n\1$/d _'

    call histdel('search', -1)          " necessary
    " let @/ = histget('search', -1)    " useless within a function
  endif
endfunction

Not: ilk denemeleri:

" Version1 from: Preben 'Peppe' Guldberg <peppe {at} xs4all {dot} nl>
" silent exe l1 . ',' . (l2 - 1) . 's/^\(.*\)\%(\n\%<' . (l2 + 1)
      " \ . 'l\1$\)\+/\1/e'

" Version from: Piet Delport <pjd {at} 303.za {dot} net>
" silent exe l1.','l2.'g/^\%<'.l2.'l\(.*\)\n\1$/d'
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.