2010-10-05 7 views
51

Je travaille souvent avec des fichiers texte qui ont une quantité variable d'espaces comme séparateurs de mots (les processeurs de texte comme Word font ceci, pour distribuer équitablement la quantité d'espace due aux différentes tailles de lettres certaines polices et ils mettent cette quantité variable d'espaces gênants même en sauvegardant en texte clair).vim regex remplacer plusieurs espaces consécutifs avec un seul espace

Je voudrais automatiser le processus de remplacement de ces séquences d'espaces de longueur variable avec des espaces simples. Je suppose qu'une regex pourrait le faire, mais il y a aussi des espaces au début des paragraphes (généralement quatre d'entre eux, mais pas toujours), que je voudrais laisser inchangés, donc mon regex ne devrait pas toucher les espaces blancs principaux et ajoute à la complexité. J'utilise vim, donc une regex dans le dialecte vim regex serait très utile pour moi, si c'est faisable.

Mon progrès actuel ressemble à ceci:

:%s/ \+/ /g 

mais il 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 char par char et ignorer les espaces après le premier, mais j'ai le sentiment que cela serait exagéré.

+0

Bon pour reformater le code aligné verticalement :) – JackHasaKeyboard

Répondre

31

Dans l'intérêt du pragmatisme, je tends à faire juste comme un processus en trois étapes:

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

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

Il 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. 

À côté: Si vous vous demandez pourquoi j'utilise :g au lieu de :s, c'est juste l'habitude surtout. :g peut tout faire :s et beaucoup plus. 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 de puissance vi, vous devriez regarder dans :g à un certain point.

+2

Oui, le puriste/idéaliste en moi a commencé à s'asseoir à l'arrière il y a longtemps. Maintenant, je veux juste faire le travail, surtout si l'alternative est une regex de 600 caractères avec back-tracking et look-ahead, que je ne comprendrai pas quand je dois revenir et déboguer dans trois mois :-) – paxdiablo

+0

+1 pluvier xyzzy – SingleNegationElimination

+0

I a utilisé une variante de ce qui précède: : g/\ +/s ///g Je comprends l'espace et \ + pour correspondre à un ou plusieurs, aucune idée de ce que le/s/ne , Quelqu'un sait? – anteatersa

80

cela remplacera 2 ou plusieurs espaces

s/ \{2,}/ /g 

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

s/ \+/ /g 
+7

Je pense que c'est probablement la meilleure et la plus simple réponse. Il a également l'avantage de travailler dans d'autres dialectes RegEx aussi! – TrinitronX

+0

C'est certainement la meilleure et la plus simple réponse. – RubyFanatic

2

Est-ce que cela fonctionne?

%s/\([^ ]\) */\1 /g 
+0

mieux utiliser'% s/[^] \ zs \ +// g' dans ce cas (': help/\ zs' – Benoit

+0

Ah! Agréable. Je suis d'accord beaucoup mieux. Je vous remercie. – frogstarr78

55

Cela fera l'affaire:

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

De nombreuses substitutions peuvent être faites dans vim plus facile qu'avec d'autres dialectes regex en utilisant les \zs et \ze méta-séquences. Ce qu'ils font 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 "finir ici"). Dans ce cas, le motif doit correspondre d'abord à un caractère non-espace ([^ ]) mais le \zs suivant indique que le résultat de la correspondance finale (qui sera remplacé) commence après ce caractère.

Comme il n'y a aucun moyen d'avoir un caractère non-espace devant les espaces de ligne, il ne sera pas reconnu par le modèle, donc la substitution ne le remplacera pas. Simple.

+1

Je voudrais proposer cette alternative: '% s! \ S \ @<= \ +! ! g'. Le '\ @<=' est un si beau canard que j'aime bien utiliser. Voir aussi ': help/\ @<=' – Benoit

+1

Je préfère simplement la réduction de l'acrobatie digitale de 'zs' en tapant' @<= '... de la même façon (si moins) que Vim vaut mieux que E (scape) M (eta) A (lt) C (ontrol) S (hift). :) OTOH, son sens du flair vaut toujours un peu de sacrifice, alors n'hésitez pas. –

+0

dépend de la disposition du clavier que vous utilisez bien sûr ... – Benoit

2

J'aime cette version - elle est similaire à la version anticipée d'Aristotle Pagaltzis, mais je la trouve plus facile à comprendre. (Probablement juste mon manque de familiarité avec \ ZS)

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

ou pour tous les espaces

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

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

+0

Bien sûr, cette version est d'un ordre de grandeur plus difficile à taper, et à formuler à la volée - et c'est pour un motif presque aussi trivial que possible. Vous serez bien servi pour vous familiariser avec '\ zs' et' \ ze', ils peuvent faire des merveilles pour l'écriture et la lisibilité de motifs plus complexes (en particulier lorsque vous avez raison d'utiliser les deux à la fois!). –

+0

Je vais sûrement regarder '\ zs' et' \ ze', mais j'utilise aussi souvent mes regexs en python et sed. Il peut donc être intéressant d'avoir une solution qui fonctionnera dans plusieurs applications. –

6

Il y a beaucoup de bonnes réponses ici (en particulier Aristote: \zs et \ze valent la peine d'apprendre). Juste pour être complet, vous pouvez aussi le faire avec un regard-behind affirmation négative:

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

Ce Says « trouver 2 ou plusieurs espaces (' \{2,}') qui ne sont pas précédés par « le début de la ligne suivie par zéro ou plus d'espaces ». Si vous préférez réduire le nombre de barres obliques inversées, vous pouvez aussi le faire:

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

mais il ne vous sauve deux personnages! Vous pouvez également utiliser ' +' au lieu de ' {2,}' si cela ne vous dérange pas de faire un chargement de modifications redondantes (c'est-à-dire de changer un seul espace en un seul espace).

Vous pouvez également utiliser le look-behind négatif pour vérifier juste pour un seul caractère non-espace:

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

qui est la même que (une version légèrement modifiée d'Aristote pour traiter les espaces et les onglets même pour sauver un peu de frappe):

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

Voir:

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

et (lire tout!):

:help pattern.txt 
1

Répondu; mais même si je jetais mon flux de travail de toute façon.

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

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

+1

ce n'est pas une bonne solution: d'abord, il supprimera les espaces de début, ce que l'auteur de la question souhaite éviter. Deuxièmement, vous pouvez faire 100 @: pour exécuter 100 fois le contenu du registre: (qui est la dernière commande ex) – Benoit

+1

donc j'ai dit que ce n'est pas la meilleure réponse dans ma réponse :) – wom

+2

Je trouve toujours cette réponse utile, même si c'est le cas ne pas répondre correctement aux questions de l'OP. –

Questions connexes