Remova espaços principais comuns


19

Ao codificar em Python, às vezes você deseja uma sequência multilinha dentro de uma função, por exemplo

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

(A barra invertida é para remover uma nova linha principal)

Se você tentar realmente imprimir s, no entanto, receberá

    Line 1
    Line 2
    Line 3

Não é isso que queremos! Há muito espaço em branco à esquerda!

O desafio

Dada uma sequência multilinha composta apenas por caracteres alfanuméricos, espaços e novas linhas, remova todos os espaços comuns do início de cada linha. É garantido que cada linha tenha pelo menos um caractere que não seja de espaço e não terá espaços à direita. A saída pode não ter espaços em branco externos, seja antes ou depois de toda a saída ou de uma linha individual (com exceção de uma única nova linha de rastreamento opcional).

A entrada pode ser via STDIN ou argumento de função e a saída pode ser via STDOUT ou valor de retorno da função. Você não pode usar nenhum built-in projetado para deduzir cadeias de linhas múltiplas ou executar esta tarefa exata, por exemplo, do Python textwrap.dedent.

Isso é , então a solução com o menor número de bytes vence. Aplicam-se brechas padrão .

Casos de teste

"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 exemplo, o último caso de teste é

   a   b
     c     d
    e f

e deve ficar assim depois de remover os espaços à esquerda:

a   b
  c     d
 e f

A saída pode ter espaços em branco à direita?
orlp

@orlp Não, não pode, vai esclarecer.
Sp3000 16/07/2015

Respostas:


12

CJam, 20 14 bytes

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

Algoritmo :

  • Primeiro dividimos a entrada em novas linhas e tiramos uma cópia ( qN/_)
  • Em seguida, a menor coluna com caractere não espacial é calculada transpondo a matriz separada por nova linha e procurando simplesmente o índice da primeira linha sem espaço ( z{S-}#)
  • Em seguida, simplesmente removemos muitos caracteres de cada linha ( f>)
  • Por fim, nos juntamos novamente à nova linha ( N*)

Expansão do 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

Experimente online aqui


8

Pitão, 19 18 17 14 bytes

jbu>R!rhCG6G.z

A implementação é bem legal.

  1. u .zpega todas as linhas de stdin em uma matriz, coloca-a G. Em seguida, avalia o corpo interno, coloca o resultado Ge continua fazendo isso até que não mais mude (ponto fixo).

  2. !rhCG6transpõe G, obtém o primeiro elemento da matriz transposta (a primeira coluna), retira-o de qualquer espaço em branco e verifica se há algum caractere que não seja de espaço em branco.

  3. O valor de 2 é um booleano, que pode ser visto como um int 0 ou 1. >R Gpega esse número e corta muitos caracteres à esquerda de cada linha G. As etapas 1, 2 e 3 combinadas basicamente significam que ele continuará removendo as colunas de espaço em branco até que não haja mais nenhuma coluna de espaço em branco puro.

  4. jb une a matriz de linhas por novas linhas e a imprime.


2
Você pode, por favor, dar uma pequena explicação para isso? Isso é muito estranho para mim!
bobbel

2
@bobbel Explicação adicionada.
orlp

Realmente ótimo, obrigado! Nunca ouvi falar sobre isso! Para tentar isso eu on-line encontrados: pyth.herokuapp.com/...
bobbel

8

sed - 26 bytes

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

correr com -rz

Bem direto:

  /(^|\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

-rA opção ativa regexps estendidos, -zlê toda a entrada como uma única sequência (na verdade, usa NUL-byte como delimitador de linha)


Você não precisa, :;N;$!bou similar, para reunir as linhas de entrada em um único espaço de padrão? Edit: não, você não; é para isso que -zserve a bandeira.
precisa

Você pode jogar golfe :;/^\S/M!s/^ //mg;t, agora não exigindo-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);!.

Edit : Mudou completamente a minha resposta. Agora ele usa códigos de caracteres em vez de cadeias.

Um exemplo de chamar isso seria a(` a b\n c d\n e f`)., com aspas. Pode ser necessário usar aspas duplas, "se você tiver um distrib SWI-Prolog antigo.


5

Julia, 93 92 81 bytes

Economizou 10 bytes graças a Glen O.

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

Isso cria uma função sem nome que aceita uma string e imprime no stdout.

Ungolfed + explicação:

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

Você pode economizar espaço procurando o primeiro não espaço, em vez de contar o número de espaços. Em vez de minimum([length(search(j, r"^ +")) for j in p])+1usar minimum([search(j,r"[^ ]")[1]for j=p]). Como o desafio afirma que todas as linhas terão texto sem espaço, é seguro e economiza 9 bytes (incluindo 3 salvos usando em =vez de `em ). Still looking to see if more can be saved. (I wish I could drop the [1]`, mas a pesquisa produz uma matriz de enumeradores do tipo Any, enquanto o mínimo exige um tipo Int)
Glen O

Desculpe o erro acima - aparentemente, usei minhas edições - não são 9 bytes, mas 6, porque não notei que você usou = na forma de golfe. De qualquer forma, eu posso salvar mais dois caracteres definindo p no início do loop for:s->for i=(p=split(s,"\n")) println(i[minimum([search(j,r"[^ ]")[1]for j=p]):end])end
Glen O

OK, aqui está outro para fazer a barba um pouco mais - em vez de usar minimum(x)quando xuma matriz é usada min(x...)por um byte extra salvo (vou adicionar este à minha lista de dicas de golfe para Julia).
Glen O

@GlenO Nice, obrigado pelas sugestões. Além disso, como Julia usa PCRE, os caracteres não espaciais podem ser comparados com, \Se não [^ ], o que salva um byte.
Alex A.

Ei, obrigado por mencionar isso - eu não sou bom com regex, mas acabou \Ssendo útil para minha solução também.
Glen O

4

Java, 159

Porque há uma falta conspícua 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));}

São apenas loops comparando comprimento com comprimento aparado e cuspindo substrings. Nada muito chique. Para os prejudicados na barra de rolagem:

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

Obrigado @ThisSuitIsBlackNot pela sugestão de usar o loop implícito do Perl

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

O acima é pontuado como 30 bytes para a linha de código + 3 para 00psinalizadores.

Versão original, como uma função:

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

Isso coloca o argumento em questão $_e tenta corresponder avidamente o espaço em branco que está presente em todas as linhas com /^( +).*(\n\1.*)*$/- se for bem-sucedido, $1agora contém o prefixo comum mais longo e executamos a substituição s/^$1//mgrpara excluí-lo do início de cada linha e retornar a sequência resultante.

Teste

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

Muito legal. Você pode cortar alguns bytes executando a linha de comando: perl -00pe '/^( +).*(\n\1.*)*$/&&s/^$1//mg'(30 bytes + 3 para 00p).
ThisSuitIsBlackNot

/medirige-se para olhar para cima -00p; Obrigado @ThisSuit
Toby Speight

3

Python 2, 86 79 75 bytes

Definitivamente, isso pode ser encurtado um pouco mais, mas agora não é ruim.

Obrigado ao xnor por salvar 4 bytes!

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

1
Uma maneira um pouco mais curta de contar espaços à esquerda é x.find(x.strip()).
Xnor

@xnor boa ligação, obrigado! Estive esperando por uma solução de 60 bytes de você o dia todo; P
Kade

input()no Python 2 se engasgaria com esses dados.
Steven Rumbalski

@StevenRumbalski, presumo que a entrada seja cercada por aspas. Eu costumava adicionar 2 à contagem de bytes para explicar isso, mas várias pessoas disseram que eu não precisava.
Kade

1
Este programa é triste:):
HyperNeutrino 8/17

3

Ruby: 77 73 70 66 65 58 57 40 caracteres

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

Exemplo de execução:

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
Que tal f=->t{t.gsub /^#{t.scan(/^ */).min}/,""}?
Ventero

Isso é ótimo, @Ventero. Obrigado.
Manatwork 17/07/2015

2

C #, 18 + 145 = 163 bytes

Requer (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()))));}

O método calcula a menor quantidade de espaços iniciais nas linhas e cria uma nova sequência construída de todas as linhas, com N caracteres ignorados (onde N é o número calculado anteriormente).


1

C #, total de 149 bytes

Praticamente a mesma solução que os do ProgramFOX, embora o número de caracteres a serem cortados seja calculado manualmente.

using System.Linq;

E a própria função:

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 eu não tinha visto a sua solução até depois que eu atualizado a página btw: o)
Sok

1

Python 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 é totalmente usando apenas correspondência e substituições 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 sempre, apenas o Firefox, desde o ES6. Adicionará a versão ES5 posteriormente.


1
Parece que seria mais curto para escrever um literal de Expressões Regulares como uma string e, em seguida, eval-lo
Downgoat

@ vihan1086 você pode estar certo. Deixe-me tentar.
Optimizer

1

K, 31 bytes

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

Pega na entrada uma lista de strings e imprime o resultado em stdout.


1

Haskell, 52 bytes

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

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

Como 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

Dados de teste

s="\
   a   b
     c     d
    e f"

Responda

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

Resultado

a   b
  c     d
 e f

2
Algumas mudanças simples no golfe reduzem isso para 56 na minha contagem:cut -c$[`grep -o '^ *'<<<"$s"|sort|sed q|wc -c`]-<<<"$s"
Digital Trauma

1
@DigitalTrauma: Bom, eu esqueci a $[]aritmética. Usar cutpara a seleção de colunas é muito melhor. Eu nunca vi sed qcomo uma alternativa para head -n1, é um bom truque de golfe. Obrigado!
Thor

2
Em relação ao head -n1vs sed q, há uma lineferramenta no pacote util-linux.
Manatwork 17/07/2015

@ manatwork: Isso salva um personagem, eu vou usá-lo. Note que está obsoleto e pode desaparecer no futuro, isto é de deprecated.txt na árvore de fontes do util-linux: "Por que: inútil, ninguém usa este comando, o head (1) é melhor".
Thor

1

R, 118 111 bytes

Usando as maravilhosas funções de string do R :) Isso é semelhante / igual a outras soluções já publicadas. A entrada é através de STDIN e os gatos para STDOUT.

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

Teste e explicação

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

Ei, parabéns pelo representante 3k!
Alex A.

@AlexA. Cheers, não achava que era importante para mim ... mas :)
MickyT

Quer dizer que sua vida não gira em torno de pontos falsos da Internet? : P
Alex A.

@AlexA. Espero que não :) parabéns em 6k #
MickyT

1

Julia, 72 62 61 57 54 49 bytes

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

Ungolfed:

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

Solução mais antiga (57 bytes):

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

Solução 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)

Pega uma sequência como argumento e retorna uma sequência (com nova linha à direita).

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

Exemplo:

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ð*Ûζ»

Experimente online!


Espere, *repete a seqüência de b uma quantidade de vezes? .. Será que não sabia sobre esse recurso de *. Normalmente faço s∍(troco e alongo) quando quero repetir um determinado caractere.
Kevin Cruijssen

Sim, de fato, isso funciona para strings, principalmente porque a vetorização não faz muito sentido no caso de strings e иproduz uma lista de caracteres.
Mr. 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 exemplo...

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]}}'

Resultado...

a   b
  c     d
 e f

Apenas dicas pouco testadas: não capture /^( +)//^ +/(então você terá o valor necessário em t[0]vez de t[1]); mudar s==""!s; remova o {e }ao redor do código depois if; remova o ;antes }; usando a função específica do Gawk para remover o código {e }ao redor dele depois de for: {sub(s,"",z[r]);print z[r]}print gensub(s,"",1,z[r]).
manatwork

Lamento dizer, mas seu código original e o da minha otimização de tamanho estão falhando na entrada com uma linha não recuada, diferente da última. (Por exemplo "um \ nzero \ n \ um \ dois".)
manatwork

0

C GCC, 74 bytes

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

Remove apenas todo o espaço em branco, não relacionado às linhas anteriores, solicitando ajuda para concluir. TAMBÉM, em termos de espaços em branco comuns, o OP significa que qual linha tem menos espaços à esquerda, ou seja, o número de espaços a serem removidos de cada linha?


Sim, o uso da linha com o menor número de espaços à esquerda está correto.
SP3000

0

Empilhados , não concorrentes, 43 bytes

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

Experimente online!

Isso funciona encontrando a quantidade de espaços no início de cada linha ( '^ +'match$#'"!), obtendo o mínimo, repita um espaço várias vezes e substituindo-o por nada em cada linha.




-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

As novas linhas são necessárias e são contadas como 1 byte cada:

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

Demo

Assim como outras respostas do ES6, elas funcionam apenas no Firefox no 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
Seria ótimo se o downvoter poderia 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`
`

As novas linhas são significativas

Demonstração 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, tentando remover um espaço de cada vez de cada linha até encontrar um não espaço.

Teste a execução do trecho abaixo - sendo ES6, apenas 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>

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.