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:

[email protected]
[email protected]
[email protected]

Depois de:

   [email protected]
[email protected]
 [email protected]

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.

Tom Brossman
fonte
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:

   [email protected]
[email protected]
 [email protected]

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.

wvxvw
fonte
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
         [email protected]
      [email protected]
       [email protected]

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.

chave de aço
fonte
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/'
     [email protected]
  [email protected]
   [email protected]
Sundeep
fonte
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
David Foerster
fonte
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:

   [email protected]
[email protected]
 [email protected]
zero2cx
fonte