Rimuovi gli spazi iniziali comuni


19

Quando si codifica in Python, a volte si desidera una stringa multilinea all'interno di una funzione, ad es

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

(La barra rovesciata consiste nel rimuovere una nuova riga iniziale)

Se provi a stampare effettivamente s, tuttavia, otterrai

    Line 1
    Line 2
    Line 3

Non è quello che vogliamo affatto! C'è troppo spazio bianco principale!

La sfida

Data una stringa multilinea composta solo da caratteri alfanumerici, spazi e nuove righe, rimuovi tutti gli spazi comuni dall'inizio di ogni riga. Ogni linea è garantita per avere almeno un carattere non spaziale e non avrà spazi finali. L'output potrebbe non avere spazi bianchi estranei, sia prima che dopo l'intero output o una singola riga (ad eccezione di una singola nuova riga finale facoltativa).

L'ingresso può essere tramite STDIN o argomento della funzione e l'output può essere tramite STDOUT o il valore di ritorno della funzione. Non è possibile utilizzare alcun builtin progettato per dedurre stringhe multilinea o eseguire questo compito esatto, ad esempio Python textwrap.dedent.

Questo è , quindi vince la soluzione nel minor numero di byte. Si applicano scappatoie standard .

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

Ad esempio, l'ultimo caso di test è

   a   b
     c     d
    e f

e dovrebbe apparire così dopo aver rimosso gli spazi iniziali:

a   b
  c     d
 e f

L'output può contenere spazi vuoti finali?
orlp,

@orlp No, potrebbe non chiarire.
Sp3000,

Risposte:


12

CJam, 20 14 byte

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

Algoritmo :

  • Dapprima dividiamo l'input su newline e ne prendiamo una copia ( qN/_)
  • Quindi la colonna più piccola con carattere non spaziale viene calcolata trasponendo l'array separato newline e quindi semplicemente cercando l'indice della prima riga non tutto spazio ( z{S-}#)
  • Quindi rimuoviamo semplicemente quel numero di caratteri da ogni riga ( f>)
  • Infine, ci uniamo nuovamente da Newline ( N*)

Espansione del codice

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

Provalo online qui


8

Pyth, 19 18 17 14 byte

jbu>R!rhCG6G.z

L'implementazione è piuttosto interessante.

  1. u .zprende tutte le linee di stdin in un array, lo inserisce G. Quindi valuta il corpo interno, inserisce il risultatoG e continua a farlo fino a quando non cambia più (punto fisso).

  2. !rhCG6 traspone G , ottiene il primo elemento dell'array trasposto (la prima colonna), lo spoglia di qualsiasi spazio bianco e controlla se sono rimasti caratteri non bianchi.

  3. Il valore da 2 è un valore booleano, che può essere visto come un int 0 o 1. >R Gprende questo numero e suddivide il numero di caratteri a sinistra di ogni riga inG . I passaggi 1, 2 e 3 combinati significano sostanzialmente che rimuoverà le colonne di spazi bianchi fino a quando non rimarrà più alcuna colonna di spazi bianchi.

  4. jb unisce la serie di linee di newline e la stampa.


2
Potete per favore dare una piccola spiegazione a questo? Questo è molto strano per me!
Bob

2
@bobbel Spiegazione aggiunta.
orlp,

Davvero fantastico, grazie! Non ne ho mai sentito parlare! Per provare questo online ho trovato: pyth.herokuapp.com/…
bobbel il

8

sed - 26 byte

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

Corri con -rz

Abbastanza diretto:

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

-ropzione attiva regexps estesi, -zlegge l'intero input come una singola stringa (attualmente utilizza il byte NUL come delimitatore di riga)


Non hai bisogno :;N;$!bo simili per cominciare, per riunire le linee di input in un unico spazio del modello? Modifica: no non lo fai; ecco a cosa -zserve la bandiera.
Toby Speight,

Puoi giocare a golf a questo :;/^\S/M!s/^ //mg;t, ora non richiede-r
Kritixi Lithos il

7

SWI-Prolog, 233 223 217 byte

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);!.

Modifica : ho completamente cambiato la mia risposta. Ora utilizza i codici carattere anziché le stringhe.

Un esempio di come chiamare questo sarebbe a(` a b\n c d\n e f`)., con backquotes. Potrebbe essere necessario utilizzare virgolette doppie "se si dispone di una vecchia distribuzione SWI-Prolog.


5

Julia, 93 92 81 byte

Risparmiato 10 byte grazie a Glen O.

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

Questo crea una funzione senza nome che accetta una stringa e stampa su stdout.

Ungolfed + spiegazione:

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

Puoi risparmiare un po 'di spazio cercando il primo non-spazio, anziché contare il numero di spazi. Piuttosto che minimum([length(search(j, r"^ +")) for j in p])+1usare minimum([search(j,r"[^ ]")[1]for j=p]). Poiché la sfida afferma che tutte le righe avranno testo non spaziale, è sicura e ti salva 9 byte (inclusi 3 salvati usando =invece di `in ). Still looking to see if more can be saved. (I wish I could drop the [1]`, ma la ricerca produce un array di enumeratori di tipo Any, mentre il minimo richiede un tipo Int)
Glen O

Scusa l'errore sopra - a quanto pare, ho esaurito le mie modifiche - non è di 9 byte, ma 6, perché non ho notato che avresti usato = nella forma del golf. Ad ogni modo, posso salvare altri due personaggi definendo p nell'avvio del ciclo for:s->for i=(p=split(s,"\n")) println(i[minimum([search(j,r"[^ ]")[1]for j=p]):end])end
Glen O

OK, eccone un altro per radere un po 'di più - piuttosto che usare minimum(x)quando xè un array, usa min(x...), per un byte in più salvato (lo aggiungerò al mio elenco di suggerimenti per il golf di Julia).
Glen O

@GlenO Bello, grazie per i suggerimenti. Inoltre, poiché Julia utilizza PCRE, è possibile abbinare caratteri non spaziali \Spiuttosto che [^ ], il che consente di salvare un byte.
Alex A.

Ehi, grazie per averlo detto - non sono bravo con regex, ma si è scoperto che \Sè utile anche per la mia soluzione.
Glen O

4

Java, 159

Perché c'è una cospicua mancanza di 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));}

Sono solo i cicli che confrontano la lunghezza con la lunghezza ritagliata, quindi sputano sottostringhe. Niente di troppo elegante. Per i non abilitati alla barra di scorrimento:

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

Grazie @ThisSuitIsBlackNot per il suggerimento di utilizzare il ciclo implicito di Perl

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

Quanto sopra è segnato come 30 byte per la riga di codice + 3 per i 00pflag.

Versione originale, in funzione:

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

Questo inserisce l'argomento $_, quindi tenta di far corrispondere avidamente lo spazio bianco presente su tutte le righe con /^( +).*(\n\1.*)*$/- in caso di successo,$1 ora contiene il prefisso comune più lungo ed eseguiamo la sostituziones/^$1//mgr per eliminarlo dall'inizio di ogni riga e restituire la stringa risultante.

Test

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

Molto bello. È possibile radere alcuni byte eseguendo sulla riga di comando: perl -00pe '/^( +).*(\n\1.*)*$/&&s/^$1//mg'(30 byte + 3 per 00p).
ThisSuitIsBlackNon

/mesi allontana per guardare in alto -00p; grazie @ThisSuit
Toby Speight,

3

Python 2, 86 79 75 byte

Questo può quasi sicuramente essere abbreviato un po 'di più, ma in questo momento non è male.

Grazie a xnor per aver salvato 4 byte!

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

1
Un modo leggermente più breve per contare gli spazi iniziali è x.find(x.strip()).
xnor

@xnor buona chiamata, grazie! Ho aspettato una soluzione a 60 byte da te tutto il giorno; P
Kade,

input()in Python 2 soffocerebbe questi dati.
Steven Rumbalski,

@StevenRumbalski, suppongo che l'input sia racchiuso tra virgolette. Per aggiungere questo, aggiungevo 2 al conteggio dei byte, ma più persone hanno affermato che non è necessario.
Kade,

1
Questo programma è triste:):
HyperNeutrino

3

Rubino: 77 73 70 66 65 58 57 40 caratteri

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

Esecuzione di esempio:

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

Ottimo, @Ventero. Grazie.
arte

2

C #, 18 + 145 = 163 byte

Richiede (18 byte):

using System.Linq;

Metodo (145 byte):

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

Il metodo calcola la quantità più bassa di spazi iniziali sulle linee e crea una nuova stringa costruita con tutte le linee, con N caratteri saltati (dove N è il numero precedentemente calcolato).


1

C #, 149 byte in totale

Praticamente la stessa soluzione di ProgramFOX, sebbene il numero di caratteri da tagliare sia calcolato manualmente.

using System.Linq;

E la funzione stessa:

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 non avevo visto la soluzione fino a quando dopo aver aggiornato la pagina di 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 byte

Questo utilizza totalmente solo la corrispondenza e le sostituzioni 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>

Come sempre, solo Firefox, da ES6. Aggiungerà la versione ES5 più tardi.


1
Sembra che sarebbe più breve scrivere letteralmente un'espressione regolare come una stringa e poi
valutarla

@ vihan1086 potresti avere ragione. Lasciami provare.
Ottimizzatore

1

K, 31 byte

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

Riceve un elenco di stringhe e stampa il risultato su stdout.


1

Haskell, 52 byte

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

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

Come funziona:

                                           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 byte):

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

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

Dati di test

s="\
   a   b
     c     d
    e f"

Risposta

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

Produzione

a   b
  c     d
 e f

2
Nel mio conteggio alcune semplici modifiche al golf lo portano a 56:cut -c$[`grep -o '^ *'<<<"$s"|sort|sed q|wc -c`]-<<<"$s"
Digital Trauma,

1
@DigitalTrauma: Bello, mi sono dimenticato $[]dell'aritmetica. L'uso cutper la selezione delle colonne è molto meglio. Non ho mai visto sed qun'alternativa a head -n1, è un buon trucco da golf. Grazie!
Thor,

2
Per quanto riguarda head -n1vs sed q, c'è uno linestrumento nel pacchetto util-linux.
arte

@manatwork: che salva un personaggio, lo userò. Nota che è deprecato e potrebbe scomparire in futuro, questo è da deprecated.txt nell'albero dei sorgenti di util-linux: "Perché: inutile, nessuno usa questo comando, head (1) è migliore".
Thor,

1

R, 118 111 byte

Usando le meravigliose funzioni di stringa di R :) Questo è simile / uguale ad altre soluzioni già pubblicate. L'input avviene tramite STDIN e i gatti su STDOUT.

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

Test e spiegazione

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

Hey, congrats on 3k rep!
Alex A.

@AlexA. Cheers, didn't think it was important to me ... but :)
MickyT

You mean your life doesn't revolve around fake Internet points? :P
Alex A.

@AlexA. Hopefully not :) congrats on 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

Older solution (57 bytes):

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

Original solution (72 bytes):

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

1

k (24 bytes)

Takes a string as an argument and returns a string (with trailing new-line).

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

Example:

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

Try it online!


Wait, * repeats the string b a amount of times?.. Didn't knew about that feature of *. I usually do s∍ (swap and lengthen) when I want to repeat a certain character.
Kevin Cruijssen

Yes, indeed, that works for strings, mainly because vectorization doesn't quite make sense in the case of strings and и yields a list of characters.
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]}}

For example...

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

Output...

a   b
  c     d
 e f

Just barely tested hints: don't capture /^( +)//^ +/ (then you will have the needed value in t[0] instead of t[1]); change s==""!s; remove the { and } around the code after if; remove the ; before }; using Gawk-specific function to be able to remove the { and } around the code after for: {sub(s,"",z[r]);print z[r]}print gensub(s,"",1,z[r]).
manatwork

Sorry to say, but both your original code and the one with my size optimization are failing on input with an unindented line, other than the last one. (For example "␠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,_));}

Only removes all whitespace, not relating to previous lines, requesting help to finish. ALSO, in terms of common whitespaces, does the OP mean that which line has the fewest leading spaces, that is the number of spaces that is to be removed from each line?


Yes, using the line with the fewest leading spaces is correct.
Sp3000

0

Stacked, noncompeting, 43 bytes

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

Try it online!

This works by finding the amount of spaces at the beginning of each line ('^ +'match$#'"!), getting the minimum, repeat a space that many times, and replacing that with nothing on each line.




-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

The newlines are necessary and are counted as 1 byte each:

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

Demo

As with other ES6 answers, they only work in Firefox at the moment.

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
It would be great if the downvoter could explain…
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`
`

The new lines are significant

ES5 Demo:

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

Recursive, trying to remove one space at a time from each row until a non-space is found.

Test running the snippet below - being ES6, Firefox only

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>

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.