Eliminar espacios iniciales comunes


19

Al codificar en Python, a veces desea una cadena multilínea dentro de una función, por ejemplo

def f():
    s = """\
    Line 1
    Line 2
    Line 3"""

(La barra diagonal inversa es eliminar una nueva línea principal)

sSin embargo, si intenta imprimir realmente , obtendrá

    Line 1
    Line 2
    Line 3

¡Eso no es lo que queremos en absoluto! ¡Hay demasiado espacio en blanco!

El reto

Dada una cadena de varias líneas que consta solo de caracteres alfanuméricos, espacios y líneas nuevas, elimine todos los espacios comunes desde el principio de cada línea. Se garantiza que cada línea tendrá al menos un carácter que no sea espacio, y no tendrá espacios finales. La salida puede no tener espacios en blanco extraños, ya sea antes o después de la salida completa o una línea individual (con la excepción de una nueva línea final opcional).

La entrada puede ser a través de STDIN o argumento de función, y la salida puede ser a través de STDOUT o valor de retorno de función. No puede usar ningún builtins diseñado para abollar cadenas multilínea o realizar esta tarea exacta, por ejemplo, Python textwrap.dedent.

Este es el , por lo que gana la solución en la menor cantidad de bytes. Se aplican lagunas estándar .

Casos de prueba

"a"                                  ->   "a"
"   abc"                             ->   "abc"
"   abc\n def\n  ghi"                ->   "  abc\ndef\n ghi"
"    a\n    b\n    c"                ->   "a\nb\nc"
"    a\n    b\n    c\nd"             ->   "    a\n    b\n    c\nd"
"   a   b\n     c     d\n    e f"    ->   "a   b\n  c     d\n e f"

Por ejemplo, el último caso de prueba es

   a   b
     c     d
    e f

y debería verse así después de quitar los espacios iniciales:

a   b
  c     d
 e f

¿Puede la salida tener espacios en blanco al final?
orlp

@orlp No, no puede, aclarará.
Sp3000

Respuestas:


12

CJam, 20 14 bytes

qN/_z{S-}#f>N*

algoritmo :

  • Primero dividimos la entrada en las nuevas líneas y tomamos una copia (qN/_ )
  • Luego, la columna más pequeña con caracteres que no son espacios se calcula transponiendo la matriz separada de nueva línea y luego simplemente buscando el índice de la primera fila que no es todo espacio (z{S-}# )
  • Luego, simplemente eliminamos esa cantidad de caracteres de cada línea (f> )
  • Finalmente, nos unimos por newline nuevamente ( N*)

Expansión de código

qN/               e# Read the entire input and split it on newline
   _z             e# Take a copy and transpose rows with columns.
                  e# Now we would have a bunch of all space rows. These rows are the ones
                  e# we want to remove (in form of columns) 
     {  }#        e# Get the index of the first item from the transposed array that returns
                  e# true for this block
      S-          e# From each part, remove spaces. If the part is all-space, it will return
                  e# an empty string, which is false in CJam. We finally will get the index
                  e# of the first non-all-space row (or column)
          f>      e# We take that index and remove that many characters from starting of each
                  e# row of the initial newline separated input
            N*    e# Join the array back using newlines and automatically print the result

Pruébalo en línea aquí


8

Pyth, 19 18 17 14 bytes

jbu>R!rhCG6G.z

La implementación es bastante buena.

  1. u .zagarra todas las líneas de stdin en una matriz, lo pone dentro G. Luego evalúa el cuerpo interno, coloca el resultado Gy continúa haciéndolo hasta que ya no cambia (punto fijo).

  2. !rhCG6se transpone G, obtiene el primer elemento de la matriz transpuesta (la primera columna), lo despoja de cualquier espacio en blanco y comprueba si quedan caracteres que no sean espacios en blanco.

  3. El valor de 2 es un booleano, que puede verse como un int 0 o 1. >R Gtoma este número y corta esa cantidad de caracteres a la izquierda de cada línea G. Los pasos 1, 2 y 3 combinados básicamente significan que seguirá quitando columnas de espacios en blanco hasta que no quede una columna de espacios en blanco pura.

  4. jb une el conjunto de líneas por líneas nuevas y lo imprime.


2
¿Puedes por favor dar una pequeña explicación a esto? ¡Esto es muy raro para mí!
bobbel

2
@bobbel Explicación agregada.
orlp

Realmente genial, gracias! ¡Nunca escuché sobre eso! Para probar esto en línea encontré: pyth.herokuapp.com/…
bobbel

8

sed - 26 bytes

:;/(^|\n)\S/q;s/^ //mg;b

corre con -rz

Muy claro:

  /(^|\n)\S/q;           - quit if there is a line that starts with non-space
              s/^ //mg;  - remove exactly one space in each line
:;                     b - repeat

-rLa opción activa expresiones regulares extendidas, -zlee la entrada completa como una sola cadena (en realidad usa el byte NUL como delimitador de línea)


¿No necesita :;N;$!bo similar para comenzar, para reunir las líneas de entrada en un solo espacio de patrón? Editar: no, no lo haces; para eso está la -zbandera.
Toby Speight

Puedes jugar golf con esto :;/^\S/M!s/^ //mg;t, ahora no requiere-r
Kritixi Lithos

7

SWI-Prolog, 233 223 217 bytes

a(A):-b(A,0,0,0,N),w(A,N,0).
b([A|T],P,K,M,N):-P=1,(A=10,b(T,0,0,M,N);b(T,1,0,M,N));A\=32,(M=0;K<M),b(T,1,0,K,N);I=K+1,b(T,0,I,M,N).
b(_,_,_,N,N).
w([A|T],N,P):-P<N,A=32,Q=P+1,w(T,N,Q);put(A),A=10,w(T,N,0);w(T,N,P);!.

Editar : Cambié completamente mi respuesta. Ahora usa códigos de caracteres en lugar de cadenas.

Un ejemplo de llamar a esto sería a(` a b\n c d\n e f`)., con comillas inversas. Es posible que necesite usar comillas dobles "en su lugar si tiene una antigua distribución SWI-Prolog.


5

Julia, 93 92 81 bytes

Guardado 10 bytes gracias a Glen O.

s->for i=(p=split(s,"\n")) println(i[min([search(j,r"\S")[1]for j=p]...):end])end

Esto crea una función sin nombre que acepta una cadena e imprime en stdout.

Ungolfed + explicación:

function f(s)
    # Split s into an array on newlines
    p = split(s, "\n")

    # Get the smallest amount of leading space by finding the
    # position of the first non-space character on each line
    # and taking the minimum
    m = min([search(j, r"\S")[1] for j in p]...)

    # Print each line starting after m
    for i in p
        println(i[m:end])
    end
end

Puede ahorrar algo de espacio buscando el primer no espacio, en lugar de contar el número de espacios. En lugar de minimum([length(search(j, r"^ +")) for j in p])+1usar minimum([search(j,r"[^ ]")[1]for j=p]). Dado que el desafío establece que todas las líneas tendrán texto sin espacio, es seguro y le ahorra 9 bytes (incluidos 3 guardados mediante el uso en =lugar de `en ). Still looking to see if more can be saved. (I wish I could drop the [1]`, pero la búsqueda produce una matriz de enumerador de tipo Any, mientras que el mínimo requiere un tipo Int)
Glen O

Disculpe el error anterior, aparentemente, he usado mis ediciones, no son 9 bytes, sino 6, porque no noté que había usado = en la forma de golf. De todos modos, puedo guardar dos caracteres más definiendo p al iniciar el ciclo for:s->for i=(p=split(s,"\n")) println(i[minimum([search(j,r"[^ ]")[1]for j=p]):end])end
Glen O

De acuerdo, aquí hay otro para afeitarse un poco más: en lugar de usar minimum(x)cuándo xes una matriz, use min(x...), por un byte adicional guardado (voy a agregar este a mi lista de consejos de golf de Julia).
Glen O

@GlenO Agradable, gracias por las sugerencias. Además, dado que Julia usa PCRE, los caracteres que no son espacios se pueden combinar con, en \Slugar de [^ ], lo que ahorra un byte.
Alex A.

Hola, gracias por mencionar eso: no soy bueno con la expresión regular, pero resultó que también \Ses útil para mi solución.
Glen O

4

Java, 159

Porque hay una notable falta de Java ...

void f(String...a){int s=1<<30,b;a=a[0].split("\n");for(String x:a)s=(b=x.length()-x.trim().length())<s?b:s;for(String x:a)System.out.println(x.substring(s));}

Es solo bucles que comparan la longitud con la longitud recortada, luego escupen subcadenas. Nada muy elegante. Para las personas con discapacidad de la barra de desplazamiento:

void f(String...a){
    int s=1<<30,b;
    a=a[0].split("\n");
    for(String x:a)
        s=(b=x.length()-x.trim().length())<s?b:s;       
    for(String x:a)
        System.out.println(x.substring(s));
}

4

Perl, 47 33

Gracias @ThisSuitIsBlackNot por la sugerencia de usar el bucle implícito de Perl

#!/usr/bin/perl -00p
/^( +).*(\n\1.*)*$/&&s/^$1//mg

Lo anterior se puntúa como 30 bytes para la línea de código + 3 para las 00pbanderas.

Versión original, como una función:

sub f{$_=@_[0];/^( +).*(\n\1.*)*$/&&s/^$1//mgr}

Esto pone el argumento en $_, luego intenta igualar con avidez los espacios en blanco que están presentes en todas las líneas con /^( +).*(\n\1.*)*$/: si tiene éxito, $1ahora contiene el prefijo común más largo y ejecutamos el reemplazos/^$1//mgr para eliminarlo desde el principio de cada línea y devolver la cadena resultante.

Prueba

$ cat 53219.data
   a   b
     c     d
    e f
$ ./53219.pl <53219.data 
a   b
  c     d
 e f

Muy genial. Puede eliminar algunos bytes ejecutando en la línea de comando: perl -00pe '/^( +).*(\n\1.*)*$/&&s/^$1//mg'(30 bytes + 3 para 00p).
ThisSuitIsBlackNot

/mese dirige a mirar hacia arriba -00p; gracias @ThisSuit
Toby Speight

3

Python 2, 86 79 75 Bytes

Esto definitivamente se puede acortar un poco más, pero en este momento no está mal.

¡Gracias a xnor por guardar 4 bytes!

s=input().split('\n')
for k in s:print k[min(x.find(x.strip())for x in s):]

1
Una forma un poco más corta de contar espacios iniciales es x.find(x.strip()).
xnor

@xnor buena llamada, gracias! He estado esperando por usted una solución de 60 bytes todo el día; P
Kade

input()en Python 2 se atragantaría con estos datos.
Steven Rumbalski

@StevenRumbalski, supongo que la entrada está rodeada de comillas. Solía ​​agregar 2 al conteo de bytes para dar cuenta de esto, pero varias personas han dicho que no necesito hacerlo.
Kade

1
Este programa es triste:):
HyperNeutrino

3

Rubí: 77 73 70 66 65 58 57 40 caracteres

f=->t{t.gsub /^#{t.scan(/^ */).min}/,""}

Ejecución de muestra:

irb(main):001:0> f=->t{t.gsub /^#{t.scan(/^ */).min}/,""}
=> #<Proc:0x00000001855948@(irb):1 (lambda)>

irb(main):002:0> puts f["   a   b\n     c     d\n    e f"]
a   b
  c     d
 e f
=> nil

irb(main):003:0> f["   a   b\n     c     d\n    e f"] == "a   b\n  c     d\n e f"
=> true

2
¿Qué tal f=->t{t.gsub /^#{t.scan(/^ */).min}/,""}?
Ventero

Eso es genial, @Ventero. Gracias.
manatwork

2

C #, 18 + 145 = 163 bytes

Requiere (18 bytes):

using System.Linq;

Método (145 bytes):

string R(string s){var l=s.Split('\n');return string.Join("\n",l.Select(x=>string.Concat(x.Skip(l.Select(z=>z.Length-z.Trim().Length).Min()))));}

El método calcula la menor cantidad de espacios iniciales en las líneas y crea una nueva cadena compuesta de todas las líneas, con N caracteres omitidos (donde N es el número calculado previamente).


1

C #, 149 bytes en total

Prácticamente la misma solución que ProgramFOX, aunque el número de caracteres a recortar se calcula manualmente.

using System.Linq;

Y la función misma:

string D(string s){var l=s.Split('\n');int i=0;while(l.All(a=>a[i]==' '))i++;return string.Join("\n",l.Select(b=>b.Substring(i)));}

@ProgramFOX No había visto su solución hasta después de actualizar la página por cierto: o)
Sok

1

Pitón 3, 100

def f(s):t=s.split("\n");return"\n".join([c[min([len(c)-len(c.lstrip(" "))for c in t]):]for c in t])

1

JavaScript, ES6, 89 86 bytes

Este utiliza totalmente solo la correspondencia y las sustituciones RegEx.

f=x=>eval(`x.replace(/(^|\\n) {${--`
${x}`.match(/\n */g).sort()[0].length}}/g,"$1")`)

// Snippet related stuff
B.onclick=x=>P.innerHTML=f(T.value)
<textarea id=T></textarea><br>
<button id=B>Trim</button>
<pre id=P></pre>

Como siempre, solo Firefox, desde ES6. Agregará la versión ES5 más tarde.


1
Parece que sería más corto escribir un literal de expresión regular como una cadena y luego
evaluarlo

@ vihan1086 puede que tengas razón. Déjame intentarlo.
Optimizador

1

K, 31 bytes

{`0:(&/{(0;#*=x)@*x}'" "=x)_'x}

Introduce una lista de cadenas e imprime el resultado en stdout.


1

Haskell, 52 bytes

unlines.until(any(/=' ').map head)(map tail).lines

Ejemplo de uso: unlines.until(any(/=' ').map head)(map tail).lines $ " abc\n def\n ghi"->" abc\ndef\n ghi\n"

Cómo funciona:

                                           lines    -- split the input at newlines into a list of lines
        until                                       -- repeat the 2nd argument, i.e.
                                 map tails          -- cut off the heads of all lines
                                                    -- until the the first argument returns "True", i.e.
             any(/=' ').map head                    -- the list of heads contains at least one non-space
unlines                                             -- transform back to a single string with newlines in-between

1

Python, 94/95

lambda (94 bytes):

f=lambda s:'\n'.join(l[min(l.find(l.strip()) for l in s.split('\n')):] for l in s.split('\n'))

def (95 bytes)

def f(s):l=s.split('\n');m=min(i.find(i.strip())for i in l);return '\n'.join(i[m:] for i in l);

1

bash + sed + coreutils, 74 , 56 , 55

Datos de prueba

s="\
   a   b
     c     d
    e f"

Responder

cut -c$[`grep -o '^ *'<<<"$s"|sort|line|wc -c`]-<<<"$s"

Salida

a   b
  c     d
 e f

2
Algunos cambios simples en el golf lo reducen a 56 en mi cuenta:cut -c$[`grep -o '^ *'<<<"$s"|sort|sed q|wc -c`]-<<<"$s"
Digital Trauma

1
@ DigitalTrauma: Bien, me olvidé de la $[]aritmética. Usar cutpara la selección de columnas es mucho mejor. Nunca lo he visto sed qcomo una alternativa head -n1, es un buen truco de golf. ¡Gracias!
Thor

2
En cuanto a head -n1vs sed q, hay una lineherramienta en el paquete util-linux.
manatwork

@manatwork: Eso ahorra un personaje, lo usaré. Tenga en cuenta que está en desuso y puede desaparecer en el futuro, esto es de deprecated.txt en el árbol fuente de util-linux: "Por qué: inútil, nadie usa este comando, head (1) es mejor".
Thor

1

R 118 bytes

Usando las maravillosas funciones de cadena de R :) Esto es similar / igual a otras soluciones ya publicadas. La entrada es a través de STDIN y gatos a STDOUT.

cat(substring(a<-scan(,'',sep='|'),Reduce(min,lapply(strsplit(a,' '),function(x)min(which(x>''))-1))),sep='\n')

Prueba y explicación

> cat(substring(a<-scan(,'',sep='|'),Reduce(min,lapply(strsplit(a,' '),function(x)min(which(x>''))-1))),sep='\n')
1:                  a<-scan(,'',sep='|') # get the input lines
2:                                                         strsplit(a,' ') # split lines on spaces
3:                                                  lapply(                ,function(x)min(which(x>''))-1) # get min index - 1 for non space of each line
4:                                      ,Reduce(min,                                                      ) # get the min of those
5:        substring(                                                                                       ) # trim it off
6:    cat(                                                                                                  ,sep='\n') # output each line
7:
Read 6 items
              a<-scan(,'',sep='|') # get the input lines
                                                     strsplit(a,' ') # split lines on spaces
                                              lapply(                ,function(x)min(which(x>''))-1) # get min index - 1 for non space of each line
                                  ,Reduce(min,                                                      ) # get the min of those
    substring(                                                                                       ) # trim it off
cat(                                                                                                  ,sep='\n') # output each line
> 

¡Hola, felicidades por 3k rep!
Alex A.

@AlexA. Saludos, no pensé que fuera importante para mí ... pero :)
MickyT

¿Quieres decir que tu vida no gira en torno a puntos de Internet falsos? : P
Alex A.

@AlexA. Esperemos que no :) felicidades por 6k
MickyT

1

Julia, 72 62 61 57 54 49 bytes

g=s->ismatch(r"^\S"m,s)?s:g(replace(s,r"^ "m,""))

Sin golf:

g(s)=
if ismatch(r"^\S"m,s)       # Determines if there's a newline followed by something other than a space
                            # Note: the m in r"^ "m says to work in multiline mode.
    s                       # If there is, return the string as the final result.
else                        # otherwise...
    m=replace(s,r"^ "m,"")  # Remove first space after each newline, and space at start of string.
    g(m)                    # Feed back into the function for recursion
end

Solución anterior (57 bytes):

g(s)=ismatch(r"
\S","
"s)?s:g(replace(s,"
 ","
")[2:end])

Solución original (72 bytes):

g(s)=all([i[1]<33for i=split(s,"\n")])?g(replace(s,"\n ","\n")[2:end]):s

1

k (24 bytes)

Toma una cadena como argumento y devuelve una cadena (con una nueva línea al final).

{`/:(&//&:'~^s)_'s:`\:x}

Ejemplo:

k) f:{`/:(&//&:'~^s)_'s:`\:x};
k) f"   a   b\n     c     d\n    e f"
"a   b\n  c     d\n e f\n

1

05AB1E , 10 bytes

|©ζ®gð*Ûζ»

Pruébalo en línea!


Espera, *repite la cadena b de una cantidad de veces? .. ¿No sabían de esa característica de *. Normalmente hago s∍(intercambiar y alargar) cuando quiero repetir un cierto personaje.
Kevin Cruijssen

Sí, de hecho, eso funciona para cadenas, principalmente porque la vectorización no tiene mucho sentido en el caso de cadenas y иproduce una lista de caracteres.
Sr. Xcoder

0

Gawk, 101 100

{match($0,/^( +)/,t);if(t[1]<s||s==""){s=t[1]};z[NR]=$0;}END{for(r in z){sub(s,"",z[r]);print z[r]}}

Por ejemplo...

cat input.txt | gawk '{match($0,/^( +)/,t);if(t[1]<s||s==""){s=t[1]};z[NR]=$0;}END{for(r in z){sub(s,"",z[r]);print z[r]}}'

Salida...

a   b
  c     d
 e f

Solo sugerencias apenas probadas: no capture /^( +)//^ +/(entonces tendrá el valor necesario en t[0]lugar de t[1]); cambiar s==""!s; eliminar el {y }alrededor del código después if; quitar el ;antes }; usando la función específica de Gawk para poder eliminar el código {y sus }alrededores después de for: {sub(s,"",z[r]);print z[r]}print gensub(s,"",1,z[r]).
manatwork

Lamento decirlo, pero tanto su código original como el que tiene mi optimización de tamaño fallan en la entrada con una línea sin sangría, que no sea la última. (Por ejemplo, "\one \ nzero \ n␠one \ n␠␠two".)
manatwork

0

C GCC, 74 bytes

main(_,z){z=1;while(-~(_=getchar()))putchar(_==32&&z?0:(z=_==10?1:0,_));}

Solo elimina todo el espacio en blanco, no está relacionado con líneas anteriores, solicitando ayuda para finalizar. TAMBIÉN, en términos de espacios en blanco comunes, ¿el OP significa que qué línea tiene la menor cantidad de espacios iniciales, que es el número de espacios que se eliminarán de cada línea?


Sí, usar la línea con la menor cantidad de espacios iniciales es correcto.
Sp3000

0

Apilado , no competitivo, 43 bytes

:lines'^ +'match$#'"!MIN' '*0# '^'\+''mrepl

Pruébalo en línea!

Esto funciona al encontrar la cantidad de espacios al comienzo de cada línea ( '^ +'match$#'"!), obtener el mínimo, repetir un espacio tantas veces y reemplazarlo con nada en cada línea.




-1

CoffeeScript, 112 bytes

f=(x)->(a=x.split "\n").map((v)->v[Math.min.apply(null,a.map((v)->(r=/^ +/.exec v)&&r[0].length))...]).join "\n"

-1

JavaScript (ES6), 106 98 bytes

Las nuevas líneas son necesarias y se cuentan como 1 byte cada una:

f=x=>(a=x.split`
`).map(v=>v.slice(Math.min(...a.map(v=>(r=/^ +/.exec(v))&&r[0].length)))).join`
`

Manifestación

Al igual que con otras respuestas de ES6, solo funcionan en Firefox en este momento.

f=x=>(a=x.split`
`).map(v=>v.slice(Math.min(...a.map(v=>(r=/^ +/.exec(v))&&r[0].length)))).join`
`

// For demonstration purposes
console.log = x => X.innerHTML += x + `\n<hr>`;

console.log(f("a"));
console.log(f("   abc"));
console.log(f("   abc\n def\n  ghi"));
console.log(f("    a\n    b\n    c"));
console.log(f("    a\n    b\n    c\nd"));
console.log(f("   a   b\n     c     d\n    e f"));
<pre id=X></pre>


11
Sería genial si el votante podría explicar ...
rink.attendant.6

-1

JavaScript ES6, 85 bytes

s=>s.split`
`.map(z=>z.slice(Math.min(...s.match(/^ */gm).map(l=>l.length)))).join`
`

Las nuevas líneas son significativas.

Demostración ES5:

function t(s) {
  return s.split("\n").map(function(z) {
    return z.slice(Math.min.apply(0, s.match(/^ */gm).map(function(l) {
      return l.length;
    })));
  }).join('');
}

// Demo
document.getElementById('go').onclick = function() {
  document.getElementById('r').innerHTML = t(document.getElementById('t').value)
};
Input:
<br>
<textarea id="t"></textarea>
<br>
<button id="go">Run</button>
<br>Output:
<br>
<pre style="background-color:#DDD;" id="r"></pre>


-1

JavaScript ( ES6 ) 56

Recursivo, tratando de eliminar un espacio a la vez de cada fila hasta que se encuentre un no espacio.

Pruebe a ejecutar el fragmento a continuación: siendo ES6, solo Firefox

f=s=>(r=s.replace(/^./gm,x=>(k|=x>' ',''),k=0),k?s:f(r))

// Test
test=
[[ "a", "a" ]
,["   abc", "abc" ]
,["   abc\n def\n  ghi", "  abc\ndef\n ghi" ]
,["    a\n    b\n    c", "a\nb\nc" ]
,["    a\n    b\n    c\nd", "    a\n    b\n    c\nd" ]
,["   a   b\n     c     d\n    e f","a   b\n  c     d\n e f" ]]

var tb=''
test.forEach(t=>{
  t[2]=f(t[0])
  t[3]=t[2]==t[1]?'OK':'FAIL'
  tb+='<tr><td>'+t.join('</td><td>')+'</td></tr>'
})
B.innerHTML=tb
td { white-space: pre; font-family: monospace; border: 1px solid#444; vertical-align:top}
#I,#O { height:100px; width: 200px }
<b>Your test:</b>
<table><tr><td><textarea id=I></textarea></td>
<th><button onclick='O.innerHTML=f(I.value)'>-></button></th>
<td id=O></td></tr></table>
<b>Test cases:</b><br>
<table ><tr><th>Input</th><th>Expected</th><th>Output</th><th>Result</th></tr>
<tbody id=B></tbody></table>

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.