web-dev-qa-db-fra.com

vim regex remplacer plusieurs espaces consécutifs par un seul espace

Je travaille souvent avec des fichiers texte qui ont une quantité variable d'espaces blancs comme séparateurs Word (les processeurs de texte comme Word le font, pour distribuer équitablement la quantité d'espaces blancs en raison de différentes tailles de lettres dans certaines polices et ils mettent cette quantité variable ennuyeuse d'espaces même lorsque enregistrement en texte brut).

Je voudrais automatiser le processus de remplacement de ces séquences d'espaces blancs de longueur variable par des espaces simples. Je soupçonne qu'un regex pourrait le faire, mais il y a aussi des espaces blancs au début des paragraphes (généralement quatre d'entre eux, mais pas toujours), que je voudrais laisser inchangés, donc fondamentalement mon regex ne devrait pas non plus toucher les espaces blancs principaux et ce ajoute à la complexité.

J'utilise vim, donc un regex dans le dialecte vim regex me serait très utile, si c'est faisable.

Ma progression actuelle ressemble à ceci:

:%s/ \+/ /g

mais cela ne fonctionne pas correctement.

J'envisage également d'écrire un script vim qui pourrait analyser les lignes de texte une par une, traiter chaque ligne caractère par caractère et ignorer les espaces blancs après le premier, mais j'ai l'impression que ce serait exagéré.

64
jedi_coder

Dans l'intérêt du pragmatisme, j'ai tendance à le faire en trois étapes:

:g/^    /s//XYZZYPARA/g
:g/ \+/s// /g
:g/^XYZZYPARA/s//    /g

Je ne doute pas qu'il puisse y avoir une meilleure façon (peut-être en utilisant des macros ou même une manière pure regex) mais je trouve généralement que cela fonctionne lorsque je suis pressé. Bien sûr, si vous avez des lignes commençant par XYZZYPARA, vous voudrez peut-être ajuster la chaîne :-)

C'est assez bon pour tourner:

    This is a new paragraph
spanning       two lines.
    And    so    is   this but on one line.

dans:

    This is a new paragraph
spanning two lines. 
    And so is this but on one line.

À part: Si vous vous demandez pourquoi j'utilise :g au lieu de :s, c'est juste une habitude surtout. :g peut tout faire :s peut et bien plus encore. C'est en fait un moyen d'exécuter une commande arbitraire sur les lignes sélectionnées. La commande à exécuter se trouve être s dans ce cas, donc il n'y a pas de réelle différence, mais si vous voulez devenir un utilisateur expérimenté de vi, vous devriez regarder dans :g à un moment donné.

39
paxdiablo

cela remplacera 2 espaces ou plus

s/ \{2,}/ /g

ou vous pouvez ajouter un espace supplémentaire avant le \+ à votre version

s/  \+/ /g
109
mikerobi

Ça fera l'affaire:

%s![^ ]\zs  \+! !g

De nombreuses substitutions peuvent être effectuées dans Vim plus facilement qu'avec d'autres dialectes d'expression régulière en utilisant le \zs et \ze méta-séquences. Ce qu'ils font, c'est d'exclure une partie du match du résultat final, soit la partie avant la séquence (\zs, "S" pour "commencer ici") ou la partie après (\ze, "E" pour "terminer ici"). Dans ce cas, le motif doit d'abord correspondre à un caractère non espace ([^ ]) mais ce qui suit \zs indique que le résultat final de la correspondance (qui sera remplacé) commence après ce caractère.

Puisqu'il n'y a aucun moyen d'avoir un caractère non-espace devant les espaces en début de ligne, il ne sera pas mis en correspondance par le motif, donc la substitution ne le remplacera pas. Simple.

59

Il y a beaucoup de bonnes réponses ici (en particulier d'Aristote: \zs et \ze valent la peine d'être appris). Juste pour être complet, vous pouvez également le faire avec une assertion négative:

:%s/\(^ *\)\@<! \{2,}/ /g

Cela dit "trouver 2 espaces ou plus (' \{2,}') qui ne sont PAS précédés du 'début de la ligne suivi de zéro ou plusieurs espaces' ". Si vous préférez réduire le nombre de barres obliques inverses, vous pouvez également procéder comme suit:

:%s/\v(^ *)@<! {2,}/ /g

mais cela ne vous fait économiser que deux personnages! Vous pouvez également utiliser ' +' au lieu de ' {2,}' si cela ne vous dérange pas de faire une charge de changements redondants (c'est-à-dire de changer un seul espace en un seul espace).

Vous pouvez également utiliser le regard négatif pour vérifier un seul caractère non espace:

:%s/\S\@<!\s\+/ /g

qui est à peu près la même chose (une version légèrement modifiée d'Aristote pour traiter les espaces et les tabulations comme les mêmes afin d'économiser un peu de frappe):

:%s/\S\zs \+/ /g

Voir:

:help \zs
:help \ze
:help \@<!
:help zero-width
:help \v

et (lisez tout!):

:help pattern.txt
7
DrAl

J'aime cette version - elle est similaire à la version prospective d'Aristote Pagaltzis, mais je la trouve plus facile à comprendre. (Probablement juste ma méconnaissance de\zs)

s/\([^ ]\) \+/\1 /g

ou pour tous les espaces

s/\(\S\)\s\+/\1 /g

Je l'ai lu comme "remplacer toutes les occurrences de quelque chose d'autre qu'un espace suivi de plusieurs espaces par quelque chose et un seul espace".

2
Michael Anderson

Répondu; mais bien que je jette mon flux de travail de toute façon.

%s/  / /g
@:@:@:@:@:@:@:@:@:@:@:@:(repeat till clean)

Rapide et simple à retenir. Il existe des solutions beaucoup plus élégantes ci-dessus; mais juste mon .02.

2
wom

Est-ce que ça marche?

%s/\([^ ]\)  */\1 /g
2
frogstarr78