2010-10-05 16 views
51

A menudo trabajo con archivos de texto que tienen una cantidad variable de espacios en blanco como separadores de palabras (procesadores de texto como Word hacen esto, para distribuir bastante el espacio en blanco debido a diferentes tamaños de letras en ciertas fuentes y ponen esta molesta cantidad variable de espacios incluso cuando se guardan como texto sin formato).vim regex reemplazar múltiples espacios consecutivos con un solo espacio

Me gustaría automatizar el proceso de sustitución de estas secuencias de espacios en blanco que tienen una longitud variable con espacios individuales. Sospecho que una expresión regular podría hacerlo, pero también hay espacios en blanco al principio de los párrafos (generalmente cuatro, pero no siempre), que me gustaría dejar sin cambios, así que básicamente mi expresión regular tampoco debería tocar los espacios en blanco principales y esto se agrega a la complejidad.

Estoy usando vim, por lo que una expresión regular en el dialecto regex vim sería muy útil para mí, si esto es factible.

Mi progreso actual es el siguiente:

:%s/ \+/ /g 

pero no funciona correctamente.

También estoy considerando escribir un script vim que pueda analizar líneas de texto una por una, procesar cada línea char por char y saltear los espacios en blanco después de la primera, pero tengo la sensación de que esto sería excesivo.

+0

Bueno para reformatear el código alineado verticalmente :) – JackHasaKeyboard

Respuesta

31

En aras del pragmatismo, tiendo a hacerlo como un proceso de tres etapas:

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

No dudo que pueda haber un bette r way (tal vez usando macros o incluso una forma pura de regex) pero normalmente encuentro que esto funciona cuando tengo prisa. Por supuesto, si usted tiene líneas que comienzan con XYZZYPARA, es posible que desee ajustar la cadena :-)

Es lo suficientemente buena como para rechazarla:

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

en:

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

Aparte: Si se está preguntando por qué uso :g en lugar de :s, eso es solo hábito en su mayoría. :g puede hacer todo :s puede y mucho más. En realidad, es una forma de ejecutar un arbitrario comando en líneas seleccionadas. El comando para ejecutar pasa a ser s en este caso, por lo que no hay diferencia real pero, si desea convertirse en un usuario avanzado de vi, debe consultar :g en algún momento.

+2

Sí, el purista/idealista en mí comenzó a tomar un asiento trasero hace mucho tiempo. Ahora solo me gusta hacer el trabajo, especialmente si la alternativa es una expresión regular de 600 caracteres con seguimiento y seguimiento, que no entenderé cuando tenga que volver y depurarlo en tres meses :-) – paxdiablo

+0

1 XYZZY chorlo – SingleNegationElimination

+0

que utiliza una variante de la anterior: : g/\ +/s ///g entiendo el espacio y \ + para que coincida con uno o más, ni idea de lo que la/s/hace , ¿nadie sabe? – anteatersa

80

ésta sustituirá 2 o más espacios

s/ \{2,}/ /g 

o se puede añadir un espacio extra antes de la \+ a su versión

s/ \+/ /g 
+7

Creo que esta es probablemente la mejor y más simple respuesta. ¡También tiene el beneficio adicional de trabajar en otros dialectos RegEx también! – TrinitronX

+0

Esta es definitivamente la mejor y más simple respuesta. – RubyFanatic

2

¿Funciona?

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

mejor use'% s/[^] \ zs \ +// g' en este caso (': help/\ zs') – Benoit

+0

¡Ah! Bonito. Estoy mucho mejor. Gracias. – frogstarr78

55

esto va a hacer el truco:

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

Muchas sustituciones se pueden hacer en Vim más fácil que con otros dialectos de expresiones regulares mediante el uso de las \zs y \ze meta-secuencias. Lo que hacen es excluir parte del partido del resultado final, ya sea la parte anterior a la secuencia (\zs, "s" para "comenzar aquí") o la parte posterior (\ze, "e" para "finalizar aquí"). En este caso, el patrón debe coincidir primero con un carácter que no sea espacio ([^ ]), pero el siguiente \zs dice que el resultado del partido final (que será el que se reemplazará) inicia después de ese carácter.

Puesto que no hay manera de tener un carácter que no sea espacio frente a los espacios en blanco-línea que conduce, se no se corresponde con el patrón, por lo que la sustitución no lo reemplazará. Sencillo.

+1

Me gustaría proponer esta alternativa: '% s! \ S \ @<= \ +! ! g'. El '\ @<=' es un pato tan hermoso que me gusta usarlo. Ver también ': help/\ @<=' – Benoit

+1

Prefiero la acrobacia de dedo reducida de 'zs' sobre tipear' @<= '... de la misma manera (aunque en menor medida) que disfruto de Vim mejor que E (scape) M (eta) A (lt) C (ontrol) S (hift). :) OTOH, el sentido del instinto siempre merece un sacrificio, así que siéntete libre. –

+0

depende de qué diseño de teclado está utilizando, por supuesto ... – Benoit

2

Me gusta esta versión, es similar a la versión anticipada de Aristóteles Pagaltzis, pero me resulta más fácil de entender. (Probablemente sólo mi falta de familiaridad con \ zs)

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

o para todos los espacios en blanco

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

lo leí como "reemplazar todas las apariciones de algo que no sea un espacio seguido de múltiples espacios y con algo y una único espacio".

+0

Por supuesto, esta versión es de un orden de magnitud más complicada de escribir, y de formular sobre la marcha, y eso es para un patrón casi tan trivial como sea posible. Te servirán para familiarizarte con '\ zs' y' \ ze'; pueden hacer maravillas con la escritura y la legibilidad de patrones más complejos (¡especialmente cuando tienes razones para usar ambos a la vez!). –

+0

Seguramente miraré '\ zs' y' \ ze', pero también suelo usar mis expresiones regulares en python y sed. Por lo tanto, puede ser agradable tener una solución que funcione en múltiples aplicaciones. –

6

Aquí hay muchas buenas respuestas (especialmente las de Aristóteles: \zs y \ze que valen la pena aprender). Simplemente para la corrección, también se puede hacer esto con un negativo afirmación mirada detrás:

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

esto dice "encontrar 2 o más espacios (' \{2,}') que no están precedidos por 'el inicio de la línea seguida por cero o más espacios '". Si prefiere reducir el número de barras invertidas, también se puede hacer esto:

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

pero sólo le ahorra dos personajes! También puede usar ' +' en lugar de ' {2,}' si no le molesta realizar una gran cantidad de cambios redundantes (es decir, cambiar un espacio individual a un espacio individual).

Usted podría también utilizar el puesto de observación detrás negativa a simplemente comprobar si hay un solo carácter no-espacio:

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

que es lo mismo que (una versión ligeramente modificada de la de Aristóteles para el tratamiento de los espacios y las pestañas como el mismo con el fin de ahorrar un poco de mecanografía):

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

Ver:

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

y (leer todo!):

:help pattern.txt 
1

respondiste; pero de todos modos lanzaría mi flujo de trabajo.

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

Rápido y fácil de recordar. Hay soluciones mucho más elegantes arriba; pero solo mi .02.

+1

esta no es una buena solución: primero eliminará el espacio en blanco inicial, que el autor de la pregunta desea evitar. En segundo lugar, puede hacer 100 @: ejecutar 100 veces los contenidos del registro: (que es el último comando ex) – Benoit

+1

por lo tanto, dije que no es la mejor respuesta en mi respuesta :) – wom

+2

Todavía encuentro esta respuesta útil, aunque lo hace no responde bien las preguntas de OP. –

Cuestiones relacionadas