Как вставить пробел через каждые четыре символа в длинной строке?


30

У меня есть длинная строка, в которую я хочу вставить пробел через каждые 4 символа, в одну строку сплошного текста, чтобы его было легче читать, какой самый простой способ сделать это? также я должен быть в состоянии ввести строку из трубы. например

echo "foobarbazblargblurg" | <some command here>

дает

foob arba zbla rgbl urg

Ответы:


54

Используйте sed следующим образом:

$ echo "foobarbazblargblurg" | sed 's/.\{4\}/& /g'
foob arba zbla rgbl urg

1
проклятие, которое было так близко к тому, что sedя попробовал первым, я мог ударить себя.
ксенотеррацид

7
Просто любопытно, что делает «&»? О, это слово «вещь, которая просто соответствует». Я такой глупый.
Всезнающий

1
следует отметить, что это добавляет пробел в конце, если в строке есть еще один символ, что может быть нежелательно
Anubis

@Anubis's/.\{4\}/& /g;s/ $//'
wieczorek1990

21

Вы можете использовать следующий простой пример:

$ echo "foobarbazblargblurg" | fold -w4 | paste -sd' ' -
foob arba zbla rgbl

Очень мило ... Я думаю, что это даже лучше, чем sedответ. Я не знал об этом foldраньше.
Wildcard

1
К сожалению, в текущих версиях GNU foldон не работает с многобайтовыми символами (как echo €€€€€€€€ | fold -w4 | paste -sd' ' -в UTF-8).
Стефан

3

Вот пример использования grepи xargs:

$ echo "foobarbazblargblurg" | grep -o .... | xargs
foob arba zbla rgbl

xargsзапускается echoпо умолчанию, поэтому он не будет работать с такими словами, как -nenили содержащие обратную косую черту, в зависимости от echoреализации. Вы также увидите странный символ новой строки время от времени, если xargs запускает более одного echo. Лучше paste -sd ' ' -вместо трубы . Обратите внимание, что -oэто не стандартная опция.
Стефан

3

Только в bash, без внешних команд:

str="foobarbazblargblurg"
[[ $str =~ ${str//?/(.)} ]]
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"

или в виде однолинейной трубы:

echo foobarbazblargblurg | 
  { IFS= read -r str; [[ $str =~ ${str//?/(.)} ]]; \
    printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"; }

Это работает путем преобразования каждого символа строки в "(.)" Для сопоставления с регулярным выражением и захвата с помощью =~, а затем просто выведите захваченные выражения из BASH_REMATCH[]массива, сгруппированного по мере необходимости Ведущие / конечные / промежуточные пробелы сохраняются, удаляйте кавычки вокруг"${BASH_REMATCH[@]:1}" чтобы пропустить их.

Здесь она заключена в функцию, она будет обрабатывать свои аргументы или читать stdin, если аргументов нет:

function fmt4() {
  while IFS= read -r str; do
    [[ $str =~ ${str//?/(.)} ]]
    printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"
  done < <( (( $# )) && printf '%s\n' "$@" || printf '%s\n' $(< /dev/stdin) )
}

$ echo foobarbazblargblurg | fmt4
foob arba zbla rgbl urg 

Вы можете легко параметризировать счетчик, чтобы соответствующим образом настроить строку формата.

Добавляется завершающий пробел, используйте два printfs вместо одного, если это проблема:

printf "%s%s%s%s" "${BASH_REMATCH[@]:1:4}"
(( ${#BASH_REMATCH[@]} > 5 )) && printf " %s%s%s%s" "${BASH_REMATCH[@]:5}"

Первый printf печатает (до) первых 4 символов, второй условно печатает все остальные (если они есть) с пробелом для разделения групп. Тест для 5 элементов, а не 4 для учета нулевого элемента.

Заметки:

  • Shell printf«s %cможет быть использован вместо %s, %c(возможно) делает цель более ясной, но это не многобайтный символ безопасности. Если ваша версия bash способна, все вышеизложенное безопасно для многобайтовых символов.
  • shell printfиспользует свою строку формата до тех пор, пока у нее не закончатся аргументы, поэтому она просто поглощает 4 аргумента за раз и обрабатывает завершающие аргументы (поэтому не требуется крайних случаев, в отличие от некоторых других ответов, которые здесь могут быть ошибочными)
  • BASH_REMATCH[0] является всей совпавшей строкой, поэтому выводится только начиная с индекса 1
  • printf -v myvar ...вместо этого используйте для сохранения в переменной myvar(в зависимости от обычного поведения цикла чтения / подоболочки)
  • добавить, printf "\n"если требуется

Вы можете заставить вышеописанное работать, zshесли вы используете массив match[]вместо BASH_REMATCH[], и вычитаете 1 из всех индексов, так как zshне сохраняете элемент 0 со всем соответствием.


3

С zshтолько:

str=foobarbazblargblurg

set -o extendedglob
printf '%s\n' ${str//(#m)????/$MATCH }

Или

printf '%s%s%s%s ' ${(s::)str}

с ksh93только:

printf '%s\n' "${str//????/\0 }"

Только с любой оболочкой POSIX (также избегая конечного пробела, если длина ввода кратна 4):

out=
while true; do
  case $str in
    (?????*)
      new_str=${str#????}
      out=$out${str%"$new_str"}' '
      str=$new_str
      ;;
    (*)
      out=$out$str
      break
  esac
done
printf '%s\n' "$out"

Теперь это для персонажей . Если вы хотите сделать это на кластерах графемы (например, разбить Stéphane, записать как $'Ste\u0301phane', как Stép haneи нет Ste phan e), с помощью zsh:

set -o rematchpcre
str=$'Ste\u301phane' out=
while [[ $str =~ '(\X{4})(.+)' ]] {
  out+="$match[1] " str=$match[2]
}
out+=$str
printf '%s\n' $out

С помощью ksh93 вы также можете разбить экран по ширине, что сработало бы для этого Stéphaneвыше, но также могло бы помочь, когда используются некоторые другие виды символов нулевой или двойной ширины:

str=$'Ste\u301phane' out=
while
  start=${ printf %L.4s. "$str"; }
  start=${start%.}
  [ "$start" != "$str" ]
do
  out+="$start " str=${str#"$start"}
done
out+=$str
printf '%s\n' "$out"

2

Я собираюсь ответить, вставляя только пробелы по мере необходимости, чтобы пробел появлялся как минимум после каждых 4 символов в строке; не уверен, каким образом вы хотите справиться с этим делом. Например, если ввести «aa bbccdd», вы получите вывод «aa bbcc dd», а не «aa b bccd d».

Я использую Perl для просмотра, но я не очень знаком с Perl в целом, поэтому могут потребоваться некоторые изменения:

$ echo "foobarbazblargblurg" | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g'
foob arba zbla rgbl urg

$ echo 'aa bbccdd' | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g'
aa bbcc dd
# not 'aa b bccd d'!

$ echo 'some input' | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g'
some inpu t
# not 'some  inp ut'!

$ echo $'aabb\nc cddee' | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g' | 
> while read; do echo "${REPLY}x"; done
aabbx
c cdde ex
# no spaces added at the end of the first line (while loop to add to the end of
# the line and show this)

0

Я сделал это с помощью Python

Сначала я читаю файл, затем делю на 4 символа и добавляю пробел

#!/usr/bin/python
import re
b=re.compile(r'[a-z]{4}')

p=open('/root/l.txt','r')
i=p.readlines()
for j in i:
    m=re.findall(b,j)
print " " .join (m) + "  "

/root/l.txt ==> Состоит из контента, который вы дали в примере

выход

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