모든 연속 복제본 삭제


13

다음과 같은 파일이 있습니다.

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

다음과 같이 표시하고 싶습니다.

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

나는 vim이 빨리 이것을 할 수있는 방법이 있어야한다고 확신하지만, 어떻게 내 머리를 감쌀 수는 없습니다. 이것이 매크로의 힘을 넘어서서 vimscript가 필요합니까?

또한 "홀드"의 각 블록에 동일한 매크로를 적용해야해도 괜찮습니다. 전체 파일을 가져 오는 단일 매크로 일 필요는 없지만 훌륭합니다.

답변:


13

다음 명령이 작동해야한다고 생각합니다.

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

설명 :

전체 파일에서 치환 명령을 사용하여 다음과 같이 변경 pattern합니다 string.

:%s/pattern/string/

여기에 pattern있습니다 ^\(.*\)\(\n\1\)\+$string입니다 \1.

pattern 다음과 같이 분류 할 수 있습니다.

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

^그리고 $각각 라인의 시작과 라인의 끝과 일치.

\(\)둘러싸는 데 사용되는 subpattern1우리가 특별한 번호로 나중에 참조 할 수 있도록 \1.
그것들은 또한 subpattern2우리가 수량 화기로 1 회 이상 반복 할 수 있도록 둘러싸는 데 사용됩니다 \+.

subpattern1.*
.개행을 제외한 모든 문자와 일치하는 메타 문자이며 *마지막 문자 0, 1 회 이상 일치하는 수량 자입니다.
따라서 .*줄 바꿈이없는 텍스트와 일치합니다.

subpattern2되고 \n\1
\n새로운 라인과 일치 \1먼저 내부 유사한 것과 같은 텍스트와 일치하는 \(, \)여기를한다 subpattern1.

따라서 다음 pattern과 같이 읽을 수 있습니다.
줄의 시작 ( ^), 새 줄이없는 텍스트 ( ), .*새 줄 ( \n), 같은 텍스트 ( \1), 뒤의 두 개가 한 번 이상 반복됩니다 ( \+) 마지막으로 줄의 끝 ( $) .

pattern일치하는 곳 (동일한 줄의 블록)이면 대체 명령 string이 여기있는 곳 \1(블록의 첫 번째 줄)으로 바꿉니다 .

파일에서 아무것도 변경하지 않고 어떤 행 블록이 영향을 받는지 확인하려면 hlsearch옵션을 활성화 n하고 명령 끝에 대체 플래그를 추가 할 수 있습니다 .

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

보다 세밀한 제어를 위해 c대체 플래그를 대신 추가하여 각 라인 블록을 변경하기 전에 확인을 요청할 수도 있습니다 .

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

대체 명령 읽기에 대한 자세한 내용은 :help :s,
대체 플래그 :help s_flags,
다양한 메타 문자 및 한정사가 읽기 :help pattern-atoms,
및 위해 정력에 정규 표현식 읽기 .

편집 : 와일드 카드$ 끝에 에를 추가하여 명령의 문제를 해결했습니다 pattern.

또한 BloodGain 은 더 짧고 읽기 쉬운 동일한 명령 버전을 가지고 있습니다.


1
좋은; $그래도 당신의 명령이 필요합니다 . 그렇지 않으면 이전 줄과 동일한 텍스트로 시작 하지만 다른 후행 문자가 있는 줄로 예기치 않은 작업을 수행 합니다. 또한 당신이 준 기본 명령은 기능적으로 내 대답과 동일 :%!uniq하지만 강조 표시 및 확인 플래그는 좋습니다.
와일드 카드

맞습니다. 방금 확인했으며 중복 행 중 하나에 다른 후행 문자가 포함되어 있으면 명령이 예상대로 작동하지 않습니다. 나는 그것을 고치는 방법을 모른다. 원자 \n는 줄의 끝과 일치하고 이것을 막아야하지만 그렇지는 않다. 나는 성공하지 않고 $바로 추가를 시도했습니다 .*. 나는 그것을 시도하고 고칠 것이지만, 그것을 할 수 없다면, 대답을 삭제하거나 끝에 경고를 추가 할 것입니다. 이 문제를 지적 해 주셔서 감사합니다.
saginaw

1
시도:%s/^\(.*\)\(\n\1\)\+$/\1/
와일드 카드

1
줄 끝이 아니라 문자열$ 끝과 일치 하는 것을 고려해야합니다 . 이것은 기술적으로 사실이 아니지만 몇 가지 예외 이외의 문자 다음에 문자를 넣으면 특수 문자 대신 리터럴과 일치합니다 . 따라서 여러 줄 일치에 사용하는 것이 좋습니다. (참조 )$\n:help /$
와일드 카드

\n정규식 내부 어디에서나 사용할 수 있지만 $마지막에만 사용해야 한다고 생각합니다 . 둘 사이의 차이를 만들기 위해 \n줄 바꿈과 일치하는 글을 작성하여 답을 편집했습니다 (본질적으로 나중에 텍스트가 있다고 생각하게 만듭니다). $줄 끝과 일치합니다 (아무것도 없다고 생각하게 만듭니다) 왼쪽).
saginaw

10

다음을 시도하십시오 :

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

saginaw의 답변 과 마찬가지로 Vim의 : substitute 명령을 사용합니다. 그러나 가독성을 향상시키기 위해 몇 가지 추가 기능을 활용합니다.

  1. Vim에서는 백 슬래시 ( \ ), 큰 따옴표 ( " ) 또는 파이프 ( | )를 제외한 영숫자가 아닌 ASCII 문자를 사용하여 일치 / 바꾸기 / 플래그 텍스트를 나눌 수 있습니다. 여기서는 세미콜론 ( ; )을 선택했습니다 . 다른 것을 고르시 오.
  2. Vim은 정규식에 "마법"설정을 제공하므로 문자가 백 슬래시 이스케이프를 요구하지 않고 특수한 의미로 해석됩니다. 이 기능은 자세한 정보를 줄이는 데 도움이되며 "nomagic"기본값보다 더 일관성이 있습니다. \v"매우 마술"로 시작 하거나 영숫자 ( A-z0-9 ) 및 밑줄 ( _ )을 제외한 모든 문자 는 특별한 의미를 갖습니다.

구성 요소의 의미는 다음과 같습니다.

전체 파일에 대한 %

님의 대체

; 대체 문자열을 시작

\ v "매우 마술"

^ 줄의 시작

. (*) 0 이상의 임의의 문자 (그룹 1)

(\ n \ 1) + 개행 다음에 (그룹 1 일치 텍스트), 1 회 이상 (그룹 2)

$ 줄 끝 (또는이 경우 다음 문자는 개행 문자 라고 생각하십시오 )

; 문자열 교체 시작

\ 1 그룹 1 일치 텍스트

; 명령의 끝 또는 시작 플래그


1
더 읽기 때문에 그것을 만들었 기 때문에 난 정말 당신의 대답처럼, 또한 나를 더의 차이 이해 \n$. \n패턴에 무언가를 추가합니다 : 문자 새 줄은 vim에게 다음 텍스트가 새 줄에 있음을 알려줍니다. $패턴에 아무 것도 추가하지 않는 반면 , 패턴 외부의 다음 문자가 새 줄이 아닌 경우 일치하는 것을 금지합니다. 적어도 귀하의 답변을 읽고 이해 한 것 :help zero-width입니다.
saginaw

그리고 같은가 참이어야 ^는 패턴에 아무것도 추가하지 않습니다, 패턴의 이전 문자의 외부는 새로운 라인이 아닌 경우, 그냥 만들 수 일치를 방지 ...
새 기노

@saginaw 당신은 정확히 맞습니다. 그리고 그것은 좋은 설명입니다. 정규 표현식에서 일부 문자는 제어 문자 로 간주 될 수 있습니다 . 예를 들어+ "이전 식 (문자 또는 그룹)을 1 회 이상 반복하지만"과는 일치하지 않습니다. ^수단 "문자열의 중간에 시작할 수 없습니다"와 $수단 "문자열의 중간에 끝낼 수 없습니다." 나는 "line"이라고 말하지 않고 거기에 "string"이라고 말했음을 주목하십시오. Vim은 기본적으로 각 줄을 문자열로 취급합니다 \n. 그리고 그것이 들어오는 곳 입니다. Vim은이 줄을 바꾸려고 개행을 소비하도록 지시합니다.
Bloodgain

8

뿐만 아니라 인접한 모든 동일한 줄을 제거 Hold하려면 다음과 같은 외부 필터를 사용하여 매우 쉽게 수행 할 수 있습니다 vim.

:%!uniq (유닉스 환경에서).

에서 직접하고 싶다면 vim실제로 매우 까다 롭습니다. 나는 방법이 있다고 생각하지만 일반적인 경우 100 % 기능을 만드는 것은 매우 까다 롭고 아직 모든 버그를 해결하지 못했습니다.

그러나이 특정 경우 중복되지 않은 다음 줄이 같은 문자로 시작하지 않는 것을 시각적으로 볼 수 있으므로 다음을 사용할 수 있습니다.

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

+전류 선 후 라인을 의미한다. . 현재 줄을 나타냅니다. 는 /^[^H]/-(전 라인을 의미 -H.로 시작하지 않는 다음 라인)

그런 다음 d가 삭제됩니다.


3
대체 및 전역 Vim 명령은 좋은 연습이지만 uniq(vim 내에서 또는 셸을 사용하여) 호출 하면이 문제를 해결할 수 있습니다. 우선, uniq공백 / 모든 공백 라인을 동등한 것으로 처리하지만 (테스트하지 않은) 라인을 처리하지만 정규 표현식으로 캡처하는 것이 훨씬 더 어려울 것입니다. 또한 작업을 수행하려고하는 동안 "바퀴를 재발 명"하지 않는다는 의미입니다.
Bloodgain

2
외부 도구를 통해 텍스트를 공급할 수 있기 때문에 Windows에서 Vim Cygwin을 추천 합니다. Vim과 shell은 단순히 함께 속해 있습니다.
DevSolar

2

Vim 기반 답변 :

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

= 같은 줄로 모든 줄 다음에 적어도 한 번은 그 자체를 바꿉니다 .


2

Vim 7.4.218 이상을 가정하면 하나 더 :

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>)

그러나 이것이 다른 솔루션보다 반드시 낫지는 않습니다.


2

다음은 Preben Gulberg와 Piet Delport 의 오래된 (2003) vim (golf) 기반 솔루션 입니다.

  • 뿌리는 %g/^\v(.*)\n\1$/d
  • 다른 솔루션과 달리 함수 로 캡슐화 되었으므로 검색 레지스터 나 명명되지 않은 레지스터를 수정하지 않습니다.
  • 또한 사용법을 단순화하기 위해 명령으로 캡슐화되었습니다.
    • :Uniq(와 동일 :%Uniq),
    • :1,Uniq (버퍼의 시작부터 현재 행까지)
    • 시각적으로 선 + 적중 선택 :Uniq<cr>(vim으로 확장 :'<,'>Uniq)
    • 등 ( :h range)

코드는 다음과 같습니다.

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

참고 : 첫 시도는 다음과 같습니다.

" 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'
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.