Como alinhar a lista para caracteres específicos?


13

Existe um comando ou conjunto de comandos que eu possa usar para alinhar horizontalmente as linhas de texto com um caractere arbitrário? Por exemplo, com uma lista de endereços de email, a saída produziria um arquivo de texto com todos os caracteres '@' alinhados verticalmente.

Para ter sucesso, acredito que um número variável de espaços vazios deve ser adicionado ao início da maioria das linhas. Não quero colunas separadas, pois elas exigem mais esforço para ler (por exemplo column -t -s "@" < file.txt).

Antes:

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

Depois de:

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

Em outras palavras: posso especificar um caractere para ser um ponto de ancoragem, em torno do qual o texto ao redor é centralizado horizontalmente? Meu caso de uso para isso são endereços de email, para facilitar a verificação visual.


1
O que deve acontecer se houver vários @símbolos?
Zeta

Boa pergunta, vários @símbolos não devem ser um problema com endereços de email, mas o usuário deve poder selecionar qual instância de um caractere por linha será a "âncora" em torno da qual o outro texto está centralizado.
Tom Brossman

1
Vários @símbolos são permitidos em endereços de e-mail, por exemplo tom"@brossmann"@example.com. Por isso perguntei o que deveria acontecer se houver vários @símbolos :).
Zeta

@Zeta Vários @símbolos não são permitidos em uma variedade de serviços de e-mail. É perfeitamente razoável esperar e-mails "normais" que se encaixem em um padrão mais rígido que o "real", a menos que você esteja lidando com dados brutos e não filtrados do usuário; nesse caso, é mais provável que você lide com linhas sem @.
Fund Monica's Lawsuit

Respostas:


3

NÃO Awk. Somente sede column:

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

Resultado:

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

Agora, pensando bem, isso é quase o mesmo que a solução da Sundeep, apenas parece mais curta / tem menos chamadas sede também assume que isso @acontece apenas uma vez em cada linha.


1
Pode ser ainda mais curto:column -ts@ input.txt | sed -r 's/([^ ]+)( *)\s\s/\2\1@/'
MiniMax

11

Na sua forma mais simples, você pode simplesmente imprimir o primeiro campo em uma largura de campo adequadamente grande, por exemplo

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

AFAIK qualquer método que não assuma uma largura de campo máxima específica exigirá manter o arquivo na memória ou fazer duas passagens.


bom, para obter o comprimento também se pode usar cw=$(cut -d@ -f1 file | wc -L)e depoisawk -v w="$cw" 'BEGIN{OFS=FS="@"} {$1 = sprintf("%*s", w, $1)} 1'
Sundeep

Testando isso em uma lista de 328 endereços, dez estão de alguma forma ausentes na saída (agora 318 linhas). Para maior clareza, eu corri 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. Ele formatou bem o restante, mas faltam alguns dados.
Tom Brossman

1
@ TomBrossman obrigado Acabei de perceber que tem uma falha bastante séria - ele não manipula campos de nome idênticos - eu vou excluir esse
steeldriver

O mesmo resultado, mas de forma mais concisaawk -F@ '{printf "%12s@%s\n", $1, $2}' input.txt
MiniMax 10/12

6

solução hacky, pressupõe muito sobre entrada de texto

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

4

Uma solução rápida em Python que usa o menor comprimento possível de preenchimento que alinha à direita todas as strings restantes do separador:

#!/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)

Uso:

python3 align-field.py < data.txt

2

Outra solução GNU awk+ column:

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

A saída:

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

Você poderia adicionar um pouco sobre como este funciona?
Joe

2

Isso também pode funcionar com a manipulação de strings do Bash.

Script 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

O resultado:

   123@example.com
456789@example.net
 01234@something-else.com
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.