Удалить общие ведущие пробелы


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

Ответы:


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 является логическим значением, которое можно рассматривать как целое число 0 или 1. >R Gзахватывает это число и отсекает столько символов слева от каждой строки в G. Комбинированные шаги 1, 2 и 3 в основном означают, что он будет удалять столбцы пробелов, пока не останется чистый столбец пробелов.

  4. jb соединяет массив строк с помощью новых строк и печатает его.


2
Можете ли вы дать небольшое объяснение этому? Это очень странно для меня!
Боббел

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флаг.
Тоби Спейт

Вы можете :;/^\S/M!s/^ //mg;t-r
играть в

7

SWI-Пролог, 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 байт

Сохранено 10 байтов благодаря Glen O.

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

Это создает безымянную функцию, которая принимает строку и печатает на стандартный вывод.

Ungolfed + объяснение:

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 байт (включая 3, сохраненных с использованием =вместо `in ). Still looking to see if more can be saved. (I wish I could drop the [1]`), но при поиске создается массив перечислителей типа Any, хотя минимум требует тип Int)
Глен О

Извините за ошибку выше - очевидно, я использовал свои правки - это не 9 байт, а 6, потому что я не заметил, что вы использовали = в форме для игры в гольф. В любом случае, я могу сохранить еще два символа, определив p в начале цикла for:s->for i=(p=split(s,"\n")) println(i[minimum([search(j,r"[^ ]")[1]for j=p]):end])end
Glen O

Хорошо, вот еще один, чтобы побриться немного больше - вместо того, чтобы использовать, minimum(x)когда xмассив, используйте min(x...)для сохранения одного дополнительного байта (я собираюсь добавить этот в свой список советов Джулии по игре в гольф).
Глен О

@GlenO Хорошо, спасибо за предложения. Кроме того, поскольку Джулия использует PCRE, непропускные символы могут быть сопоставлены с \S, чем [^ ], что сохраняет байт.
Алекс А.

Эй, спасибо, что упомянул это - я не очень хорошо разбираюсь в регулярных выражениях, но оказалось, что \Sэто тоже полезно для моего решения.
Глен 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 байтов для строки кода + 3 для 00pфлагов.

Оригинальная версия, как функция:

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
Тоби Спейт

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

@xно хороший звонок, спасибо! Я ждал от вас 60-байтового решения весь день; P
Kade

input()в Python 2 захлебнулся бы этими данными.
Стивен Румбальски

@ StevenRumbalski, я предполагаю, что входные данные заключены в кавычки. Я имел обыкновение добавлять 2 к числу байтов, чтобы учесть это, но несколько человек сказали, что мне это не нужно.
Каде

1
Эта программа печальна:):
HyperNeutrino

3

Рубин: 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. Спасибо.
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 Я не видел твоего решения, пока не обновил страницу, кстати: о)
Сок


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
Кажется, что было бы короче написать литерал Регулярного выражения в виде строки и затем
оценить

@ vihan1086 ты можешь быть прав. Позвольте мне попробовать.
Оптимизатор

1

К, 31 байт

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

Вводит список строк и выводит результат на стандартный вывод.


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

лямбда (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, 74 , 56 , 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"
Цифровая травма

1
@DigitalTrauma: Хорошо, я забыл об $[]арифметике. Использование cutдля выбора столбца намного лучше. Я никогда не видел sed qв качестве альтернативы head -n1, это хороший трюк в гольф. Благодарность!
Тор

2
Что касается head -n1vs sed q, lineв пакете util-linux есть инструмент.
manatwork

@manatwork: Это спасет одного персонажа, я буду его использовать. Обратите внимание, что он устарел и может исчезнуть в будущем, это из файла deprecated.txt в дереве исходного кода util-linux: «Почему: бесполезно, никто не использует эту команду, head (1) лучше»).
Тор

1

R, 118 111 байт

Использование замечательных строковых функций 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 респ!
Алекс А.

@AlexA. Приветствия, не думали, что это было важно для меня ... но :)
MickyT

Вы имеете в виду, что ваша жизнь не вращается вокруг поддельных интернет-точек? : P
Алекс А.

@AlexA. Надеюсь, что нет :) поздравляю с 6k
MickyT

1

Юлия, 72 62 61 57 54 49 байтов

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

Старый раствор (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

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

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".)
manatwork

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
Было бы замечательно, если бы downvoter мог объяснить ...
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>

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.