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:

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

Po:

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

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.


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:

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

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.


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
         123@example.com
      456789@example.net
       01234@something-else.com

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ść.


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/'
     123@example.com
  456789@example.net
   01234@something-else.com

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

2

Inne rozwiązanie GNU awk+ column:

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

Wyjście:

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

Czy mógłbyś dodać coś o tym, jak ten działa?
Joe

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:

   123@example.com
456789@example.net
 01234@something-else.com
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.