删除常见的前导空格


19

使用Python进行编码时,有时您希望在函数中使用多行字符串,例如

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

(反斜杠是删除前导的换行符)

s但是,如果您尝试实际打印出来,则会得到

    Line 1
    Line 2
    Line 3

那根本不是我们想要的!领先的空白太多了!

挑战

给定一个仅由字母数字字符,空格和换行符组成的多行字符串,请从每行的开头删除所有公共空格。保证每行至少有一个非空格字符,并且没有尾随空格。输出可能没有多余的空格,无论是在整个输出之前还是之后,还是在单独的行之后(单个可选的尾随换行符除外)。

输入可以通过STDIN或函数自变量,输出可以通过STDOUT或函数返回值。您不能使用任何旨在使多行字符串变短或执行此确切任务的内置程序,例如Python的textwrap.dedent

这是,因此以最少字节为单位的解决方案将获胜。有标准漏洞

测试用例

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

例如,最后一个测试用例是

   a   b
     c     d
    e f

并在除去前导空格后应如下所示:

a   b
  c     d
 e f

输出的末尾可以有空格吗?
orlp

@orlp否,可能会澄清。
Sp3000

Answers:


12

CJam,20个 14字节

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

算法

  • 我们首先在换行符上分割输入,然后复制(qN/_
  • 然后,通过换行换行分隔的数组,然后简单地查找第一个非全空格行(z{S-}#)的索引,即可计算出具有非空格字符的最小列
  • 然后,我们只需从每一行(f>)中删除那么多字符
  • 最后,我们再次通过换行(N*)加入

代码扩展

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

在这里在线尝试


8

Pyth,19 18 17 14字节

jbu>R!rhCG6G.z

实现非常酷。

  1. u .z获取数组中所有stdin行,并将其放入中G。然后,它评估内部主体,将结果放入中G,并继续执行该过程,直到不再更改(固定点)为止。

  2. !rhCG6transposes G,获取转置数组的第一个元素(第一列),将其除去任何空白,并检查是否还有任何非空白字符。

  3. 从2开始的值是一个布尔值,可以看作一个int 0或1。获取>R G该数字并在中的每一行的左侧切掉那么多字符G。将步骤1、2和3合并起来基本上意味着它将继续剥离空白列,直到没有剩余的纯空白列为止。

  4. jb 用换行符连接线阵列并进行打印。


2
您能对此做些解释吗?这对我来说很奇怪!
bobbel

2
@bobbel添加了解释。
orlp

真的很棒,谢谢!从来没有听说过!要在线尝试,我发现:pyth.herokuapp.com/…–
bobbel

8

sed-26个字节

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

-rz

很简单:

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

-r选项打开扩展的正则表达式,-z将整个输入读取为单个字符串(实际上使用NUL字节作为行定界符)


您是否不需要:;N;$!b或者类似地将输入线收集到单个模式空间中?编辑:不,你不;这就是-z标志的目的。
Toby Speight

您可以打高尔夫球:;/^\S/M!s/^ //mg;t,现在不需要-r
Kritixi Lithos

7

SWI-Prolog的,233个 223 217字节

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

编辑:完全改变了我的答案。现在,它使用字符代码代替字符串。

调用它的一个示例是a(` a b\n c d\n e f`).带有反引号的。"如果您有旧的SWI-Prolog 分配,则可能需要使用双引号。


5

朱莉娅93 92 81字节

感谢Glen O,节省了10个字节。

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

这将创建一个未命名的函数,该函数接受字符串并打印到stdout。

取消+说明:

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

您可以通过查找第一个非空格来节省一些空间,而不用计算空格数。而不是minimum([length(search(j, r"^ +")) for j in p])+1使用minimum([search(j,r"[^ ]")[1]for j=p])。由于该挑战指出所有行都将具有非空格文本,因此很安全,它可以节省9个字节(包括使用[1]中的` =代替` 保存的3个字节). Still looking to see if more can be saved. (I wish I could drop the ,但是搜索会生成类型为Any的枚举器数组,而最低要求int类型)
Glen O

请原谅上面的错误-显然,我用完了我的编辑-它不是9个字节,而是6个字节,因为我没有注意到您在打高尔夫球的形式中使用了=。无论如何,我可以通过在for循环中定义p来保存另外两个字符:s->for i=(p=split(s,"\n")) println(i[minimum([search(j,r"[^ ]")[1]for j=p]):end])end
Glen O

好的,这是另一个需要多刮胡子的地方-而不是使用minimum(x)when x是一个数组,而是使用来min(x...)节省一个额外的字节(我将把这个添加到我的Julia高尔夫球技巧列表中)。
Glen O

@GlenO很好,谢谢您的建议。另外,由于Julia使用PCRE,因此非空格字符可以与\S而不是匹配[^ ],从而节省了一个字节。
Alex A.

嘿,感谢您提到-我对regex不太满意,但事实证明这\S对我的解决方案也很有用。
Glen O

4

爪哇,159

因为明显缺乏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));}

它只是比较长度和修剪长度的循环,然后吐出子串。没什么好看的。对于受损的滚动条:

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

感谢@ThisSuitIsBlackNot对于使用Perl隐式循环的建议

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

上面的代码行代码得分为30字节,00p标志代码得分为3 。

原始版本,功能如下:

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

这会将参数放入$_,然后尝试用-贪婪地匹配所有行上存在的空格/^( +).*(\n\1.*)*$/-如果成功,则$1现在包含最长的公共前缀,我们执行替换操作s/^$1//mgr以从每行的开头将其删除并返回结果字符串。

测试

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

很酷。您可以通过在命令行上运行来节省一些字节:(perl -00pe '/^( +).*(\n\1.*)*$/&&s/^$1//mg'30个字节+ 3个代表00p)。
ThisSuitIsBlackNot

/me抬头抬头-00p; 感谢@ThisSuit
Toby Speight

3

Python 2,86 79 75字节

几乎可以肯定地将其缩短一些,但是现在还不错。

感谢xnor节省了4个字节!

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

1
计算前导空格的方法略短一些x.find(x.strip())
xnor

@xnor好的电话,谢谢!我整天都在等待您提供60字节的解决方案; P
Kade

input()在Python 2中会阻塞该数据。
史蒂芬·鲁姆巴尔斯基

@StevenRumbalski,我假设输入用引号引起来。我曾经在字节数上加2来解决这个问题,但是很多人都说我不需要。
卡德,2015年

1
该程序是可悲的:):
HyperNeutrino

3

Ruby:77 73 70 66 65 58 57 40个字符

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

样品运行:

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

太好了,@ Ventero。谢谢。
manatwork

2

C#,18 + 145 = 163字节

需要(18个字节):

using System.Linq;

方法(145字节):

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

该方法计算出行中的前导空格的最低数量,并创建一个由所有行构成的新字符串,并跳过N个字符(其中N是先前计算的数字)。


1

C#,共149个字节

尽管要修剪的字符数是手动计算的,但实际上与ProgramFOX的解决方案相同。

using System.Linq;

函数本身:

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我直到刷新页面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字节

这完全只使用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>

一如既往,仅适用于Firefox,自ES6起。稍后将添加ES5版本。


1
将正则表达式文字写为字符串然后进行评估似乎要短得多
Downgoat

@ vihan1086您可能是对的。让我试试看。
Optimizer

1

K,31个字节

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

输入一个字符串列表,并将结果打印到stdout。


1

Haskell,52个字节

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

用法示例:unlines.until(any(/=' ').map head)(map tail).lines $ " abc\n def\n ghi"->" abc\ndef\n ghi\n"

怎么运行的:

                                           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个字节):

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个字节)

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,7456,55

测试数据

s="\
   a   b
     c     d
    e f"

回答

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

输出量

a   b
  c     d
 e f

2
我进行了一些简单的高尔夫球更改,使这一数字下降到56:cut -c$[`grep -o '^ *'<<<"$s"|sort|sed q|wc -c`]-<<<"$s"
Digital Trauma

1
@DigitalTrauma:很好,我忘了$[]算术。使用cut的列选择要好得多。我从来没有看到sed q过替代head -n1高尔夫的好方法。谢谢!
2015年

2
关于head -n1vs sed qline在util-linux软件包中有一个工具。
manatwork

@manatwork:保存一个字符,我将使用它。请注意,它已被弃用,并且将来可能会消失,这来自util-linux的源代码树中的deprecated.txt:“为什么:没用,没人使用此命令,head(1)更好”。
2015年

1

R,118111字节

使用R的出色的字符串函数:)与已经发布的其他解决方案相似/相同。输入通过STDIN,输入到STDOUT。

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

嘿,恭喜3k代表!
Alex A.

@AlexA。干杯,我认为这对我来说并不重要...但是:)
MickyT 2015年

您的意思是您的生活不会围绕假互联网点展开吗?:P
Alex A.

@AlexA。希望不是:)
恭喜

1

朱莉娅72 62 61 57 54 49字节

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

取消高尔夫:

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

较旧的解决方案(57个字节):

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

原始解决方案(72字节):

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

1

k(24个字节)

将字符串作为参数并返回字符串(带有换行符)。

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

例:

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个字节

|©ζ®gð*Ûζ»

在线尝试!


等待,*重复串b 一个的倍量的?..没有知道的该功能*s∍当我想重复某个字符时,我通常会做(交换和延长)。
凯文·克鲁伊森

是的,的确确实如此,它适用于字符串,主要是因为在字符串情况下矢量化不太有意义,并且会и产生一个字符列表。
Xcoder先生18年

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

例如...

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

输出...

a   b
  c     d
 e f

只是经过很少测试的提示:不要捕获/^( +)//^ +/(那么您将拥有所需的值t[0]而不是t[1]);改变s==""!s; 之后删除{}周围的代码if; 删除;之前}; 使用Gawk特有的功能可以删除以下代码{}代码for{sub(s,"",z[r]);print z[r]}print gensub(s,"",1,z[r])
manatwork

抱歉地说,但是您的原始代码和使用我的尺寸优化的代码都在输入时出现了缩进,但最后一行除外。(例如“␠one\ nzero \n␠one\n␠␠two”。)
技巧

0

C GCC,74字节

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

仅删除所有空白,与前几行无关,请求帮助完成。另外,就常见的空格而言,OP是否表示哪一行的前导空格最少,即每行要删除的空格数?


是的,使用行距最少的行是正确的。
Sp3000

0

堆叠式,非竞争性,43个字节

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

在线尝试!

这是通过在每行('^ +'match$#'"!)的开头查找空格数量,获取最小值,重复一个空格多次并在每行中将其替换为空来实现的。




-1

CoffeeScript,112字节

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

换行是必需的,每个换行计为1个字节:

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

演示版

与其他ES6答案一样,它们目前仅在Firefox中有效。

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
如果下风者可以解释一下,那就太好了
rink.attendant.6

-1

JavaScript ES6,85个字节

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

新行意义重大

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

递归,尝试一次从每一行中删除一个空格,直到找到一个非空格为止。

测试下面的代码段-仅ES6,仅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>

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.