Как выровнять список по конкретному персонажу?

13

Есть ли команда или набор команд, которые я могу использовать для горизонтального выравнивания строк текста по произвольному символу? Например, со списком адресов электронной почты в результате будет получен текстовый файл со всеми символами «@», выстроенными вертикально.

Чтобы быть успешным, я считаю, что переменное количество пустых мест должно быть добавлено в начало большинства строк. Я не хочу отдельных столбцов, поскольку они требуют больше усилий для чтения (например,column -t -s "@" < file.txt ).

Перед:

123@example.com
456789@example.net
01234@something-else.com

После:

   123@example.com
456789@example.net
 01234@something-else.com

Другими словами: могу ли я указать символ как опорную точку, вокруг которой окружающий текст горизонтально центрирован? Мой вариант использования для этого - адреса электронной почты, чтобы их было проще сканировать визуально.

Том Броссман
источник
1
Что должно произойти, если есть несколько @символов?
Зета
Хороший вопрос, множественные @символы не должны быть проблемой с адресами электронной почты, но пользователь должен иметь возможность выбрать, какой экземпляр символа в строке будет «якорем», вокруг которого центрируется другой текст.
Том Броссман
1
В @адресах электронной почты допускается несколько символов, например tom"@brossmann"@example.com. Вот почему я спросил, что должно произойти, если есть несколько @символов :).
Зета
@ Зета Несколько @символов не допускаются в различных почтовых службах. Вполне разумно ожидать, что «нормальные» электронные письма соответствуют более строгим стандартам, чем «реальные», если вы не имеете дело с необработанным, нефильтрованным пользовательским вводом, и в этом случае вы, скорее всего, будете иметь дело со строками без @.
Фонд Моника иск

Ответы:

3

НЕТ Awk. Только sedи column:

column -ts@ file.txt | sed -E 's/([^ ]+)([ ]+) (.+)/\2\1@\3/'

Выход:

   123@example.com
456789@example.net
 01234@something-else.com

Теперь, о чем я думаю, это почти то же самое, что и решение Sundeep, оно выглядит короче / имеет меньше вызовов sed, и также предполагает, что это @происходит только один раз в каждой строке.

wvxvw
источник
1
Это может быть еще короче:column -ts@ input.txt | sed -r 's/([^ ]+)( *)\s\s/\2\1@/'
MiniMax
11

В простейшем случае вы можете просто напечатать первое поле в достаточно большой ширине поля, например

awk -F@ 'BEGIN{OFS=FS} {$1 = sprintf("%12s", $1)} 1' file
         123@example.com
      456789@example.net
       01234@something-else.com

AFAIK любой метод, который не предполагает конкретной максимальной ширины поля, потребует либо удержания файла в памяти, либо выполнения двух проходов.

steeldriver
источник
хорошо, чтобы получить длину можно также использовать, cw=$(cut -d@ -f1 file | wc -L)а затемawk -v w="$cw" 'BEGIN{OFS=FS="@"} {$1 = sprintf("%*s", w, $1)} 1'
Sundeep
Тестируя это по списку из 328 адресов, десять выходных так или иначе отсутствуют в выводе (теперь 318 строк). Для наглядности я побежал awk -F@ '{a[$1] = $2; w = length($1) > w? length($1) : w; next} END {for (i in a) printf("%*s%c%s\n", w, i, FS, a[i])}' INPUT-FILE.txt > OUT.txt. Это действительно форматировало остаток хорошо, но некоторые данные отсутствуют.
Том Броссман
1
@ TomBrossman спасибо, я только что понял, что у него довольно серьезный недостаток - он не будет обрабатывать идентичные поля имени - я собираюсь удалить это
steeldriver
Тот же результат, но более лаконичноawk -F@ '{printf "%12s@%s\n", $1, $2}' input.txt
MiniMax
6

хакерское решение, много предполагает ввод текста

$ # four commas to reduce chance of it affecting actual email address
$ sed 's/@/,,,,@/' ip.txt | column -t -s,,,,
123     @example.com
456789  @example.net
01234   @something-else.com

$ sed 's/@/,,,,@/' ip.txt | column -t -s,,,, | sed -E 's/^([^ ]+)( +)/\2\1/'
     123@example.com
  456789@example.net
   01234@something-else.com
Sundeep
источник
4

Быстрое решение Python, которое использует минимально возможную длину заполнения, выравнивая по правому краю все строки слева от разделителя:

#!/usr/bin/env python3
import sys
fieldsep = '@'
records = [line.rstrip('\n').split(fieldsep, 1) for line in sys.stdin]
col1_len = max((len(r[0]) for r in records), default=0)
for r in records:
    print(r[0].rjust(col1_len), r[1], sep=fieldsep)

Использование:

python3 align-field.py < data.txt
Дэвид Фёрстер
источник
2

Другое решение GNU awk+ column:

awk '{ split($0,a,/ +/,sep); printf "%*s@%s\n",length($1 sep[1])-2,$1,$2 }' <(column -ts'@' file)

Выход:

   123@example.com
456789@example.net
 01234@something-else.com
RomanPerekhrest
источник
Не могли бы вы немного рассказать о том, как это работает?
Джо
2

Это может работать и с манипуляциями с Bash.

Скрипт Bash (4.x):

#!/bin/bash

read -d '' -r -a data <"data.txt"

for ((pos=0, i=0; i<${#data[@]}; i++)); do
    locl=${data[$i]%@*}                         # The local-part.
    [[ ${#locl} -gt $pos ]] && pos=${#locl}     # Determine the lengthiest $locl.
done

for ((i=0; i<${#data[@]}; i++)); do
    email=${data[$i]}
    locl=${email%@*}                            # The local-part.
    domain=${email#*@}                          # The email domain.
    printf '%*s@%s\n' $pos $locl $domain        # Align $locl to the right, at $pos.
done

Результат:

   123@example.com
456789@example.net
 01234@something-else.com
zero2cx
источник