Jak wyrównać listę do określonego znaku?

13

Czy istnieje polecenie lub zestaw poleceń, których można użyć do wyrównania linii tekstu w poziomie do dowolnego znaku? Na przykład z listą adresów e-mail dane wyjściowe utworzyłyby plik tekstowy ze wszystkimi znakami „@” ustawionymi pionowo.

Aby odnieść sukces, uważam, że na początku większości wierszy należy dodać zmienną liczbę pustych spacji. Nie chcę osobnych kolumn, ponieważ czytają więcej wysiłku (na przykład column -t -s "@" < file.txt).

Przed:

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

Po:

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

Innymi słowy: czy mogę określić znak, który ma być punktem kontrolnym, wokół którego otaczający tekst jest wyśrodkowany w poziomie? Mój przypadek użycia to adresy e-mail, aby ułatwić ich skanowanie wizualne.

Tom Brossman
źródło
1
Co powinno się stać, jeśli istnieje wiele @symboli?
Zeta,
Dobre pytanie, wiele @symboli nie powinno stanowić problemu z adresami e-mail, ale użytkownik powinien być w stanie wybrać, które wystąpienie znaku w wierszu ma być „kotwicą”, wokół której drugi tekst jest wyśrodkowany.
Tom Brossman,
1
Wiele @adresów jest dozwolonych w adresach e-mail, np tom"@brossmann"@example.com. Dlatego zapytałem, co powinno się stać, jeśli jest wiele @symboli :).
Zeta,
@Zeta Wiele @symboli nie jest dozwolonych w różnych usługach e-mail. Całkowicie uzasadnione jest oczekiwanie, że „normalne” e-maile będą pasować do bardziej rygorystycznych standardów niż „prawdziwe”, chyba że masz do czynienia z nieprzetworzonymi, niefiltrowanymi danymi wejściowymi użytkowników, w którym to przypadku bardziej prawdopodobne jest, że poradzisz sobie z liniami bez @.
Pozew Fund Moniki z

Odpowiedzi:

3

NIE Awk. Tylko sedi column:

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

Wynik:

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

Teraz, o czym myślę, jest to prawie to samo, co rozwiązanie Sundeep, po prostu wygląda na krótsze / ma mniej połączeń z sed, a także zakłada, że @dzieje się to tylko raz na każdej linii.

wvxvw
źródło
1
Może być jeszcze krótszy:column -ts@ input.txt | sed -r 's/([^ ]+)( *)\s\s/\2\1@/'
MiniMax
11

W najprostszym przypadku możesz po prostu wydrukować pierwsze pole w odpowiednio dużej szerokości pola, np

awk -F@ 'BEGIN{OFS=FS} {$1 = sprintf("%12s", $1)} 1' file
         [email protected]
      [email protected]
       [email protected]

AFAIK każda metoda, która nie zakłada określonej maksymalnej szerokości pola, będzie wymagała albo przechowywania pliku w pamięci, albo wykonania dwóch przejść.

steeldriver
źródło
dobrze, aby uzyskać długość, można również użyć, cw=$(cut -d@ -f1 file | wc -L)a następnieawk -v w="$cw" 'BEGIN{OFS=FS="@"} {$1 = sprintf("%*s", w, $1)} 1'
Sundeep
Testując to na liście 328 adresów, brakuje jakichś dziesięciu danych wyjściowych (obecnie 318 linii). Dla jasności pobiegłem 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. Ładnie sformatował resztę, ale brakuje niektórych danych.
Tom Brossman,
1
@TomBrossman dzięki Właśnie zdałem sobie sprawę, że ma dość poważną wadę - nie będzie obsługiwał identycznych pól nazw - zamierzam go usunąć
steeldriver
Ten sam wynik, ale bardziej zwięźleawk -F@ '{printf "%12s@%s\n", $1, $2}' input.txt
MiniMax
6

hacky rozwiązanie, dużo zakłada tekst wejściowy

$ # 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
źródło
4

Szybkie rozwiązanie w języku Python, które wykorzystuje najkrótszą możliwą długość wypełnienia, która wyrównuje do prawej wszystkie ciągi po lewej stronie separatora:

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

Stosowanie:

python3 align-field.py < data.txt
David Foerster
źródło
2

Może to również działać z manipulacją ciągiem Bash.

Skrypt 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

Wynik:

   [email protected]
[email protected]
 [email protected]
zero2cx
źródło