Was ist der einfachste Weg, um abschließende Leerzeichen aus allen Zeilen einer Datei zu entfernen?

139

Es kommt häufig vor, dass beim Programmieren oder Öffnen von Textdateien am Ende einer Zeile ein Leerzeichen nachgestellt wird. vim hat eine Möglichkeit, dies zu zeigen, indem Sie die trailOption in der listcharsOption setzen und dann einschalten list.

Was ist jedoch der einfachste Weg, um dieses nachgestellte Leerzeichen global über die gesamte Datei hinweg zu beseitigen (idealerweise ohne Plug-in)?

Andrew Ferrier
quelle
Hier ist ein Dokumenteintrag zum Thema.
Filipp W.
Wenn Sie vim-faq installiert haben , können Sie dort eine Offline-Antwort erhalten: :h vim-faqund suchen /trailing. Das schwer zu merkende Tag ist :h faq-12.1.
Hotschke

Antworten:

72

Verwenden Sie eine Tastenkombination, um alle nachgestellten Leerzeichen zu entfernen

Da einige Seiten, die ich bearbeite, tatsächlich abschließende Leerzeichen (z. B. Abschriften) benötigen und andere nicht, habe ich eine Tastaturbindung eingerichtet, F5damit es trivial ist, ohne automatische Zeichen auszukommen . Fügen Sie dazu den folgenden Code (von vim.wikia) oder eine Variation davon zu Ihrem .vimrc:

"Remove all trailing whitespace by pressing F5
nnoremap <F5> :let _s=@/<Bar>:%s/\s\+$//e<Bar>:let @/=_s<Bar><CR>
  • nnoremap <F5>führt F5im normalen Modus eine nicht rekursive Zuordnung zum Schlüssel durch
  • :let _s=@/speichert den letzten Suchbegriff (aus dem Makro @/) in der Variablen_s
  • <Bar>Fungiert als Pipe-Symbol |zum Trennen von Befehlen, |würde jedoch einen Befehl in diesem Kontext beenden und <Bar>muss stattdessen verwendet werden.
  • :%s/\s\+$//esucht nach nachgestellten Leerzeichen und löscht sie überall im Puffer ( eine detaillierte Aufschlüsselung dieses Ausdrucks finden Sie in der Antwort von CarpetSmoker )
  • let @/=_sStellt Ihren letzten Suchbegriff für das Makro wieder her @/, sodass er beim nächsten Treffer verfügbar ist n.
  • <CR> Beendet die Zuordnung

... oder selektiver sein

Wenn Sie Fälle haben, in denen Sie nicht alle abschließenden Leerzeichen entfernen möchten, können Sie ein selektiveres Muster verwenden. Der folgende Code zeigt zum Beispiel, wie ich abschließende Leerzeichen nur entferne, wenn sie nach einem Semikolon stehen (hier ist es gebunden F8).

nnoremap <F8> :let _s=@/<Bar>:%s/;\s\+$/;/e<Bar>:let @/=_s<Bar><CR>

Dies ist nützlich, wenn Sie, wie ich, einige Dateien mit abschriftenähnlichen Heredocs haben, die zwischen Semikolon-terminierten Programmieranweisungen eingefügt sind.

Christopher Bottoms
quelle
6
Versuchen Sie :keeppatterns, ein Überschreiben zu verhindern @/. Und schauen Sie sich auch an :keepjumps.
Bohr
@Bohr Welche Version von vim verwendest du? Ich habe versucht :help keeppatternund nichts bekommen.
Christopher Bottoms
@ChristopherBottoms Mindestens Version 7.4.155 .
Bohr
@Bohr. Vielen Dank! Finde heraus, dass ich noch 7.4.0 verwendet habe. Ich habe die neueste Version installiert und sie ist verfügbar.
Christopher Bottoms
2
Sie können den gleichen Effekt erzielen, indem Sie diesen Befehl in eine Funktion einschließen, da dann der letzte Suchbegriff automatisch wiederhergestellt wird :-) Auf diese Weise müssen Sie auch nicht herumspielen :nohl, wenn Sie etwas hervorheben, wird es weiterhin hervorgehoben es (siehe meine aktualisierte Antwort).
Martin Tournoij
175

Der "einfachste" Weg ist, einfach zu benutzen :substitute:

:%s/\s\+$//e
  • :%s:substituteüber den Bereich laufen %, der den gesamten Puffer darstellt.
  • \s t Entspricht allen Whitespace-Zeichen.
  • \+ um sie 1 oder mehrmals zu wiederholen.
  • $ am Ende der Linie zu verankern.
  • Das eFlag, dass kein Fehler ausgegeben wird, wenn keine Übereinstimmung vorliegt (dh die Datei enthält bereits keinen nachgestellten Whitespace).

Dies ist jedoch wahrscheinlich nicht der "beste" Weg, da zwei Nebenwirkungen auftreten:

  1. es bewegt den Cursor zum letzten Treffer;
  2. Es fügt den Befehl dem Verlauf und dem Suchverlauf hinzu.
  3. Der letzte Suchbegriff wird zurückgesetzt.

Sie können beide Elemente reparieren, indem Sie dies in eine Funktion umwandeln:

fun! TrimWhitespace()
    let l:save = winsaveview()
    keeppatterns %s/\s\+$//e
    call winrestview(l:save)
endfun

Und dann benutze es wie folgt:

:call TrimWhitespace()
  1. Dadurch winsaveview()wird die aktuelle "Ansicht" winrestview()gespeichert , die die Cursorposition, Falze, Sprünge usw. enthält. Am Ende wird dies aus der gespeicherten Variablen wiederhergestellt.
  2. Das :keeppatternsverhindert , dass das \s\+$Muster aus der Such Geschichte hinzugefügt werden.
  3. Der zuletzt verwendete Suchbegriff wird nach dem Verlassen einer Funktion automatisch wiederhergestellt, sodass wir dafür nichts weiter tun müssen.

Da es etwas ärgerlich ist, immer etwas einzugeben :call, können Sie einen Befehl definieren:

command! TrimWhitespace call TrimWhitespace()

Welche können ohne die verwendet werden :call:

:TrimWitespace

Und Sie können es natürlich an einen Schlüssel binden:

:noremap <Leader>w :call TrimWhitespace()<CR>

Einige Leute machen das gerne automatisch, bevor sie eine Datei auf die Festplatte schreiben, so:

autocmd BufWritePre * :call TrimWhitespace()

Es gefällt mir nicht, da einige Formate abschließende Leerzeichen erfordern (z. B. Markdown), und in einigen Fällen möchten Sie sogar abschließende Leerzeichen in Ihrem Code (z. B. das Formatieren einer E-Mail und das Verwenden des --<Space>Markers zum Anzeigen des Beginns einer Signatur) ).


Schamloser Plug-Modus: Vor einiger Zeit habe ich ein kleines Python-Skript geschrieben , um Whitespace für ein gesamtes Projekt auf einmal zu bereinigen.

Martin Tournoij
quelle
1
Wenn Sie keine Funktion erstellen möchten, um zur vorherigen Position zu gelangen, können Sie ​`​nach Abschluss des Ersetzens zweimal drücken . Dies eröffnet die Möglichkeit, einen Oneliner wie diesen zu erstellen:%s/\s\+$//e | exe "normal ``"
Neaţu Ovidiu Gabriel
1
@NeaţuOvidiuGabriel, natürlich funktioniert der doppelte Backtick nicht mehr so, nachdem der Oneliner ausgeführt wurde. ;)
Wildcard
Ähnlich: stackoverflow.com/a/1618401 . Aber ich mag Martins Code mehr.
John cj
11

Um alle nachfolgenden Leerzeichen (am Ende jeder Zeile) zu löschen, können Sie den folgenden Befehl verwenden:

:%s/ \+$//

Verwenden Sie zum \sEinschließen von Registerkarten anstelle von Leerzeichen.


Über die Befehlszeile:

$ ex +'%s/\s\+$//e' -cwq file.c

Alle Dateien im aktuellen Verzeichnis (rekursiv verwenden **/*.*):

$ ex +'bufdo!%s/\s\+$//e' -cxa *.*

Python Weg:

:py import vim
:pydo vim.current.buffer[linenr - 1] = vim.current.buffer[linenr - 1].strip()

oder:

:py import vim
:py for i, l in enumerate(vim.current.buffer): vim.current.buffer[i] = l.rstrip()

Verwenden Sie lstrip()für den linken Streifen (nachlaufend), rstrip()für den rechten Streifen (vorlaufend) oder strip()zum Entfernen von beiden Enden.


Hier ist eine nützliche Funktion, die überflüssige Leerzeichen am Ende einer Zeile entfernt, die Sie hinzufügen können .vimrc:

" Removes superfluous white space from the end of a line
function! RemoveWhiteSpace()
   :%s/\s*$//g
    :'^
    "`.
endfunction

Dafür gibt es auch das Plugin DeleteTrailingWhitespace .


Leerzeichen hervorheben

Verwenden Sie Folgendes, um zu überprüfen, ob alle nachgestellten Leerzeichen weg sind:

  1. Tippe / $, um sie zu finden. Wenn es welche gibt, würde vim sie für Sie hervorheben.

  2. Verwenden Sie Farben, um sie hervorzuheben:

    :highlight ws ctermbg=red guibg=red
    :match ws /\s\+$/
    
  3. Verwenden Sie sichtbare Zeichen ( Quelle ):

    :set encoding=utf-8
    :set listchars=trail:·
    :set list
    

Siehe auch: Markieren Sie unerwünschte Leerzeichen

Um abschließende Leerzeichen standardmäßig hervorzuheben, können Sie Folgendes konfigurieren .vimrc:

highlight ws ctermbg=red guibg=red
match ws /\s\+$/
autocmd BufWinEnter * match ws / \+$/

Leerzeichen werden standardmäßig entfernt

Wenn Sie sicherstellen möchten, dass alle nachgestellten Leerzeichen in einer Datei beim Speichern automatisch entfernt werden, können Sie den folgenden Befehl hinzufügen .vimrc:

autocmd BufWritePre *.c,*.php :%s/ \+$//ge

Dies wird nicht empfohlen, da abschließende Leerzeichen aus jeder vom Benutzer gespeicherten Datei entfernt werden (auch wenn Leerzeichen gewünscht werden können).


Siehe auch:

Kenorb
quelle
5

Um Christopher Bottoms Antwort ein wenig zu kommentieren : Jonathan Palardy hat einen guten Artikel dazu geschrieben . Darin schreibt er eine Funktion, Preserve(command)die den Zustand des Editors (hauptsächlich Cursorposition und letztes Suchmuster) beim Ausführen eines beliebigen Befehls beibehält:

function! Preserve(command)
  " Preparation: save window state
  let l:saved_winview = winsaveview()
  " Run the command:
  execute a:command
  " Clean up: restore previous window position
  call winrestview(l:saved_winview)
endfunction

Dies hat den Vorteil, dass es sich um eine Mehrzweckanwendung handelt. Sie können es beispielsweise verwenden, um alle nachfolgenden Leerzeichen zu ersetzen (wie es Jonathan tut), indem Sie Folgendes zuordnen:

nnoremap <F5> :call Preserve("%s/\\s\\+$//e")<CR>

Sie können es auch für eine visuelle Moduszuordnung verwenden, um nur die nachgestellten Leerzeichen auf visuell ausgewählten Linien zu entfernen:

xnoremap <F5> :call Preserve("'<,'>s/\\s\\+$//e")<CR>

Sie können es auch für andere Anrufe verwenden, z. B. zum Formatieren des gesamten Dokuments unter =Beibehaltung Ihrer Position (verwenden Sie diesmal einen anderen Schlüssel, um Konflikte zu vermeiden):

nnoremap <F6> :call Preserve("normal gg=G")<CR>

Alles in allem habe ich festgestellt, dass die Preserve(command)Funktion ein gutes Werkzeug ist.

Alex
quelle
2
Der zuletzt verwendete Suchbegriff sollte automatisch beibehalten werden, wenn Sie eine Funktion verlassen. so ein herumspielen mit @/sollte nicht nötig sein (in diesem fall jedenfalls).
Martin Tournoij
3
winsaveview()und winrestview()sind weit überlegen.
Dash-Tom-Bang
Ganz recht! Aktualisiert basierend auf Ihrem Feedback.
Alex
0

Eine andere Version der StripTrailingSpaces-Funktion:

if !exists('*StripTrailingWhitespace')
    function! StripTrailingWhitespace() range
        if !&binary && &filetype != 'diff'
            call Preserve(":" . a:firstline . "," . a:lastline . "s/\\s\\+$//e")
        endif
    endfunction
endif

WIRKLICH GIBT ES EINEN FEHLER IN DIESER FUNKTION (DIESER): Der Besitz wird aufgrund der Option "Reichweite" nicht aufbewahrt. es ist entfernt, es funktioniert sehr gut, aber ich teile den Code, um etwas Hilfe zu erhalten.

Wie Sie sehen können, wird auch die oben gezeigte Preserve- Funktion verwendet, allerdings auf etwas andere Weise.

Der Unterschied hierbei ist, dass ich einen Zeilenbereich oder einen Absatz mit auswählen kann vipund der Bereich :'<,'>dann automatisch an der Eingabeaufforderung angezeigt wird.

Die Idee kam aus dem Post von Bez Hermoso .

SergioAraujo
quelle